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