EuiccController.java revision 2938f11d0b1e409089828c0247a5b79f3550a35a
1/* 2 * Copyright (C) 2017 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 */ 16package com.android.internal.telephony.euicc; 17 18import android.Manifest; 19import android.annotation.Nullable; 20import android.app.AppOpsManager; 21import android.app.PendingIntent; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.PackageInfo; 25import android.content.pm.PackageManager; 26import android.os.Binder; 27import android.os.Bundle; 28import android.os.ServiceManager; 29import android.provider.Settings; 30import android.service.euicc.EuiccService; 31import android.service.euicc.GetDefaultDownloadableSubscriptionListResult; 32import android.service.euicc.GetDownloadableSubscriptionMetadataResult; 33import android.service.euicc.GetEuiccProfileInfoListResult; 34import android.telephony.SubscriptionInfo; 35import android.telephony.SubscriptionManager; 36import android.telephony.TelephonyManager; 37import android.telephony.UiccAccessRule; 38import android.telephony.euicc.DownloadableSubscription; 39import android.telephony.euicc.EuiccInfo; 40import android.telephony.euicc.EuiccManager; 41import android.util.Log; 42 43import com.android.internal.annotations.VisibleForTesting; 44import com.android.internal.telephony.SubscriptionController; 45 46import java.io.FileDescriptor; 47import java.io.PrintWriter; 48import java.util.List; 49import java.util.concurrent.CountDownLatch; 50import java.util.concurrent.atomic.AtomicReference; 51 52/** Backing implementation of {@link android.telephony.euicc.EuiccManager}. */ 53public class EuiccController extends IEuiccController.Stub { 54 private static final String TAG = "EuiccController"; 55 56 /** Extra set on resolution intents containing the {@link EuiccOperation}. */ 57 @VisibleForTesting 58 static final String EXTRA_OPERATION = "operation"; 59 60 // Aliases so line lengths stay short. 61 private static final int OK = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK; 62 private static final int RESOLVABLE_ERROR = 63 EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR; 64 private static final int ERROR = 65 EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR; 66 67 private static EuiccController sInstance; 68 69 private final Context mContext; 70 private final EuiccConnector mConnector; 71 private final SubscriptionManager mSubscriptionManager; 72 private final AppOpsManager mAppOpsManager; 73 private final PackageManager mPackageManager; 74 75 /** Initialize the instance. Should only be called once. */ 76 public static EuiccController init(Context context) { 77 synchronized (EuiccController.class) { 78 if (sInstance == null) { 79 sInstance = new EuiccController(context); 80 } else { 81 Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance); 82 } 83 } 84 return sInstance; 85 } 86 87 /** Get an instance. Assumes one has already been initialized with {@link #init}. */ 88 public static EuiccController get() { 89 if (sInstance == null) { 90 synchronized (EuiccController.class) { 91 if (sInstance == null) { 92 throw new IllegalStateException("get() called before init()"); 93 } 94 } 95 } 96 return sInstance; 97 } 98 99 private EuiccController(Context context) { 100 this(context, new EuiccConnector(context)); 101 ServiceManager.addService("econtroller", this); 102 } 103 104 @VisibleForTesting 105 public EuiccController(Context context, EuiccConnector connector) { 106 mContext = context; 107 mConnector = connector; 108 mSubscriptionManager = (SubscriptionManager) 109 context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 110 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 111 mPackageManager = context.getPackageManager(); 112 } 113 114 /** 115 * Continue an operation which failed with a user-resolvable error. 116 * 117 * <p>The implementation here makes a key assumption that the resolutionIntent has not been 118 * tampered with. This is guaranteed because: 119 * <UL> 120 * <LI>The intent is wrapped in a PendingIntent created by the phone process which is created 121 * with {@link #EXTRA_OPERATION} already present. This means that the operation cannot be 122 * overridden on the PendingIntent - a caller can only add new extras. 123 * <LI>The resolution activity is restricted by a privileged permission; unprivileged apps 124 * cannot start it directly. So the PendingIntent is the only way to start it. 125 * </UL> 126 */ 127 @Override 128 public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) { 129 if (!callerCanWriteEmbeddedSubscriptions()) { 130 throw new SecurityException( 131 "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to continue operation"); 132 } 133 long token = Binder.clearCallingIdentity(); 134 try { 135 EuiccOperation op = resolutionIntent.getParcelableExtra(EXTRA_OPERATION); 136 if (op == null) { 137 throw new IllegalArgumentException("Invalid resolution intent"); 138 } 139 140 PendingIntent callbackIntent = 141 resolutionIntent.getParcelableExtra( 142 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT); 143 op.continueOperation(resolutionExtras, callbackIntent); 144 } finally { 145 Binder.restoreCallingIdentity(token); 146 } 147 } 148 149 /** 150 * Return the EID. 151 * 152 * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load, 153 * that IPC should generally be fast, and the EID shouldn't be needed in the normal course of 154 * operation. 155 */ 156 @Override 157 public String getEid() { 158 if (!callerCanReadPhoneStatePrivileged() 159 && !callerHasCarrierPrivilegesForActiveSubscription()) { 160 throw new SecurityException( 161 "Must have carrier privileges on active subscription to read EID"); 162 } 163 long token = Binder.clearCallingIdentity(); 164 try { 165 return blockingGetEidFromEuiccService(); 166 } finally { 167 Binder.restoreCallingIdentity(token); 168 } 169 } 170 171 @Override 172 public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription, 173 String callingPackage, PendingIntent callbackIntent) { 174 getDownloadableSubscriptionMetadata( 175 subscription, false /* forceDeactivateSim */, callingPackage, callbackIntent); 176 } 177 178 void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription, 179 boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) { 180 if (!callerCanWriteEmbeddedSubscriptions()) { 181 throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get metadata"); 182 } 183 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 184 long token = Binder.clearCallingIdentity(); 185 try { 186 mConnector.getDownloadableSubscriptionMetadata( 187 subscription, forceDeactivateSim, 188 new GetMetadataCommandCallback( 189 token, subscription, callingPackage, callbackIntent)); 190 } finally { 191 Binder.restoreCallingIdentity(token); 192 } 193 } 194 195 class GetMetadataCommandCallback implements EuiccConnector.GetMetadataCommandCallback { 196 protected final long mCallingToken; 197 protected final DownloadableSubscription mSubscription; 198 protected final String mCallingPackage; 199 protected final PendingIntent mCallbackIntent; 200 201 GetMetadataCommandCallback( 202 long callingToken, 203 DownloadableSubscription subscription, 204 String callingPackage, 205 PendingIntent callbackIntent) { 206 mCallingToken = callingToken; 207 mSubscription = subscription; 208 mCallingPackage = callingPackage; 209 mCallbackIntent = callbackIntent; 210 } 211 212 @Override 213 public void onGetMetadataComplete( 214 GetDownloadableSubscriptionMetadataResult result) { 215 Intent extrasIntent = new Intent(); 216 final int resultCode; 217 switch (result.result) { 218 case EuiccService.RESULT_OK: 219 resultCode = OK; 220 extrasIntent.putExtra( 221 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION, 222 result.subscription); 223 break; 224 case EuiccService.RESULT_MUST_DEACTIVATE_SIM: 225 resultCode = RESOLVABLE_ERROR; 226 addResolutionIntent(extrasIntent, 227 EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM, 228 mCallingPackage, 229 getOperationForDeactivateSim()); 230 break; 231 default: 232 resultCode = ERROR; 233 extrasIntent.putExtra( 234 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 235 result.result); 236 break; 237 } 238 239 sendResult(mCallbackIntent, resultCode, extrasIntent); 240 } 241 242 @Override 243 public void onEuiccServiceUnavailable() { 244 sendResult(mCallbackIntent, ERROR, null /* extrasIntent */); 245 } 246 247 protected EuiccOperation getOperationForDeactivateSim() { 248 return EuiccOperation.forGetMetadataDeactivateSim( 249 mCallingToken, mSubscription, mCallingPackage); 250 } 251 } 252 253 @Override 254 public void downloadSubscription(DownloadableSubscription subscription, 255 boolean switchAfterDownload, String callingPackage, PendingIntent callbackIntent) { 256 downloadSubscription(subscription, switchAfterDownload, callingPackage, 257 false /* forceDeactivateSim */, callbackIntent); 258 } 259 260 void downloadSubscription(DownloadableSubscription subscription, 261 boolean switchAfterDownload, String callingPackage, boolean forceDeactivateSim, 262 PendingIntent callbackIntent) { 263 boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions(); 264 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 265 266 long token = Binder.clearCallingIdentity(); 267 try { 268 if (callerCanWriteEmbeddedSubscriptions) { 269 // With WRITE_EMBEDDED_SUBSCRIPTIONS, we can skip profile-specific permission checks 270 // and move straight to the profile download. 271 downloadSubscriptionPrivileged(token, subscription, switchAfterDownload, 272 forceDeactivateSim, callingPackage, callbackIntent); 273 return; 274 } 275 // Without WRITE_EMBEDDED_SUBSCRIPTIONS, the caller *must* be whitelisted per the 276 // metadata of the profile to be downloaded, so check the metadata first. 277 mConnector.getDownloadableSubscriptionMetadata(subscription, 278 forceDeactivateSim, 279 new DownloadSubscriptionGetMetadataCommandCallback(token, subscription, 280 switchAfterDownload, callingPackage, forceDeactivateSim, 281 callbackIntent)); 282 } finally { 283 Binder.restoreCallingIdentity(token); 284 } 285 } 286 287 class DownloadSubscriptionGetMetadataCommandCallback extends GetMetadataCommandCallback { 288 private final boolean mSwitchAfterDownload; 289 private final boolean mForceDeactivateSim; 290 291 DownloadSubscriptionGetMetadataCommandCallback(long callingToken, 292 DownloadableSubscription subscription, boolean switchAfterDownload, 293 String callingPackage, boolean forceDeactivateSim, 294 PendingIntent callbackIntent) { 295 super(callingToken, subscription, callingPackage, callbackIntent); 296 mSwitchAfterDownload = switchAfterDownload; 297 mForceDeactivateSim = forceDeactivateSim; 298 } 299 300 @Override 301 public void onGetMetadataComplete( 302 GetDownloadableSubscriptionMetadataResult result) { 303 if (result.result == EuiccService.RESULT_MUST_DEACTIVATE_SIM) { 304 // If we need to deactivate the current SIM to even check permissions, go ahead and 305 // require that the user resolve the stronger permission dialog. 306 Intent extrasIntent = new Intent(); 307 addResolutionIntent(extrasIntent, EuiccService.ACTION_RESOLVE_NO_PRIVILEGES, 308 mCallingPackage, 309 EuiccOperation.forDownloadNoPrivileges( 310 mCallingToken, mSubscription, mSwitchAfterDownload, 311 mCallingPackage)); 312 sendResult(mCallbackIntent, RESOLVABLE_ERROR, extrasIntent); 313 return; 314 } 315 316 if (result.result != EuiccService.RESULT_OK) { 317 // Just propagate the error as normal. 318 super.onGetMetadataComplete(result); 319 return; 320 } 321 322 DownloadableSubscription subscription = result.subscription; 323 UiccAccessRule[] rules = subscription.getAccessRules(); 324 if (rules == null) { 325 Log.e(TAG, "No access rules but caller is unprivileged"); 326 sendResult(mCallbackIntent, ERROR, null /* extrasIntent */); 327 return; 328 } 329 330 final PackageInfo info; 331 try { 332 info = mPackageManager.getPackageInfo( 333 mCallingPackage, PackageManager.GET_SIGNATURES); 334 } catch (PackageManager.NameNotFoundException e) { 335 Log.e(TAG, "Calling package valid but gone"); 336 sendResult(mCallbackIntent, ERROR, null /* extrasIntent */); 337 return; 338 } 339 340 for (int i = 0; i < rules.length; i++) { 341 if (rules[i].getCarrierPrivilegeStatus(info) 342 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 343 // Caller can download this profile. Now, determine whether the caller can also 344 // manage the current profile; if so, we can perform the download silently; if 345 // not, the user must provide consent. 346 if (canManageActiveSubscription(mCallingPackage)) { 347 downloadSubscriptionPrivileged( 348 mCallingToken, subscription, mSwitchAfterDownload, 349 mForceDeactivateSim, mCallingPackage, mCallbackIntent); 350 return; 351 } 352 353 // Switch might still be permitted, but the user must consent first. 354 Intent extrasIntent = new Intent(); 355 addResolutionIntent(extrasIntent, EuiccService.ACTION_RESOLVE_NO_PRIVILEGES, 356 mCallingPackage, 357 EuiccOperation.forDownloadNoPrivileges( 358 mCallingToken, subscription, mSwitchAfterDownload, 359 mCallingPackage)); 360 sendResult(mCallbackIntent, RESOLVABLE_ERROR, extrasIntent); 361 return; 362 } 363 } 364 Log.e(TAG, "Caller is not permitted to download this profile"); 365 sendResult(mCallbackIntent, ERROR, null /* extrasIntent */); 366 } 367 368 @Override 369 protected EuiccOperation getOperationForDeactivateSim() { 370 return EuiccOperation.forDownloadDeactivateSim( 371 mCallingToken, mSubscription, mSwitchAfterDownload, mCallingPackage); 372 } 373 } 374 375 void downloadSubscriptionPrivileged(final long callingToken, 376 DownloadableSubscription subscription, boolean switchAfterDownload, 377 boolean forceDeactivateSim, final String callingPackage, 378 final PendingIntent callbackIntent) { 379 mConnector.downloadSubscription( 380 subscription, 381 switchAfterDownload, 382 forceDeactivateSim, 383 new EuiccConnector.DownloadCommandCallback() { 384 @Override 385 public void onDownloadComplete(int result) { 386 Intent extrasIntent = new Intent(); 387 final int resultCode; 388 switch (result) { 389 case EuiccService.RESULT_OK: 390 resultCode = OK; 391 // Now that a profile has been successfully downloaded, mark the 392 // eUICC as provisioned so it appears in settings UI as appropriate. 393 Settings.Global.putInt( 394 mContext.getContentResolver(), 395 Settings.Global.EUICC_PROVISIONED, 396 1); 397 if (!switchAfterDownload) { 398 // Since we're not switching, nothing will trigger a 399 // subscription list refresh on its own, so request one here. 400 refreshSubscriptionsAndSendResult( 401 callbackIntent, resultCode, extrasIntent); 402 return; 403 } 404 break; 405 case EuiccService.RESULT_MUST_DEACTIVATE_SIM: 406 resultCode = RESOLVABLE_ERROR; 407 addResolutionIntent(extrasIntent, 408 EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM, 409 callingPackage, 410 EuiccOperation.forDownloadDeactivateSim( 411 callingToken, subscription, switchAfterDownload, 412 callingPackage)); 413 break; 414 default: 415 resultCode = ERROR; 416 extrasIntent.putExtra( 417 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 418 result); 419 break; 420 } 421 422 sendResult(callbackIntent, resultCode, extrasIntent); 423 } 424 425 @Override 426 public void onEuiccServiceUnavailable() { 427 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 428 } 429 }); 430 } 431 432 /** 433 * Blocking call to {@link EuiccService#onGetEuiccProfileInfoList}. 434 * 435 * <p>Does not perform permission checks as this is not an exposed API and is only used within 436 * the phone process. 437 */ 438 public GetEuiccProfileInfoListResult blockingGetEuiccProfileInfoList() { 439 final CountDownLatch latch = new CountDownLatch(1); 440 final AtomicReference<GetEuiccProfileInfoListResult> resultRef = new AtomicReference<>(); 441 mConnector.getEuiccProfileInfoList( 442 new EuiccConnector.GetEuiccProfileInfoListCommandCallback() { 443 @Override 444 public void onListComplete(GetEuiccProfileInfoListResult result) { 445 resultRef.set(result); 446 latch.countDown(); 447 } 448 449 @Override 450 public void onEuiccServiceUnavailable() { 451 latch.countDown(); 452 } 453 }); 454 try { 455 latch.await(); 456 } catch (InterruptedException e) { 457 Thread.currentThread().interrupt(); 458 } 459 return resultRef.get(); 460 } 461 462 @Override 463 public void getDefaultDownloadableSubscriptionList( 464 String callingPackage, PendingIntent callbackIntent) { 465 getDefaultDownloadableSubscriptionList( 466 false /* forceDeactivateSim */, callingPackage, callbackIntent); 467 } 468 469 void getDefaultDownloadableSubscriptionList( 470 boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) { 471 if (!callerCanWriteEmbeddedSubscriptions()) { 472 throw new SecurityException( 473 "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get default list"); 474 } 475 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 476 long token = Binder.clearCallingIdentity(); 477 try { 478 mConnector.getDefaultDownloadableSubscriptionList( 479 forceDeactivateSim, new GetDefaultListCommandCallback( 480 token, callingPackage, callbackIntent)); 481 } finally { 482 Binder.restoreCallingIdentity(token); 483 } 484 } 485 486 class GetDefaultListCommandCallback implements EuiccConnector.GetDefaultListCommandCallback { 487 final long mCallingToken; 488 final String mCallingPackage; 489 final PendingIntent mCallbackIntent; 490 491 GetDefaultListCommandCallback(long callingToken, String callingPackage, 492 PendingIntent callbackIntent) { 493 mCallingToken = callingToken; 494 mCallingPackage = callingPackage; 495 mCallbackIntent = callbackIntent; 496 } 497 498 @Override 499 public void onGetDefaultListComplete(GetDefaultDownloadableSubscriptionListResult result) { 500 Intent extrasIntent = new Intent(); 501 final int resultCode; 502 switch (result.result) { 503 case EuiccService.RESULT_OK: 504 resultCode = OK; 505 extrasIntent.putExtra( 506 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS, 507 result.subscriptions); 508 break; 509 case EuiccService.RESULT_MUST_DEACTIVATE_SIM: 510 resultCode = RESOLVABLE_ERROR; 511 addResolutionIntent(extrasIntent, 512 EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM, 513 mCallingPackage, 514 EuiccOperation.forGetDefaultListDeactivateSim( 515 mCallingToken, mCallingPackage)); 516 break; 517 default: 518 resultCode = ERROR; 519 extrasIntent.putExtra( 520 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 521 result.result); 522 break; 523 } 524 525 sendResult(mCallbackIntent, resultCode, extrasIntent); 526 } 527 528 @Override 529 public void onEuiccServiceUnavailable() { 530 sendResult(mCallbackIntent, ERROR, null /* extrasIntent */); 531 } 532 } 533 534 /** 535 * Return the {@link EuiccInfo}. 536 * 537 * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load, 538 * that IPC should generally be fast, and this info shouldn't be needed in the normal course of 539 * operation. 540 */ 541 @Override 542 public EuiccInfo getEuiccInfo() { 543 // No permissions required as EuiccInfo is not sensitive. 544 long token = Binder.clearCallingIdentity(); 545 try { 546 return blockingGetEuiccInfoFromEuiccService(); 547 } finally { 548 Binder.restoreCallingIdentity(token); 549 } 550 } 551 552 @Override 553 public void deleteSubscription(int subscriptionId, String callingPackage, 554 PendingIntent callbackIntent) { 555 boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions(); 556 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 557 558 long token = Binder.clearCallingIdentity(); 559 try { 560 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 561 if (sub == null) { 562 Log.e(TAG, "Cannot delete nonexistent subscription: " + subscriptionId); 563 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 564 return; 565 } 566 567 if (!callerCanWriteEmbeddedSubscriptions 568 && !sub.canManageSubscription(mContext, callingPackage)) { 569 Log.e(TAG, "No permissions: " + subscriptionId); 570 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 571 return; 572 } 573 574 deleteSubscriptionPrivileged(sub.getIccId(), callbackIntent); 575 } finally { 576 Binder.restoreCallingIdentity(token); 577 } 578 } 579 580 void deleteSubscriptionPrivileged(String iccid, final PendingIntent callbackIntent) { 581 mConnector.deleteSubscription( 582 iccid, 583 new EuiccConnector.DeleteCommandCallback() { 584 @Override 585 public void onDeleteComplete(int result) { 586 Intent extrasIntent = new Intent(); 587 final int resultCode; 588 switch (result) { 589 case EuiccService.RESULT_OK: 590 resultCode = OK; 591 refreshSubscriptionsAndSendResult( 592 callbackIntent, resultCode, extrasIntent); 593 return; 594 default: 595 resultCode = ERROR; 596 extrasIntent.putExtra( 597 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 598 result); 599 break; 600 } 601 602 sendResult(callbackIntent, resultCode, extrasIntent); 603 } 604 605 @Override 606 public void onEuiccServiceUnavailable() { 607 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 608 } 609 }); 610 } 611 612 @Override 613 public void switchToSubscription(int subscriptionId, String callingPackage, 614 PendingIntent callbackIntent) { 615 switchToSubscription( 616 subscriptionId, false /* forceDeactivateSim */, callingPackage, callbackIntent); 617 } 618 619 void switchToSubscription(int subscriptionId, boolean forceDeactivateSim, String callingPackage, 620 PendingIntent callbackIntent) { 621 boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions(); 622 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 623 624 long token = Binder.clearCallingIdentity(); 625 try { 626 if (callerCanWriteEmbeddedSubscriptions) { 627 // Assume that if a privileged caller is calling us, we don't need to prompt the 628 // user about changing carriers, because the caller would only be acting in response 629 // to user action. 630 forceDeactivateSim = true; 631 } 632 633 final String iccid; 634 if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 635 // Switch to "no" subscription. Only the system can do this. 636 if (!callerCanWriteEmbeddedSubscriptions) { 637 Log.e(TAG, "Not permitted to switch to empty subscription"); 638 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 639 return; 640 } 641 iccid = null; 642 } else { 643 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 644 if (sub == null) { 645 Log.e(TAG, "Cannot switch to nonexistent subscription: " + subscriptionId); 646 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 647 return; 648 } 649 if (!callerCanWriteEmbeddedSubscriptions 650 && !sub.canManageSubscription(mContext, callingPackage)) { 651 Log.e(TAG, "Not permitted to switch to subscription: " + subscriptionId); 652 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 653 return; 654 } 655 iccid = sub.getIccId(); 656 } 657 658 if (!callerCanWriteEmbeddedSubscriptions 659 && !canManageActiveSubscription(callingPackage)) { 660 // Switch needs consent. 661 Intent extrasIntent = new Intent(); 662 addResolutionIntent(extrasIntent, 663 EuiccService.ACTION_RESOLVE_NO_PRIVILEGES, 664 callingPackage, 665 EuiccOperation.forSwitchNoPrivileges( 666 token, subscriptionId, callingPackage)); 667 sendResult(callbackIntent, RESOLVABLE_ERROR, extrasIntent); 668 return; 669 } 670 671 switchToSubscriptionPrivileged(token, subscriptionId, iccid, forceDeactivateSim, 672 callingPackage, callbackIntent); 673 } finally { 674 Binder.restoreCallingIdentity(token); 675 } 676 } 677 678 void switchToSubscriptionPrivileged(final long callingToken, int subscriptionId, 679 boolean forceDeactivateSim, final String callingPackage, 680 final PendingIntent callbackIntent) { 681 String iccid = null; 682 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 683 if (sub != null) { 684 iccid = sub.getIccId(); 685 } 686 switchToSubscriptionPrivileged(callingToken, subscriptionId, iccid, forceDeactivateSim, 687 callingPackage, callbackIntent); 688 } 689 690 void switchToSubscriptionPrivileged(final long callingToken, int subscriptionId, 691 @Nullable String iccid, boolean forceDeactivateSim, final String callingPackage, 692 final PendingIntent callbackIntent) { 693 mConnector.switchToSubscription( 694 iccid, 695 forceDeactivateSim, 696 new EuiccConnector.SwitchCommandCallback() { 697 @Override 698 public void onSwitchComplete(int result) { 699 Intent extrasIntent = new Intent(); 700 final int resultCode; 701 switch (result) { 702 case EuiccService.RESULT_OK: 703 resultCode = OK; 704 break; 705 case EuiccService.RESULT_MUST_DEACTIVATE_SIM: 706 resultCode = RESOLVABLE_ERROR; 707 addResolutionIntent(extrasIntent, 708 EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM, 709 callingPackage, 710 EuiccOperation.forSwitchDeactivateSim( 711 callingToken, subscriptionId, callingPackage)); 712 break; 713 default: 714 resultCode = ERROR; 715 extrasIntent.putExtra( 716 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 717 result); 718 break; 719 } 720 721 sendResult(callbackIntent, resultCode, extrasIntent); 722 } 723 724 @Override 725 public void onEuiccServiceUnavailable() { 726 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 727 } 728 }); 729 } 730 731 @Override 732 public void updateSubscriptionNickname(int subscriptionId, String nickname, 733 PendingIntent callbackIntent) { 734 if (!callerCanWriteEmbeddedSubscriptions()) { 735 throw new SecurityException( 736 "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to update nickname"); 737 } 738 long token = Binder.clearCallingIdentity(); 739 try { 740 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 741 if (sub == null) { 742 Log.e(TAG, "Cannot update nickname to nonexistent subscription: " + subscriptionId); 743 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 744 return; 745 } 746 mConnector.updateSubscriptionNickname( 747 sub.getIccId(), nickname, 748 new EuiccConnector.UpdateNicknameCommandCallback() { 749 @Override 750 public void onUpdateNicknameComplete(int result) { 751 Intent extrasIntent = new Intent(); 752 final int resultCode; 753 switch (result) { 754 case EuiccService.RESULT_OK: 755 resultCode = OK; 756 break; 757 default: 758 resultCode = ERROR; 759 extrasIntent.putExtra( 760 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 761 result); 762 break; 763 } 764 765 sendResult(callbackIntent, resultCode, extrasIntent); 766 } 767 768 @Override 769 public void onEuiccServiceUnavailable() { 770 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 771 } 772 }); 773 } finally { 774 Binder.restoreCallingIdentity(token); 775 } 776 } 777 778 @Override 779 public void eraseSubscriptions(PendingIntent callbackIntent) { 780 if (!callerCanWriteEmbeddedSubscriptions()) { 781 throw new SecurityException( 782 "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to erase subscriptions"); 783 } 784 long token = Binder.clearCallingIdentity(); 785 try { 786 mConnector.eraseSubscriptions(new EuiccConnector.EraseCommandCallback() { 787 @Override 788 public void onEraseComplete(int result) { 789 Intent extrasIntent = new Intent(); 790 final int resultCode; 791 switch (result) { 792 case EuiccService.RESULT_OK: 793 resultCode = OK; 794 refreshSubscriptionsAndSendResult( 795 callbackIntent, resultCode, extrasIntent); 796 return; 797 default: 798 resultCode = ERROR; 799 extrasIntent.putExtra( 800 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 801 result); 802 break; 803 } 804 805 sendResult(callbackIntent, resultCode, extrasIntent); 806 } 807 808 @Override 809 public void onEuiccServiceUnavailable() { 810 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 811 } 812 }); 813 } finally { 814 Binder.restoreCallingIdentity(token); 815 } 816 } 817 818 @Override 819 public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) { 820 mContext.enforceCallingPermission(Manifest.permission.MASTER_CLEAR, 821 "Must have MASTER_CLEAR to retain subscriptions for factory reset"); 822 long token = Binder.clearCallingIdentity(); 823 try { 824 mConnector.retainSubscriptions( 825 new EuiccConnector.RetainSubscriptionsCommandCallback() { 826 @Override 827 public void onRetainSubscriptionsComplete(int result) { 828 Intent extrasIntent = new Intent(); 829 final int resultCode; 830 switch (result) { 831 case EuiccService.RESULT_OK: 832 resultCode = OK; 833 break; 834 default: 835 resultCode = ERROR; 836 extrasIntent.putExtra( 837 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 838 result); 839 break; 840 } 841 842 sendResult(callbackIntent, resultCode, extrasIntent); 843 } 844 845 @Override 846 public void onEuiccServiceUnavailable() { 847 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 848 } 849 }); 850 } finally { 851 Binder.restoreCallingIdentity(token); 852 } 853 } 854 855 /** Refresh the embedded subscription list and dispatch the given result upon completion. */ 856 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 857 public void refreshSubscriptionsAndSendResult( 858 PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { 859 SubscriptionController.getInstance() 860 .requestEmbeddedSubscriptionInfoListRefresh( 861 () -> sendResult(callbackIntent, resultCode, extrasIntent)); 862 } 863 864 /** Dispatch the given callback intent with the given result code and data. */ 865 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 866 public void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { 867 try { 868 callbackIntent.send(mContext, resultCode, extrasIntent); 869 } catch (PendingIntent.CanceledException e) { 870 // Caller canceled the callback; do nothing. 871 } 872 } 873 874 /** Add a resolution intent to the given extras intent. */ 875 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 876 public void addResolutionIntent(Intent extrasIntent, String resolutionAction, 877 String callingPackage, EuiccOperation op) { 878 Intent intent = new Intent(EuiccManager.ACTION_RESOLVE_ERROR); 879 intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION, 880 resolutionAction); 881 intent.putExtra(EuiccService.EXTRA_RESOLUTION_CALLING_PACKAGE, callingPackage); 882 intent.putExtra(EXTRA_OPERATION, op); 883 PendingIntent resolutionIntent = PendingIntent.getActivity( 884 mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_ONE_SHOT); 885 extrasIntent.putExtra( 886 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT, resolutionIntent); 887 } 888 889 @Override 890 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 891 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP"); 892 final long token = Binder.clearCallingIdentity(); 893 try { 894 mConnector.dump(fd, pw, args); 895 } finally { 896 Binder.restoreCallingIdentity(token); 897 } 898 } 899 900 @Nullable 901 private SubscriptionInfo getSubscriptionForSubscriptionId(int subscriptionId) { 902 List<SubscriptionInfo> subs = mSubscriptionManager.getAvailableSubscriptionInfoList(); 903 int subCount = subs.size(); 904 for (int i = 0; i < subCount; i++) { 905 SubscriptionInfo sub = subs.get(i); 906 if (subscriptionId == sub.getSubscriptionId()) { 907 return sub; 908 } 909 } 910 return null; 911 } 912 913 @Nullable 914 private String blockingGetEidFromEuiccService() { 915 CountDownLatch latch = new CountDownLatch(1); 916 AtomicReference<String> eidRef = new AtomicReference<>(); 917 mConnector.getEid(new EuiccConnector.GetEidCommandCallback() { 918 @Override 919 public void onGetEidComplete(String eid) { 920 eidRef.set(eid); 921 latch.countDown(); 922 } 923 924 @Override 925 public void onEuiccServiceUnavailable() { 926 latch.countDown(); 927 } 928 }); 929 return awaitResult(latch, eidRef); 930 } 931 932 @Nullable 933 private EuiccInfo blockingGetEuiccInfoFromEuiccService() { 934 CountDownLatch latch = new CountDownLatch(1); 935 AtomicReference<EuiccInfo> euiccInfoRef = new AtomicReference<>(); 936 mConnector.getEuiccInfo(new EuiccConnector.GetEuiccInfoCommandCallback() { 937 @Override 938 public void onGetEuiccInfoComplete(EuiccInfo euiccInfo) { 939 euiccInfoRef.set(euiccInfo); 940 latch.countDown(); 941 } 942 943 @Override 944 public void onEuiccServiceUnavailable() { 945 latch.countDown(); 946 } 947 }); 948 return awaitResult(latch, euiccInfoRef); 949 } 950 951 private static <T> T awaitResult(CountDownLatch latch, AtomicReference<T> resultRef) { 952 try { 953 latch.await(); 954 } catch (InterruptedException e) { 955 Thread.currentThread().interrupt(); 956 } 957 return resultRef.get(); 958 } 959 960 private boolean canManageActiveSubscription(String callingPackage) { 961 // TODO(b/36260308): We should plumb a slot ID through here for multi-SIM devices. 962 List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); 963 if (subInfoList == null) { 964 return false; 965 } 966 int size = subInfoList.size(); 967 for (int subIndex = 0; subIndex < size; subIndex++) { 968 SubscriptionInfo subInfo = subInfoList.get(subIndex); 969 if (subInfo.isEmbedded() && subInfo.canManageSubscription(mContext, callingPackage)) { 970 return true; 971 } 972 } 973 return false; 974 } 975 976 private boolean callerCanReadPhoneStatePrivileged() { 977 return mContext.checkCallingPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) 978 == PackageManager.PERMISSION_GRANTED; 979 } 980 981 private boolean callerCanWriteEmbeddedSubscriptions() { 982 return mContext.checkCallingPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) 983 == PackageManager.PERMISSION_GRANTED; 984 } 985 986 /** 987 * Returns whether the caller has carrier privileges for the active mSubscription on this eUICC. 988 */ 989 private boolean callerHasCarrierPrivilegesForActiveSubscription() { 990 // TODO(b/36260308): We should plumb a slot ID through here for multi-SIM devices. 991 TelephonyManager tm = 992 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 993 return tm.hasCarrierPrivileges(); 994 } 995} 996