1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony.dataconnection;
18 
19 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
20 
21 import android.annotation.NonNull;
22 import android.app.AppOpsManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.ServiceConnection;
29 import android.content.pm.IPackageManager;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ResolveInfo;
32 import android.net.LinkProperties;
33 import android.os.AsyncResult;
34 import android.os.Handler;
35 import android.os.IBinder;
36 import android.os.Message;
37 import android.os.PersistableBundle;
38 import android.os.RegistrantList;
39 import android.os.RemoteException;
40 import android.os.ServiceManager;
41 import android.os.UserHandle;
42 import android.telephony.AccessNetworkConstants;
43 import android.telephony.AccessNetworkConstants.TransportType;
44 import android.telephony.AnomalyReporter;
45 import android.telephony.CarrierConfigManager;
46 import android.telephony.SubscriptionManager;
47 import android.telephony.data.DataCallResponse;
48 import android.telephony.data.DataProfile;
49 import android.telephony.data.DataService;
50 import android.telephony.data.DataServiceCallback;
51 import android.telephony.data.IDataService;
52 import android.telephony.data.IDataServiceCallback;
53 import android.text.TextUtils;
54 
55 import com.android.internal.telephony.Phone;
56 import com.android.internal.telephony.PhoneConfigurationManager;
57 import com.android.internal.telephony.util.TelephonyUtils;
58 import com.android.telephony.Rlog;
59 
60 import java.util.HashSet;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Set;
64 import java.util.UUID;
65 import java.util.concurrent.ConcurrentHashMap;
66 
67 /**
68  * Data service manager manages handling data requests and responses on data services (e.g.
69  * Cellular data service, IWLAN data service).
70  */
71 public class DataServiceManager extends Handler {
72     private static final boolean DBG = true;
73 
74     static final String DATA_CALL_RESPONSE = "data_call_response";
75 
76     private static final int EVENT_BIND_DATA_SERVICE = 1;
77 
78     private static final int EVENT_WATCHDOG_TIMEOUT = 2;
79 
80     private static final long REQUEST_UNRESPONDED_TIMEOUT = 10 * MINUTE_IN_MILLIS; // 10 mins
81 
82     private final Phone mPhone;
83 
84     private final String mTag;
85 
86     private final CarrierConfigManager mCarrierConfigManager;
87     private final AppOpsManager mAppOps;
88     private final IPackageManager mPackageManager;
89 
90     private final int mTransportType;
91 
92     private boolean mBound;
93 
94     private IDataService mIDataService;
95 
96     private DataServiceManagerDeathRecipient mDeathRecipient;
97 
98     private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList();
99 
100     private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>();
101 
102     private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
103 
104     private String mTargetBindingPackageName;
105 
106     private CellularDataServiceConnection mServiceConnection;
107 
108     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
109         @Override
110         public void onReceive(Context context, Intent intent) {
111             final String action = intent.getAction();
112             if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
113                     && mPhone.getPhoneId() == intent.getIntExtra(
114                     CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
115                 // We should wait for carrier config changed event because the target binding
116                 // package name can come from the carrier config. Note that we still get this event
117                 // even when SIM is absent.
118                 if (DBG) log("Carrier config changed. Try to bind data service.");
119                 sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
120             }
121         }
122     };
123 
124     private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
125         @Override
binderDied()126         public void binderDied() {
127             // TODO: try to rebind the service.
128             loge("DataService " + mTargetBindingPackageName +  ", transport type " + mTransportType
129                     + " died.");
130         }
131     }
132 
grantPermissionsToService(String packageName)133     private void grantPermissionsToService(String packageName) {
134         final String[] pkgToGrant = {packageName};
135         try {
136             mPackageManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
137                     pkgToGrant, UserHandle.myUserId());
138             mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS,
139                 UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
140         } catch (RemoteException e) {
141             loge("Binder to package manager died, permission grant for DataService failed.");
142             throw TelephonyUtils.rethrowAsRuntimeException(e);
143         }
144     }
145 
146     /**
147      * Loop through all DataServices installed on the system and revoke permissions from any that
148      * are not currently the WWAN or WLAN data service.
149      */
revokePermissionsFromUnusedDataServices()150     private void revokePermissionsFromUnusedDataServices() {
151         // Except the current data services from having their permissions removed.
152         Set<String> dataServices = getAllDataServicePackageNames();
153         for (int transportType : mPhone.getTransportManager().getAvailableTransports()) {
154             dataServices.remove(getDataServicePackageName(transportType));
155         }
156 
157         try {
158             String[] dataServicesArray = new String[dataServices.size()];
159             dataServices.toArray(dataServicesArray);
160             mPackageManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
161                     dataServicesArray, UserHandle.myUserId());
162             for (String pkg : dataServices) {
163                 mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS, UserHandle.myUserId(),
164                         pkg, AppOpsManager.MODE_ERRORED);
165             }
166         } catch (RemoteException e) {
167             loge("Binder to package manager died; failed to revoke DataService permissions.");
168             throw TelephonyUtils.rethrowAsRuntimeException(e);
169         }
170     }
171 
172     private final class CellularDataServiceConnection implements ServiceConnection {
173         @Override
onServiceConnected(ComponentName name, IBinder service)174         public void onServiceConnected(ComponentName name, IBinder service) {
175             if (DBG) log("onServiceConnected");
176             mIDataService = IDataService.Stub.asInterface(service);
177             mDeathRecipient = new DataServiceManagerDeathRecipient();
178             mBound = true;
179 
180             try {
181                 service.linkToDeath(mDeathRecipient, 0);
182                 mIDataService.createDataServiceProvider(mPhone.getPhoneId());
183                 mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
184                         new CellularDataServiceCallback("dataCallListChanged"));
185             } catch (RemoteException e) {
186                 mDeathRecipient.binderDied();
187                 loge("Remote exception. " + e);
188                 return;
189             }
190             removeMessages(EVENT_WATCHDOG_TIMEOUT);
191             mServiceBindingChangedRegistrants.notifyResult(true);
192         }
193         @Override
onServiceDisconnected(ComponentName name)194         public void onServiceDisconnected(ComponentName name) {
195             if (DBG) log("onServiceDisconnected");
196             removeMessages(EVENT_WATCHDOG_TIMEOUT);
197             mIDataService = null;
198             mBound = false;
199             mServiceBindingChangedRegistrants.notifyResult(false);
200             mTargetBindingPackageName = null;
201         }
202     }
203 
204     private final class CellularDataServiceCallback extends IDataServiceCallback.Stub {
205 
206         private final String mTag;
207 
CellularDataServiceCallback(String tag)208         CellularDataServiceCallback(String tag) {
209             mTag = tag;
210         }
211 
getTag()212         public String getTag() {
213             return mTag;
214         }
215 
216         @Override
onSetupDataCallComplete(@ataServiceCallback.ResultCode int resultCode, DataCallResponse response)217         public void onSetupDataCallComplete(@DataServiceCallback.ResultCode int resultCode,
218                                             DataCallResponse response) {
219             if (DBG) {
220                 log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = "
221                         + response);
222             }
223             removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
224             Message msg = mMessageMap.remove(asBinder());
225             if (msg != null) {
226                 msg.getData().putParcelable(DATA_CALL_RESPONSE, response);
227                 sendCompleteMessage(msg, resultCode);
228             } else {
229                 loge("Unable to find the message for setup call response.");
230             }
231         }
232 
233         @Override
onDeactivateDataCallComplete(@ataServiceCallback.ResultCode int resultCode)234         public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) {
235             if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode);
236             removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
237             Message msg = mMessageMap.remove(asBinder());
238             sendCompleteMessage(msg, resultCode);
239         }
240 
241         @Override
onSetInitialAttachApnComplete(@ataServiceCallback.ResultCode int resultCode)242         public void onSetInitialAttachApnComplete(@DataServiceCallback.ResultCode int resultCode) {
243             if (DBG) log("onSetInitialAttachApnComplete. resultCode = " + resultCode);
244             Message msg = mMessageMap.remove(asBinder());
245             sendCompleteMessage(msg, resultCode);
246         }
247 
248         @Override
onSetDataProfileComplete(@ataServiceCallback.ResultCode int resultCode)249         public void onSetDataProfileComplete(@DataServiceCallback.ResultCode int resultCode) {
250             if (DBG) log("onSetDataProfileComplete. resultCode = " + resultCode);
251             Message msg = mMessageMap.remove(asBinder());
252             sendCompleteMessage(msg, resultCode);
253         }
254 
255         @Override
onRequestDataCallListComplete(@ataServiceCallback.ResultCode int resultCode, List<DataCallResponse> dataCallList)256         public void onRequestDataCallListComplete(@DataServiceCallback.ResultCode int resultCode,
257                                               List<DataCallResponse> dataCallList) {
258             if (DBG) log("onRequestDataCallListComplete. resultCode = " + resultCode);
259             Message msg = mMessageMap.remove(asBinder());
260             sendCompleteMessage(msg, resultCode);
261         }
262 
263         @Override
onDataCallListChanged(List<DataCallResponse> dataCallList)264         public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
265             mDataCallListChangedRegistrants.notifyRegistrants(
266                     new AsyncResult(null, dataCallList, null));
267         }
268     }
269 
270     /**
271      * Constructor
272      *
273      * @param phone The phone object
274      * @param transportType The transport type
275      * @param tagSuffix Logging tag suffix
276      */
DataServiceManager(Phone phone, @TransportType int transportType, String tagSuffix)277     public DataServiceManager(Phone phone, @TransportType int transportType, String tagSuffix) {
278         mPhone = phone;
279         mTag = "DSM" + tagSuffix;
280         mTransportType = transportType;
281         mBound = false;
282         mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
283                 Context.CARRIER_CONFIG_SERVICE);
284         mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
285         mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
286 
287         IntentFilter intentFilter = new IntentFilter();
288         intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
289         try {
290             Context contextAsUser = phone.getContext().createPackageContextAsUser(
291                     phone.getContext().getPackageName(), 0, UserHandle.ALL);
292             contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter,
293                     null /* broadcastPermission */, null);
294         } catch (PackageManager.NameNotFoundException e) {
295             loge("Package name not found: " + e.getMessage());
296         }
297         PhoneConfigurationManager.registerForMultiSimConfigChange(
298                 this, EVENT_BIND_DATA_SERVICE, null);
299 
300         sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
301     }
302 
303     /**
304      * Handle message events
305      *
306      * @param msg The message to handle
307      */
308     @Override
handleMessage(Message msg)309     public void handleMessage(Message msg) {
310         switch (msg.what) {
311             case EVENT_BIND_DATA_SERVICE:
312                 rebindDataService();
313                 break;
314             case EVENT_WATCHDOG_TIMEOUT:
315                 handleRequestUnresponded((CellularDataServiceCallback) msg.obj);
316                 break;
317             default:
318                 loge("Unhandled event " + msg.what);
319         }
320     }
321 
handleRequestUnresponded(CellularDataServiceCallback callback)322     private void handleRequestUnresponded(CellularDataServiceCallback callback) {
323         String message = "Request " + callback.getTag() + " unresponded on transport "
324                 + AccessNetworkConstants.transportTypeToString(mTransportType) + " in "
325                 + REQUEST_UNRESPONDED_TIMEOUT / 1000 + " seconds.";
326         log(message);
327         // Using fixed UUID to avoid duplicate bugreport notification
328         AnomalyReporter.reportAnomaly(
329                 UUID.fromString("f5d5cbe6-9bd6-4009-b764-42b1b649b1de"),
330                 message);
331     }
332 
unbindDataService()333     private void unbindDataService() {
334         // Start by cleaning up all packages that *shouldn't* have permissions.
335         revokePermissionsFromUnusedDataServices();
336         if (mIDataService != null && mIDataService.asBinder().isBinderAlive()) {
337             log("unbinding service");
338             // Remove the network availability updater and then unbind the service.
339             try {
340                 mIDataService.removeDataServiceProvider(mPhone.getPhoneId());
341             } catch (RemoteException e) {
342                 loge("Cannot remove data service provider. " + e);
343             }
344         }
345 
346         if (mServiceConnection != null) {
347             mPhone.getContext().unbindService(mServiceConnection);
348         }
349         mIDataService = null;
350         mServiceConnection = null;
351         mTargetBindingPackageName = null;
352         mBound = false;
353     }
354 
bindDataService(String packageName)355     private void bindDataService(String packageName) {
356         if (mPhone == null || !SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())) {
357             loge("can't bindDataService with invalid phone or phoneId.");
358             return;
359         }
360 
361         if (TextUtils.isEmpty(packageName)) {
362             loge("Can't find the binding package");
363             return;
364         }
365 
366         Intent intent = null;
367         String className = getDataServiceClassName();
368         if (TextUtils.isEmpty(className)) {
369             intent = new Intent(DataService.SERVICE_INTERFACE);
370             intent.setPackage(packageName);
371         } else {
372             ComponentName cm = new ComponentName(packageName, className);
373             intent = new Intent(DataService.SERVICE_INTERFACE).setComponent(cm);
374         }
375 
376         // Then pre-emptively grant the permissions to the package we will bind.
377         grantPermissionsToService(packageName);
378 
379         try {
380             mServiceConnection = new CellularDataServiceConnection();
381             if (!mPhone.getContext().bindService(
382                     intent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
383                 loge("Cannot bind to the data service.");
384                 return;
385             }
386             mTargetBindingPackageName = packageName;
387         } catch (Exception e) {
388             loge("Cannot bind to the data service. Exception: " + e);
389         }
390     }
391 
rebindDataService()392     private void rebindDataService() {
393         String packageName = getDataServicePackageName();
394         // Do nothing if no need to rebind.
395         if (SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())
396                 && TextUtils.equals(packageName, mTargetBindingPackageName)) {
397             if (DBG) log("Service " + packageName + " already bound or being bound.");
398             return;
399         }
400 
401         unbindDataService();
402         bindDataService(packageName);
403     }
404 
405     @NonNull
getAllDataServicePackageNames()406     private Set<String> getAllDataServicePackageNames() {
407         // Cowardly using the public PackageManager interface here.
408         // Note: This matches only packages that were installed on the system image. If we ever
409         // expand the permissions model to allow CarrierPrivileged packages, then this will need
410         // to be updated.
411         List<ResolveInfo> dataPackages =
412                 mPhone.getContext().getPackageManager().queryIntentServices(
413                         new Intent(DataService.SERVICE_INTERFACE),
414                                 PackageManager.MATCH_SYSTEM_ONLY);
415         HashSet<String> packageNames = new HashSet<>();
416         for (ResolveInfo info : dataPackages) {
417             if (info.serviceInfo == null) continue;
418             packageNames.add(info.serviceInfo.packageName);
419         }
420         return packageNames;
421     }
422 
423     /**
424      * Get the data service package name for our current transport type.
425      *
426      * @return package name of the data service package for the the current transportType.
427      */
getDataServicePackageName()428     private String getDataServicePackageName() {
429         return getDataServicePackageName(mTransportType);
430     }
431 
432     /**
433      * Get the data service package by transport type.
434      *
435      * When we bind to a DataService package, we need to revoke permissions from stale
436      * packages; we need to exclude data packages for all transport types, so we need to
437      * to be able to query by transport type.
438      *
439      * @param transportType The transport type
440      * @return package name of the data service package for the specified transportType.
441      */
getDataServicePackageName(@ransportType int transportType)442     private String getDataServicePackageName(@TransportType int transportType) {
443         String packageName;
444         int resourceId;
445         String carrierConfig;
446 
447         switch (transportType) {
448             case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
449                 resourceId = com.android.internal.R.string.config_wwan_data_service_package;
450                 carrierConfig = CarrierConfigManager
451                         .KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
452                 break;
453             case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
454                 resourceId = com.android.internal.R.string.config_wlan_data_service_package;
455                 carrierConfig = CarrierConfigManager
456                         .KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
457                 break;
458             default:
459                 throw new IllegalStateException("Transport type not WWAN or WLAN. type="
460                         + AccessNetworkConstants.transportTypeToString(mTransportType));
461         }
462 
463         // Read package name from resource overlay
464         packageName = mPhone.getContext().getResources().getString(resourceId);
465 
466         PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
467 
468         if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
469             // If carrier config overrides it, use the one from carrier config
470             packageName = b.getString(carrierConfig, packageName);
471         }
472 
473         return packageName;
474     }
475 
476     /**
477      * Get the data service class name for our current transport type.
478      *
479      * @return class name of the data service package for the the current transportType.
480      */
getDataServiceClassName()481     private String getDataServiceClassName() {
482         return getDataServiceClassName(mTransportType);
483     }
484 
485 
486     /**
487      * Get the data service class by transport type.
488      *
489      * @param transportType either WWAN or WLAN
490      * @return class name of the data service package for the specified transportType.
491      */
getDataServiceClassName(int transportType)492     private String getDataServiceClassName(int transportType) {
493         String className;
494         int resourceId;
495         String carrierConfig;
496         switch (transportType) {
497             case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
498                 resourceId = com.android.internal.R.string.config_wwan_data_service_class;
499                 carrierConfig = CarrierConfigManager
500                         .KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING;
501                 break;
502             case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
503                 resourceId = com.android.internal.R.string.config_wlan_data_service_class;
504                 carrierConfig = CarrierConfigManager
505                         .KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING;
506                 break;
507             default:
508                 throw new IllegalStateException("Transport type not WWAN or WLAN. type="
509                         + transportType);
510         }
511 
512         // Read package name from resource overlay
513         className = mPhone.getContext().getResources().getString(resourceId);
514 
515         PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
516 
517         if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
518             // If carrier config overrides it, use the one from carrier config
519             className = b.getString(carrierConfig, className);
520         }
521 
522         return className;
523     }
524 
sendCompleteMessage(Message msg, int code)525     private void sendCompleteMessage(Message msg, int code) {
526         if (msg != null) {
527             msg.arg1 = code;
528             msg.sendToTarget();
529         }
530     }
531 
532     /**
533      * Setup a data connection. The data service provider must implement this method to support
534      * establishing a packet data connection. When completed or error, the service must invoke
535      * the provided callback to notify the platform.
536      *
537      * @param accessNetworkType Access network type that the data call will be established on.
538      *        Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
539      * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
540      * @param isRoaming True if the device is data roaming.
541      * @param allowRoaming True if data roaming is allowed by the user.
542      * @param reason The reason for data setup. Must be {@link DataService#REQUEST_REASON_NORMAL} or
543      *        {@link DataService#REQUEST_REASON_HANDOVER}.
544      * @param linkProperties If {@code reason} is {@link DataService#REQUEST_REASON_HANDOVER}, this
545      *        is the link properties of the existing data connection, otherwise null.
546      * @param onCompleteMessage The result message for this request. Null if the client does not
547      *        care about the result.
548      */
setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, Message onCompleteMessage)549     public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
550                               boolean allowRoaming, int reason, LinkProperties linkProperties,
551                               Message onCompleteMessage) {
552         if (DBG) log("setupDataCall");
553         if (!mBound) {
554             loge("Data service not bound.");
555             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
556             return;
557         }
558 
559         CellularDataServiceCallback callback = new CellularDataServiceCallback("setupDataCall");
560         if (onCompleteMessage != null) {
561             mMessageMap.put(callback.asBinder(), onCompleteMessage);
562         }
563         try {
564             sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
565                     REQUEST_UNRESPONDED_TIMEOUT);
566             mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
567                     isRoaming, allowRoaming, reason, linkProperties, callback);
568         } catch (RemoteException e) {
569             loge("Cannot invoke setupDataCall on data service.");
570             mMessageMap.remove(callback.asBinder());
571             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
572         }
573     }
574 
575     /**
576      * Deactivate a data connection. The data service provider must implement this method to
577      * support data connection tear down. When completed or error, the service must invoke the
578      * provided callback to notify the platform.
579      *
580      * @param cid Call id returned in the callback of {@link #setupDataCall(int, DataProfile,
581      *        boolean, boolean, int, LinkProperties, Message)}
582      * @param reason The reason for data deactivation. Must be
583      *        {@link DataService#REQUEST_REASON_NORMAL}, {@link DataService#REQUEST_REASON_SHUTDOWN}
584      *        or {@link DataService#REQUEST_REASON_HANDOVER}.
585      * @param onCompleteMessage The result message for this request. Null if the client does not
586      *        care about the result.
587      */
deactivateDataCall(int cid, int reason, Message onCompleteMessage)588     public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) {
589         if (DBG) log("deactivateDataCall");
590         if (!mBound) {
591             loge("Data service not bound.");
592             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
593             return;
594         }
595 
596         CellularDataServiceCallback callback =
597                 new CellularDataServiceCallback("deactivateDataCall");
598         if (onCompleteMessage != null) {
599             mMessageMap.put(callback.asBinder(), onCompleteMessage);
600         }
601         try {
602             sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
603                     REQUEST_UNRESPONDED_TIMEOUT);
604             mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
605         } catch (RemoteException e) {
606             loge("Cannot invoke deactivateDataCall on data service.");
607             mMessageMap.remove(callback.asBinder());
608             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
609         }
610     }
611 
612     /**
613      * Set an APN to initial attach network.
614      *
615      * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
616      * @param isRoaming True if the device is data roaming.
617      * @param onCompleteMessage The result message for this request. Null if the client does not
618      *        care about the result.
619      */
setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message onCompleteMessage)620     public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
621                                     Message onCompleteMessage) {
622         if (DBG) log("setInitialAttachApn");
623         if (!mBound) {
624             loge("Data service not bound.");
625             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
626             return;
627         }
628 
629         CellularDataServiceCallback callback =
630                 new CellularDataServiceCallback("setInitialAttachApn");
631         if (onCompleteMessage != null) {
632             mMessageMap.put(callback.asBinder(), onCompleteMessage);
633         }
634         try {
635             mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming,
636                     callback);
637         } catch (RemoteException e) {
638             loge("Cannot invoke setInitialAttachApn on data service.");
639             mMessageMap.remove(callback.asBinder());
640             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
641         }
642     }
643 
644     /**
645      * Send current carrier's data profiles to the data service for data call setup. This is
646      * only for CDMA carrier that can change the profile through OTA. The data service should
647      * always uses the latest data profile sent by the framework.
648      *
649      * @param dps A list of data profiles.
650      * @param isRoaming True if the device is data roaming.
651      * @param onCompleteMessage The result message for this request. Null if the client does not
652      *        care about the result.
653      */
setDataProfile(List<DataProfile> dps, boolean isRoaming, Message onCompleteMessage)654     public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
655                                Message onCompleteMessage) {
656         if (DBG) log("setDataProfile");
657         if (!mBound) {
658             loge("Data service not bound.");
659             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
660             return;
661         }
662 
663         CellularDataServiceCallback callback = new CellularDataServiceCallback("setDataProfile");
664         if (onCompleteMessage != null) {
665             mMessageMap.put(callback.asBinder(), onCompleteMessage);
666         }
667         try {
668             mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback);
669         } catch (RemoteException e) {
670             loge("Cannot invoke setDataProfile on data service.");
671             mMessageMap.remove(callback.asBinder());
672             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
673         }
674     }
675 
676     /**
677      * Get the active data call list.
678      *
679      * @param onCompleteMessage The result message for this request. Null if the client does not
680      *        care about the result.
681      */
requestDataCallList(Message onCompleteMessage)682     public void requestDataCallList(Message onCompleteMessage) {
683         if (DBG) log("requestDataCallList");
684         if (!mBound) {
685             loge("Data service not bound.");
686             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
687             return;
688         }
689 
690         CellularDataServiceCallback callback =
691                 new CellularDataServiceCallback("requestDataCallList");
692         if (onCompleteMessage != null) {
693             mMessageMap.put(callback.asBinder(), onCompleteMessage);
694         }
695         try {
696             mIDataService.requestDataCallList(mPhone.getPhoneId(), callback);
697         } catch (RemoteException e) {
698             loge("Cannot invoke requestDataCallList on data service.");
699             if (callback != null) {
700                 mMessageMap.remove(callback.asBinder());
701             }
702             sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
703         }
704     }
705 
706     /**
707      * Register for data call list changed event.
708      *
709      * @param h The target to post the event message to.
710      * @param what The event.
711      */
registerForDataCallListChanged(Handler h, int what)712     public void registerForDataCallListChanged(Handler h, int what) {
713         if (h != null) {
714             mDataCallListChangedRegistrants.addUnique(h, what, null);
715         }
716     }
717 
718     /**
719      * Unregister for data call list changed event.
720      *
721      * @param h The handler
722      */
unregisterForDataCallListChanged(Handler h)723     public void unregisterForDataCallListChanged(Handler h) {
724         if (h != null) {
725             mDataCallListChangedRegistrants.remove(h);
726         }
727     }
728 
729     /**
730      * Register for data service binding status changed event.
731      *
732      * @param h The target to post the event message to.
733      * @param what The event.
734      * @param obj The user object.
735      */
registerForServiceBindingChanged(Handler h, int what, Object obj)736     public void registerForServiceBindingChanged(Handler h, int what, Object obj) {
737         if (h != null) {
738             mServiceBindingChangedRegistrants.addUnique(h, what, obj);
739         }
740 
741     }
742 
743     /**
744      * Unregister for data service binding status changed event.
745      *
746      * @param h The handler
747      */
unregisterForServiceBindingChanged(Handler h)748     public void unregisterForServiceBindingChanged(Handler h) {
749         if (h != null) {
750             mServiceBindingChangedRegistrants.remove(h);
751         }
752     }
753 
754     /**
755      * Get the transport type. Must be a {@link TransportType}.
756      *
757      * @return
758      */
getTransportType()759     public int getTransportType() {
760         return mTransportType;
761     }
762 
log(String s)763     private void log(String s) {
764         Rlog.d(mTag, s);
765     }
766 
loge(String s)767     private void loge(String s) {
768         Rlog.e(mTag, s);
769     }
770 
771 }
772