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