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.telephony.AccessNetworkConstants.TransportType.WLAN; 20 import static android.telephony.AccessNetworkConstants.TransportType.WWAN; 21 22 import android.annotation.NonNull; 23 import android.app.AppOpsManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.ServiceConnection; 28 import android.content.pm.IPackageManager; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ResolveInfo; 31 import android.net.LinkProperties; 32 import android.os.AsyncResult; 33 import android.os.Handler; 34 import android.os.IBinder; 35 import android.os.Message; 36 import android.os.PersistableBundle; 37 import android.os.RegistrantList; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.telephony.CarrierConfigManager; 41 import android.telephony.Rlog; 42 import android.telephony.data.DataCallResponse; 43 import android.telephony.data.DataProfile; 44 import android.telephony.data.DataService; 45 import android.telephony.data.DataServiceCallback; 46 import android.telephony.data.IDataService; 47 import android.telephony.data.IDataServiceCallback; 48 import android.text.TextUtils; 49 50 import com.android.internal.telephony.Phone; 51 52 import java.util.HashSet; 53 import java.util.List; 54 import java.util.Map; 55 import java.util.Set; 56 import java.util.concurrent.ConcurrentHashMap; 57 58 /** 59 * Data service manager manages handling data requests and responses on data services (e.g. 60 * Cellular data service, IWLAN data service). 61 */ 62 public class DataServiceManager { 63 private static final String TAG = DataServiceManager.class.getSimpleName(); 64 private static final boolean DBG = false; 65 66 static final String DATA_CALL_RESPONSE = "data_call_response"; 67 68 private final Phone mPhone; 69 70 private final CarrierConfigManager mCarrierConfigManager; 71 private final AppOpsManager mAppOps; 72 private final IPackageManager mPackageManager; 73 74 private final int mTransportType; 75 76 private boolean mBound; 77 78 private IDataService mIDataService; 79 80 private DataServiceManagerDeathRecipient mDeathRecipient; 81 82 private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList(); 83 84 private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>(); 85 86 private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList(); 87 88 // not final because it is set by the onServiceConnected method 89 private ComponentName mComponentName; 90 91 private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient { 92 @Override 93 public void binderDied() { 94 // TODO: try to rebind the service. 95 loge("DataService(" + mComponentName + " transport type " + mTransportType 96 + ") died."); 97 } 98 } 99 100 private void grantPermissionsToService(String packageName) { 101 final String[] pkgToGrant = {packageName}; 102 try { 103 mPackageManager.grantDefaultPermissionsToEnabledTelephonyDataServices( 104 pkgToGrant, mPhone.getContext().getUserId()); 105 mAppOps.setMode(AppOpsManager.OP_MANAGE_IPSEC_TUNNELS, mPhone.getContext().getUserId(), 106 pkgToGrant[0], AppOpsManager.MODE_ALLOWED); 107 } catch (RemoteException e) { 108 loge("Binder to package manager died, permission grant for DataService failed."); 109 throw e.rethrowAsRuntimeException(); 110 } 111 } 112 113 /** 114 * Loop through all DataServices installed on the system and revoke permissions from any that 115 * are not currently the WWAN or WLAN data service. 116 */ 117 private void revokePermissionsFromUnusedDataServices() { 118 // Except the current data services from having their permissions removed. 119 Set<String> dataServices = getAllDataServicePackageNames(); 120 for (int transportType : new int[] {WWAN, WLAN}) { 121 dataServices.remove(getDataServicePackageName(transportType)); 122 } 123 124 try { 125 String[] dataServicesArray = new String[dataServices.size()]; 126 dataServices.toArray(dataServicesArray); 127 mPackageManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices( 128 dataServicesArray, mPhone.getContext().getUserId()); 129 for (String pkg : dataServices) { 130 mAppOps.setMode(AppOpsManager.OP_MANAGE_IPSEC_TUNNELS, 131 mPhone.getContext().getUserId(), 132 pkg, AppOpsManager.MODE_ERRORED); 133 } 134 } catch (RemoteException e) { 135 loge("Binder to package manager died; failed to revoke DataService permissions."); 136 throw e.rethrowAsRuntimeException(); 137 } 138 } 139 140 private final class CellularDataServiceConnection implements ServiceConnection { 141 @Override 142 public void onServiceConnected(ComponentName name, IBinder service) { 143 if (DBG) log("onServiceConnected"); 144 mComponentName = name; 145 mIDataService = IDataService.Stub.asInterface(service); 146 mDeathRecipient = new DataServiceManagerDeathRecipient(); 147 mBound = true; 148 149 try { 150 service.linkToDeath(mDeathRecipient, 0); 151 mIDataService.createDataServiceProvider(mPhone.getPhoneId()); 152 mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(), 153 new CellularDataServiceCallback()); 154 } catch (RemoteException e) { 155 mDeathRecipient.binderDied(); 156 loge("Remote exception. " + e); 157 return; 158 } 159 160 mServiceBindingChangedRegistrants.notifyResult(true); 161 } 162 @Override 163 public void onServiceDisconnected(ComponentName name) { 164 if (DBG) log("onServiceDisconnected"); 165 mIDataService.asBinder().unlinkToDeath(mDeathRecipient, 0); 166 mIDataService = null; 167 mBound = false; 168 mServiceBindingChangedRegistrants.notifyResult(false); 169 } 170 } 171 172 private final class CellularDataServiceCallback extends IDataServiceCallback.Stub { 173 @Override 174 public void onSetupDataCallComplete(@DataServiceCallback.ResultCode int resultCode, 175 DataCallResponse response) { 176 if (DBG) { 177 log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = " 178 + response); 179 } 180 Message msg = mMessageMap.remove(asBinder()); 181 if (msg != null) { 182 msg.getData().putParcelable(DATA_CALL_RESPONSE, response); 183 sendCompleteMessage(msg, resultCode); 184 } else { 185 loge("Unable to find the message for setup call response."); 186 } 187 } 188 189 @Override 190 public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) { 191 if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode); 192 Message msg = mMessageMap.remove(asBinder()); 193 sendCompleteMessage(msg, resultCode); 194 } 195 196 @Override 197 public void onSetInitialAttachApnComplete(@DataServiceCallback.ResultCode int resultCode) { 198 if (DBG) log("onSetInitialAttachApnComplete. resultCode = " + resultCode); 199 Message msg = mMessageMap.remove(asBinder()); 200 sendCompleteMessage(msg, resultCode); 201 } 202 203 @Override 204 public void onSetDataProfileComplete(@DataServiceCallback.ResultCode int resultCode) { 205 if (DBG) log("onSetDataProfileComplete. resultCode = " + resultCode); 206 Message msg = mMessageMap.remove(asBinder()); 207 sendCompleteMessage(msg, resultCode); 208 } 209 210 @Override 211 public void onGetDataCallListComplete(@DataServiceCallback.ResultCode int resultCode, 212 List<DataCallResponse> dataCallList) { 213 if (DBG) log("onGetDataCallListComplete. resultCode = " + resultCode); 214 Message msg = mMessageMap.remove(asBinder()); 215 sendCompleteMessage(msg, resultCode); 216 } 217 218 @Override 219 public void onDataCallListChanged(List<DataCallResponse> dataCallList) { 220 mDataCallListChangedRegistrants.notifyRegistrants( 221 new AsyncResult(null, dataCallList, null)); 222 } 223 } 224 225 /** 226 * Constructor 227 * 228 * @param phone The phone object 229 * @param transportType The transport type. Must be a 230 * {@link AccessNetworkConstants.TransportType}. 231 */ 232 public DataServiceManager(Phone phone, int transportType) { 233 mPhone = phone; 234 mTransportType = transportType; 235 mBound = false; 236 mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService( 237 Context.CARRIER_CONFIG_SERVICE); 238 mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 239 mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE); 240 bindDataService(); 241 } 242 243 private void bindDataService() { 244 // Start by cleaning up all packages that *shouldn't* have permissions. 245 revokePermissionsFromUnusedDataServices(); 246 247 String packageName = getDataServicePackageName(); 248 if (TextUtils.isEmpty(packageName)) { 249 loge("Can't find the binding package"); 250 return; 251 } 252 // Then pre-emptively grant the permissions to the package we will bind. 253 grantPermissionsToService(packageName); 254 255 try { 256 if (!mPhone.getContext().bindService( 257 new Intent(DataService.DATA_SERVICE_INTERFACE).setPackage(packageName), 258 new CellularDataServiceConnection(), 259 Context.BIND_AUTO_CREATE)) { 260 loge("Cannot bind to the data service."); 261 } 262 } catch (Exception e) { 263 loge("Cannot bind to the data service. Exception: " + e); 264 } 265 } 266 267 @NonNull 268 private Set<String> getAllDataServicePackageNames() { 269 // Cowardly using the public PackageManager interface here. 270 // Note: This matches only packages that were installed on the system image. If we ever 271 // expand the permissions model to allow CarrierPrivileged packages, then this will need 272 // to be updated. 273 List<ResolveInfo> dataPackages = 274 mPhone.getContext().getPackageManager().queryIntentServices( 275 new Intent(DataService.DATA_SERVICE_INTERFACE), 276 PackageManager.MATCH_SYSTEM_ONLY); 277 HashSet<String> packageNames = new HashSet<>(); 278 for (ResolveInfo info : dataPackages) { 279 if (info.serviceInfo == null) continue; 280 packageNames.add(info.serviceInfo.packageName); 281 } 282 return packageNames; 283 } 284 285 /** 286 * Get the data service package name for our current transport type. 287 * 288 * @return package name of the data service package for the the current transportType. 289 */ 290 private String getDataServicePackageName() { 291 return getDataServicePackageName(mTransportType); 292 } 293 294 /** 295 * Get the data service package by transport type. 296 * 297 * When we bind to a DataService package, we need to revoke permissions from stale 298 * packages; we need to exclude data packages for all transport types, so we need to 299 * to be able to query by transport type. 300 * 301 * @param transportType either WWAN or WLAN 302 * @return package name of the data service package for the specified transportType. 303 */ 304 private String getDataServicePackageName(int transportType) { 305 String packageName; 306 int resourceId; 307 String carrierConfig; 308 309 switch (transportType) { 310 case WWAN: 311 resourceId = com.android.internal.R.string.config_wwan_data_service_package; 312 carrierConfig = CarrierConfigManager 313 .KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING; 314 break; 315 case WLAN: 316 resourceId = com.android.internal.R.string.config_wlan_data_service_package; 317 carrierConfig = CarrierConfigManager 318 .KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING; 319 break; 320 default: 321 throw new IllegalStateException("Transport type not WWAN or WLAN. type=" 322 + mTransportType); 323 } 324 325 // Read package name from resource overlay 326 packageName = mPhone.getContext().getResources().getString(resourceId); 327 328 PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId()); 329 330 if (b != null) { 331 // If carrier config overrides it, use the one from carrier config 332 packageName = b.getString(carrierConfig, packageName); 333 } 334 335 return packageName; 336 } 337 338 private void sendCompleteMessage(Message msg, int code) { 339 if (msg != null) { 340 msg.arg1 = code; 341 msg.sendToTarget(); 342 } 343 } 344 345 /** 346 * Setup a data connection. The data service provider must implement this method to support 347 * establishing a packet data connection. When completed or error, the service must invoke 348 * the provided callback to notify the platform. 349 * 350 * @param accessNetworkType Access network type that the data call will be established on. 351 * Must be one of {@link AccessNetworkConstants.AccessNetworkType}. 352 * @param dataProfile Data profile used for data call setup. See {@link DataProfile} 353 * @param isRoaming True if the device is data roaming. 354 * @param allowRoaming True if data roaming is allowed by the user. 355 * @param reason The reason for data setup. Must be {@link DataService#REQUEST_REASON_NORMAL} or 356 * {@link DataService#REQUEST_REASON_HANDOVER}. 357 * @param linkProperties If {@code reason} is {@link DataService#REQUEST_REASON_HANDOVER}, this 358 * is the link properties of the existing data connection, otherwise null. 359 * @param onCompleteMessage The result message for this request. Null if the client does not 360 * care about the result. 361 */ 362 public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, 363 boolean allowRoaming, int reason, LinkProperties linkProperties, 364 Message onCompleteMessage) { 365 if (DBG) log("setupDataCall"); 366 if (!mBound) { 367 loge("Data service not bound."); 368 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 369 return; 370 } 371 372 CellularDataServiceCallback callback = null; 373 if (onCompleteMessage != null) { 374 callback = new CellularDataServiceCallback(); 375 mMessageMap.put(callback.asBinder(), onCompleteMessage); 376 } 377 try { 378 mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile, 379 isRoaming, allowRoaming, reason, linkProperties, callback); 380 } catch (RemoteException e) { 381 loge("Cannot invoke setupDataCall on data service."); 382 if (callback != null) { 383 mMessageMap.remove(callback.asBinder()); 384 } 385 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 386 } 387 } 388 389 /** 390 * Deactivate a data connection. The data service provider must implement this method to 391 * support data connection tear down. When completed or error, the service must invoke the 392 * provided callback to notify the platform. 393 * 394 * @param cid Call id returned in the callback of {@link #setupDataCall(int, DataProfile, 395 * boolean, boolean, int, LinkProperties, Message)} 396 * @param reason The reason for data deactivation. Must be 397 * {@link DataService#REQUEST_REASON_NORMAL}, {@link DataService#REQUEST_REASON_SHUTDOWN} 398 * or {@link DataService#REQUEST_REASON_HANDOVER}. 399 * @param onCompleteMessage The result message for this request. Null if the client does not 400 * care about the result. 401 */ 402 public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) { 403 if (DBG) log("deactivateDataCall"); 404 if (!mBound) { 405 loge("Data service not bound."); 406 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 407 return; 408 } 409 410 CellularDataServiceCallback callback = null; 411 if (onCompleteMessage != null) { 412 callback = new CellularDataServiceCallback(); 413 mMessageMap.put(callback.asBinder(), onCompleteMessage); 414 } 415 try { 416 mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback); 417 } catch (RemoteException e) { 418 loge("Cannot invoke deactivateDataCall on data service."); 419 if (callback != null) { 420 mMessageMap.remove(callback.asBinder()); 421 } 422 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 423 } 424 } 425 426 /** 427 * Set an APN to initial attach network. 428 * 429 * @param dataProfile Data profile used for data call setup. See {@link DataProfile}. 430 * @param isRoaming True if the device is data roaming. 431 * @param onCompleteMessage The result message for this request. Null if the client does not 432 * care about the result. 433 */ 434 public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, 435 Message onCompleteMessage) { 436 if (DBG) log("setInitialAttachApn"); 437 if (!mBound) { 438 loge("Data service not bound."); 439 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 440 return; 441 } 442 443 CellularDataServiceCallback callback = null; 444 if (onCompleteMessage != null) { 445 callback = new CellularDataServiceCallback(); 446 mMessageMap.put(callback.asBinder(), onCompleteMessage); 447 } 448 try { 449 mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming, 450 callback); 451 } catch (RemoteException e) { 452 loge("Cannot invoke setInitialAttachApn on data service."); 453 if (callback != null) { 454 mMessageMap.remove(callback.asBinder()); 455 } 456 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 457 } 458 } 459 460 /** 461 * Send current carrier's data profiles to the data service for data call setup. This is 462 * only for CDMA carrier that can change the profile through OTA. The data service should 463 * always uses the latest data profile sent by the framework. 464 * 465 * @param dps A list of data profiles. 466 * @param isRoaming True if the device is data roaming. 467 * @param onCompleteMessage The result message for this request. Null if the client does not 468 * care about the result. 469 */ 470 public void setDataProfile(List<DataProfile> dps, boolean isRoaming, 471 Message onCompleteMessage) { 472 if (DBG) log("setDataProfile"); 473 if (!mBound) { 474 loge("Data service not bound."); 475 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 476 return; 477 } 478 479 CellularDataServiceCallback callback = null; 480 if (onCompleteMessage != null) { 481 callback = new CellularDataServiceCallback(); 482 mMessageMap.put(callback.asBinder(), onCompleteMessage); 483 } 484 try { 485 mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback); 486 } catch (RemoteException e) { 487 loge("Cannot invoke setDataProfile on data service."); 488 if (callback != null) { 489 mMessageMap.remove(callback.asBinder()); 490 } 491 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 492 } 493 } 494 495 /** 496 * Get the active data call list. 497 * 498 * @param onCompleteMessage The result message for this request. Null if the client does not 499 * care about the result. 500 */ 501 public void getDataCallList(Message onCompleteMessage) { 502 if (DBG) log("getDataCallList"); 503 if (!mBound) { 504 loge("Data service not bound."); 505 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 506 return; 507 } 508 509 CellularDataServiceCallback callback = null; 510 if (onCompleteMessage != null) { 511 callback = new CellularDataServiceCallback(); 512 mMessageMap.put(callback.asBinder(), onCompleteMessage); 513 } 514 try { 515 mIDataService.getDataCallList(mPhone.getPhoneId(), callback); 516 } catch (RemoteException e) { 517 loge("Cannot invoke getDataCallList on data service."); 518 if (callback != null) { 519 mMessageMap.remove(callback.asBinder()); 520 } 521 sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE); 522 } 523 } 524 525 /** 526 * Register for data call list changed event. 527 * 528 * @param h The target to post the event message to. 529 * @param what The event. 530 */ 531 public void registerForDataCallListChanged(Handler h, int what) { 532 if (h != null) { 533 mDataCallListChangedRegistrants.addUnique(h, what, null); 534 } 535 } 536 537 /** 538 * Unregister for data call list changed event. 539 * 540 * @param h The handler 541 */ 542 public void unregisterForDataCallListChanged(Handler h) { 543 if (h != null) { 544 mDataCallListChangedRegistrants.remove(h); 545 } 546 } 547 548 /** 549 * Register for data service binding status changed event. 550 * 551 * @param h The target to post the event message to. 552 * @param what The event. 553 * @param obj The user object. 554 */ 555 public void registerForServiceBindingChanged(Handler h, int what, Object obj) { 556 if (h != null) { 557 mServiceBindingChangedRegistrants.addUnique(h, what, obj); 558 } 559 560 } 561 562 /** 563 * Unregister for data service binding status changed event. 564 * 565 * @param h The handler 566 */ 567 public void unregisterForServiceBindingChanged(Handler h) { 568 if (h != null) { 569 mServiceBindingChangedRegistrants.remove(h); 570 } 571 } 572 573 /** 574 * Get the transport type. Must be a {@link AccessNetworkConstants.TransportType}. 575 * 576 * @return 577 */ 578 public int getTransportType() { 579 return mTransportType; 580 } 581 582 private void log(String s) { 583 Rlog.d(TAG, s); 584 } 585 586 private void loge(String s) { 587 Rlog.e(TAG, s); 588 } 589 590 } 591