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