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