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