EuiccController.java revision 9af6424827eb9e023e037dd5aefd229235aceddb
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 SubscriptionController.getInstance() 401 .requestEmbeddedSubscriptionInfoListRefresh(); 402 } 403 break; 404 case EuiccService.RESULT_MUST_DEACTIVATE_SIM: 405 resultCode = RESOLVABLE_ERROR; 406 addResolutionIntent(extrasIntent, 407 EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM, 408 callingPackage, 409 EuiccOperation.forDownloadDeactivateSim( 410 callingToken, subscription, switchAfterDownload, 411 callingPackage)); 412 break; 413 default: 414 resultCode = ERROR; 415 extrasIntent.putExtra( 416 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 417 result); 418 break; 419 } 420 421 sendResult(callbackIntent, resultCode, extrasIntent); 422 } 423 424 @Override 425 public void onEuiccServiceUnavailable() { 426 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 427 } 428 }); 429 } 430 431 /** 432 * Blocking call to {@link EuiccService#onGetEuiccProfileInfoList}. 433 * 434 * <p>Does not perform permission checks as this is not an exposed API and is only used within 435 * the phone process. 436 */ 437 public GetEuiccProfileInfoListResult blockingGetEuiccProfileInfoList() { 438 final CountDownLatch latch = new CountDownLatch(1); 439 final AtomicReference<GetEuiccProfileInfoListResult> resultRef = new AtomicReference<>(); 440 mConnector.getEuiccProfileInfoList( 441 new EuiccConnector.GetEuiccProfileInfoListCommandCallback() { 442 @Override 443 public void onListComplete(GetEuiccProfileInfoListResult result) { 444 resultRef.set(result); 445 latch.countDown(); 446 } 447 448 @Override 449 public void onEuiccServiceUnavailable() { 450 latch.countDown(); 451 } 452 }); 453 try { 454 latch.await(); 455 } catch (InterruptedException e) { 456 Thread.currentThread().interrupt(); 457 } 458 return resultRef.get(); 459 } 460 461 @Override 462 public void getDefaultDownloadableSubscriptionList( 463 String callingPackage, PendingIntent callbackIntent) { 464 getDefaultDownloadableSubscriptionList( 465 false /* forceDeactivateSim */, callingPackage, callbackIntent); 466 } 467 468 void getDefaultDownloadableSubscriptionList( 469 boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) { 470 if (!callerCanWriteEmbeddedSubscriptions()) { 471 throw new SecurityException( 472 "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get default list"); 473 } 474 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 475 long token = Binder.clearCallingIdentity(); 476 try { 477 mConnector.getDefaultDownloadableSubscriptionList( 478 forceDeactivateSim, new GetDefaultListCommandCallback( 479 token, callingPackage, callbackIntent)); 480 } finally { 481 Binder.restoreCallingIdentity(token); 482 } 483 } 484 485 class GetDefaultListCommandCallback implements EuiccConnector.GetDefaultListCommandCallback { 486 final long mCallingToken; 487 final String mCallingPackage; 488 final PendingIntent mCallbackIntent; 489 490 GetDefaultListCommandCallback(long callingToken, String callingPackage, 491 PendingIntent callbackIntent) { 492 mCallingToken = callingToken; 493 mCallingPackage = callingPackage; 494 mCallbackIntent = callbackIntent; 495 } 496 497 @Override 498 public void onGetDefaultListComplete(GetDefaultDownloadableSubscriptionListResult result) { 499 Intent extrasIntent = new Intent(); 500 final int resultCode; 501 switch (result.result) { 502 case EuiccService.RESULT_OK: 503 resultCode = OK; 504 extrasIntent.putExtra( 505 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS, 506 result.subscriptions); 507 break; 508 case EuiccService.RESULT_MUST_DEACTIVATE_SIM: 509 resultCode = RESOLVABLE_ERROR; 510 addResolutionIntent(extrasIntent, 511 EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM, 512 mCallingPackage, 513 EuiccOperation.forGetDefaultListDeactivateSim( 514 mCallingToken, mCallingPackage)); 515 break; 516 default: 517 resultCode = ERROR; 518 extrasIntent.putExtra( 519 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 520 result.result); 521 break; 522 } 523 524 sendResult(mCallbackIntent, resultCode, extrasIntent); 525 } 526 527 @Override 528 public void onEuiccServiceUnavailable() { 529 sendResult(mCallbackIntent, ERROR, null /* extrasIntent */); 530 } 531 } 532 533 /** 534 * Return the {@link EuiccInfo}. 535 * 536 * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load, 537 * that IPC should generally be fast, and this info shouldn't be needed in the normal course of 538 * operation. 539 */ 540 @Override 541 public EuiccInfo getEuiccInfo() { 542 // No permissions required as EuiccInfo is not sensitive. 543 long token = Binder.clearCallingIdentity(); 544 try { 545 return blockingGetEuiccInfoFromEuiccService(); 546 } finally { 547 Binder.restoreCallingIdentity(token); 548 } 549 } 550 551 @Override 552 public void deleteSubscription(int subscriptionId, String callingPackage, 553 PendingIntent callbackIntent) { 554 boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions(); 555 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 556 557 long token = Binder.clearCallingIdentity(); 558 try { 559 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 560 if (sub == null) { 561 Log.e(TAG, "Cannot delete nonexistent subscription: " + subscriptionId); 562 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 563 return; 564 } 565 566 if (!callerCanWriteEmbeddedSubscriptions 567 && !sub.canManageSubscription(mContext, callingPackage)) { 568 Log.e(TAG, "No permissions: " + subscriptionId); 569 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 570 return; 571 } 572 573 deleteSubscriptionPrivileged(sub.getIccId(), callbackIntent); 574 } finally { 575 Binder.restoreCallingIdentity(token); 576 } 577 } 578 579 void deleteSubscriptionPrivileged(String iccid, final PendingIntent callbackIntent) { 580 mConnector.deleteSubscription( 581 iccid, 582 new EuiccConnector.DeleteCommandCallback() { 583 @Override 584 public void onDeleteComplete(int result) { 585 Intent extrasIntent = new Intent(); 586 final int resultCode; 587 switch (result) { 588 case EuiccService.RESULT_OK: 589 resultCode = OK; 590 SubscriptionController.getInstance() 591 .requestEmbeddedSubscriptionInfoListRefresh(); 592 break; 593 default: 594 resultCode = ERROR; 595 extrasIntent.putExtra( 596 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 597 result); 598 break; 599 } 600 601 sendResult(callbackIntent, resultCode, extrasIntent); 602 } 603 604 @Override 605 public void onEuiccServiceUnavailable() { 606 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 607 } 608 }); 609 } 610 611 @Override 612 public void switchToSubscription(int subscriptionId, String callingPackage, 613 PendingIntent callbackIntent) { 614 switchToSubscription( 615 subscriptionId, false /* forceDeactivateSim */, callingPackage, callbackIntent); 616 } 617 618 void switchToSubscription(int subscriptionId, boolean forceDeactivateSim, String callingPackage, 619 PendingIntent callbackIntent) { 620 boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions(); 621 mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); 622 623 long token = Binder.clearCallingIdentity(); 624 try { 625 final String iccid; 626 if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 627 // Switch to "no" subscription. Only the system can do this. 628 if (!callerCanWriteEmbeddedSubscriptions) { 629 Log.e(TAG, "Not permitted to switch to empty subscription"); 630 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 631 return; 632 } 633 iccid = null; 634 } else { 635 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 636 if (sub == null) { 637 Log.e(TAG, "Cannot switch to nonexistent subscription: " + subscriptionId); 638 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 639 return; 640 } 641 if (!callerCanWriteEmbeddedSubscriptions 642 && !sub.canManageSubscription(mContext, callingPackage)) { 643 Log.e(TAG, "Not permitted to switch to subscription: " + subscriptionId); 644 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 645 return; 646 } 647 iccid = sub.getIccId(); 648 } 649 650 if (!callerCanWriteEmbeddedSubscriptions 651 && !canManageActiveSubscription(callingPackage)) { 652 // Switch needs consent. 653 Intent extrasIntent = new Intent(); 654 addResolutionIntent(extrasIntent, 655 EuiccService.ACTION_RESOLVE_NO_PRIVILEGES, 656 callingPackage, 657 EuiccOperation.forSwitchNoPrivileges( 658 token, subscriptionId, callingPackage)); 659 sendResult(callbackIntent, RESOLVABLE_ERROR, extrasIntent); 660 return; 661 } 662 663 switchToSubscriptionPrivileged(token, subscriptionId, iccid, forceDeactivateSim, 664 callingPackage, callbackIntent); 665 } finally { 666 Binder.restoreCallingIdentity(token); 667 } 668 } 669 670 void switchToSubscriptionPrivileged(final long callingToken, int subscriptionId, 671 boolean forceDeactivateSim, final String callingPackage, 672 final PendingIntent callbackIntent) { 673 String iccid = null; 674 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 675 if (sub != null) { 676 iccid = sub.getIccId(); 677 } 678 switchToSubscriptionPrivileged(callingToken, subscriptionId, iccid, forceDeactivateSim, 679 callingPackage, callbackIntent); 680 } 681 682 void switchToSubscriptionPrivileged(final long callingToken, int subscriptionId, 683 @Nullable String iccid, boolean forceDeactivateSim, final String callingPackage, 684 final PendingIntent callbackIntent) { 685 mConnector.switchToSubscription( 686 iccid, 687 forceDeactivateSim, 688 new EuiccConnector.SwitchCommandCallback() { 689 @Override 690 public void onSwitchComplete(int result) { 691 Intent extrasIntent = new Intent(); 692 final int resultCode; 693 switch (result) { 694 case EuiccService.RESULT_OK: 695 resultCode = OK; 696 break; 697 case EuiccService.RESULT_MUST_DEACTIVATE_SIM: 698 resultCode = RESOLVABLE_ERROR; 699 addResolutionIntent(extrasIntent, 700 EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM, 701 callingPackage, 702 EuiccOperation.forSwitchDeactivateSim( 703 callingToken, subscriptionId, callingPackage)); 704 break; 705 default: 706 resultCode = ERROR; 707 extrasIntent.putExtra( 708 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 709 result); 710 break; 711 } 712 713 sendResult(callbackIntent, resultCode, extrasIntent); 714 } 715 716 @Override 717 public void onEuiccServiceUnavailable() { 718 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 719 } 720 }); 721 } 722 723 @Override 724 public void updateSubscriptionNickname(int subscriptionId, String nickname, 725 PendingIntent callbackIntent) { 726 if (!callerCanWriteEmbeddedSubscriptions()) { 727 throw new SecurityException( 728 "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to update nickname"); 729 } 730 long token = Binder.clearCallingIdentity(); 731 try { 732 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId); 733 if (sub == null) { 734 Log.e(TAG, "Cannot update nickname to nonexistent subscription: " + subscriptionId); 735 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 736 return; 737 } 738 mConnector.updateSubscriptionNickname( 739 sub.getIccId(), nickname, 740 new EuiccConnector.UpdateNicknameCommandCallback() { 741 @Override 742 public void onUpdateNicknameComplete(int result) { 743 Intent extrasIntent = new Intent(); 744 final int resultCode; 745 switch (result) { 746 case EuiccService.RESULT_OK: 747 resultCode = OK; 748 break; 749 default: 750 resultCode = ERROR; 751 extrasIntent.putExtra( 752 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 753 result); 754 break; 755 } 756 757 sendResult(callbackIntent, resultCode, extrasIntent); 758 } 759 760 @Override 761 public void onEuiccServiceUnavailable() { 762 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 763 } 764 }); 765 } finally { 766 Binder.restoreCallingIdentity(token); 767 } 768 } 769 770 @Override 771 public void eraseSubscriptions(PendingIntent callbackIntent) { 772 if (!callerCanWriteEmbeddedSubscriptions()) { 773 throw new SecurityException( 774 "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to erase subscriptions"); 775 } 776 long token = Binder.clearCallingIdentity(); 777 try { 778 mConnector.eraseSubscriptions(new EuiccConnector.EraseCommandCallback() { 779 @Override 780 public void onEraseComplete(int result) { 781 Intent extrasIntent = new Intent(); 782 final int resultCode; 783 switch (result) { 784 case EuiccService.RESULT_OK: 785 resultCode = OK; 786 SubscriptionController.getInstance() 787 .requestEmbeddedSubscriptionInfoListRefresh(); 788 break; 789 default: 790 resultCode = ERROR; 791 extrasIntent.putExtra( 792 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 793 result); 794 break; 795 } 796 797 sendResult(callbackIntent, resultCode, extrasIntent); 798 } 799 800 @Override 801 public void onEuiccServiceUnavailable() { 802 sendResult(callbackIntent, ERROR, null /* extrasIntent */); 803 } 804 }); 805 } finally { 806 Binder.restoreCallingIdentity(token); 807 } 808 } 809 810 811 /** Dispatch the given callback intent with the given result code and data. */ 812 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 813 public void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { 814 try { 815 callbackIntent.send(mContext, resultCode, extrasIntent); 816 } catch (PendingIntent.CanceledException e) { 817 // Caller canceled the callback; do nothing. 818 } 819 } 820 821 /** Add a resolution intent to the given extras intent. */ 822 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 823 public void addResolutionIntent(Intent extrasIntent, String resolutionAction, 824 String callingPackage, EuiccOperation op) { 825 Intent intent = new Intent(EuiccManager.ACTION_RESOLVE_ERROR); 826 intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION, 827 resolutionAction); 828 intent.putExtra(EuiccService.EXTRA_RESOLUTION_CALLING_PACKAGE, callingPackage); 829 intent.putExtra(EXTRA_OPERATION, op); 830 PendingIntent resolutionIntent = PendingIntent.getActivity( 831 mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_ONE_SHOT); 832 extrasIntent.putExtra( 833 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT, resolutionIntent); 834 } 835 836 @Override 837 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 838 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP"); 839 final long token = Binder.clearCallingIdentity(); 840 try { 841 mConnector.dump(fd, pw, args); 842 } finally { 843 Binder.restoreCallingIdentity(token); 844 } 845 } 846 847 @Nullable 848 private SubscriptionInfo getSubscriptionForSubscriptionId(int subscriptionId) { 849 List<SubscriptionInfo> subs = mSubscriptionManager.getAvailableSubscriptionInfoList(); 850 int subCount = subs.size(); 851 for (int i = 0; i < subCount; i++) { 852 SubscriptionInfo sub = subs.get(i); 853 if (subscriptionId == sub.getSubscriptionId()) { 854 return sub; 855 } 856 } 857 return null; 858 } 859 860 @Nullable 861 private String blockingGetEidFromEuiccService() { 862 CountDownLatch latch = new CountDownLatch(1); 863 AtomicReference<String> eidRef = new AtomicReference<>(); 864 mConnector.getEid(new EuiccConnector.GetEidCommandCallback() { 865 @Override 866 public void onGetEidComplete(String eid) { 867 eidRef.set(eid); 868 latch.countDown(); 869 } 870 871 @Override 872 public void onEuiccServiceUnavailable() { 873 latch.countDown(); 874 } 875 }); 876 return awaitResult(latch, eidRef); 877 } 878 879 @Nullable 880 private EuiccInfo blockingGetEuiccInfoFromEuiccService() { 881 CountDownLatch latch = new CountDownLatch(1); 882 AtomicReference<EuiccInfo> euiccInfoRef = new AtomicReference<>(); 883 mConnector.getEuiccInfo(new EuiccConnector.GetEuiccInfoCommandCallback() { 884 @Override 885 public void onGetEuiccInfoComplete(EuiccInfo euiccInfo) { 886 euiccInfoRef.set(euiccInfo); 887 latch.countDown(); 888 } 889 890 @Override 891 public void onEuiccServiceUnavailable() { 892 latch.countDown(); 893 } 894 }); 895 return awaitResult(latch, euiccInfoRef); 896 } 897 898 private static <T> T awaitResult(CountDownLatch latch, AtomicReference<T> resultRef) { 899 try { 900 latch.await(); 901 } catch (InterruptedException e) { 902 Thread.currentThread().interrupt(); 903 } 904 return resultRef.get(); 905 } 906 907 private boolean canManageActiveSubscription(String callingPackage) { 908 // TODO(b/36260308): We should plumb a slot ID through here for multi-SIM devices. 909 List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); 910 if (subInfoList == null) { 911 return false; 912 } 913 int size = subInfoList.size(); 914 for (int subIndex = 0; subIndex < size; subIndex++) { 915 SubscriptionInfo subInfo = subInfoList.get(subIndex); 916 if (subInfo.isEmbedded() && subInfo.canManageSubscription(mContext, callingPackage)) { 917 return true; 918 } 919 } 920 return false; 921 } 922 923 private boolean callerCanReadPhoneStatePrivileged() { 924 return mContext.checkCallingPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) 925 == PackageManager.PERMISSION_GRANTED; 926 } 927 928 private boolean callerCanWriteEmbeddedSubscriptions() { 929 return mContext.checkCallingPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) 930 == PackageManager.PERMISSION_GRANTED; 931 } 932 933 /** 934 * Returns whether the caller has carrier privileges for the active mSubscription on this eUICC. 935 */ 936 private boolean callerHasCarrierPrivilegesForActiveSubscription() { 937 // TODO(b/36260308): We should plumb a slot ID through here for multi-SIM devices. 938 TelephonyManager tm = 939 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 940 return tm.hasCarrierPrivileges(); 941 } 942} 943