• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Manifest.permission;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.AppOpsManager;
23 import android.app.PendingIntent;
24 import android.app.compat.CompatChanges;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.ComponentInfo;
29 import android.content.pm.PackageInfo;
30 import android.content.pm.PackageManager;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.provider.Settings;
34 import android.service.euicc.DownloadSubscriptionResult;
35 import android.service.euicc.EuiccService;
36 import android.service.euicc.GetDefaultDownloadableSubscriptionListResult;
37 import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
38 import android.service.euicc.GetEuiccProfileInfoListResult;
39 import android.telephony.AnomalyReporter;
40 import android.telephony.SubscriptionInfo;
41 import android.telephony.SubscriptionManager;
42 import android.telephony.TelephonyFrameworkInitializer;
43 import android.telephony.TelephonyManager;
44 import android.telephony.UiccAccessRule;
45 import android.telephony.UiccCardInfo;
46 import android.telephony.UiccPortInfo;
47 import android.telephony.UiccSlotInfo;
48 import android.telephony.euicc.DownloadableSubscription;
49 import android.telephony.euicc.EuiccCardManager.ResetOption;
50 import android.telephony.euicc.EuiccInfo;
51 import android.telephony.euicc.EuiccManager;
52 import android.telephony.euicc.EuiccManager.OtaStatus;
53 import android.text.TextUtils;
54 import android.util.EventLog;
55 import android.util.Log;
56 import android.util.Pair;
57 
58 import com.android.internal.annotations.VisibleForTesting;
59 import com.android.internal.telephony.CarrierPrivilegesTracker;
60 import com.android.internal.telephony.Phone;
61 import com.android.internal.telephony.PhoneFactory;
62 import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;
63 import com.android.internal.telephony.subscription.SubscriptionManagerService;
64 import com.android.internal.telephony.uicc.IccUtils;
65 import com.android.internal.telephony.uicc.UiccController;
66 import com.android.internal.telephony.uicc.UiccPort;
67 import com.android.internal.telephony.uicc.UiccSlot;
68 
69 import java.io.FileDescriptor;
70 import java.io.PrintWriter;
71 import java.util.Collections;
72 import java.util.List;
73 import java.util.Stack;
74 import java.util.UUID;
75 import java.util.concurrent.CountDownLatch;
76 import java.util.concurrent.TimeUnit;
77 import java.util.concurrent.atomic.AtomicReference;
78 
79 /** Backing implementation of {@link android.telephony.euicc.EuiccManager}. */
80 public class EuiccController extends IEuiccController.Stub {
81     private static final String TAG = "EuiccController";
82 
83     /** Extra set on resolution intents containing the {@link EuiccOperation}. */
84     @VisibleForTesting
85     static final String EXTRA_OPERATION = "operation";
86 
87     /**
88      * Time out for {@link #dump(FileDescriptor, PrintWriter, String[])}
89      */
90     private static final int EUICC_DUMP_TIME_OUT_SECONDS = 5;
91 
92     // Aliases so line lengths stay short.
93     private static final int OK = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK;
94     private static final int RESOLVABLE_ERROR =
95             EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR;
96     private static final int ERROR =
97             EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR;
98     private static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
99             EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION;
100 
101     /** Restrictions limiting access to the PendingIntent */
102     private static final String RESOLUTION_ACTIVITY_PACKAGE_NAME = "com.android.phone";
103     private static final String RESOLUTION_ACTIVITY_CLASS_NAME =
104             "com.android.phone.euicc.EuiccResolutionUiDispatcherActivity";
105 
106     private static EuiccController sInstance;
107 
108     private final Context mContext;
109     private final EuiccConnector mConnector;
110     private final SubscriptionManager mSubscriptionManager;
111     private final TelephonyManager mTelephonyManager;
112     private final AppOpsManager mAppOpsManager;
113     private final PackageManager mPackageManager;
114 
115     // These values should be set or updated upon 1) system boot, 2) EuiccService/LPA is bound to
116     // the phone process, 3) values are updated remotely by server flags.
117     private List<String> mSupportedCountries;
118     private List<String> mUnsupportedCountries;
119 
120     /** Initialize the instance. Should only be called once. */
init(Context context)121     public static EuiccController init(Context context) {
122         synchronized (EuiccController.class) {
123             if (sInstance == null) {
124                 sInstance = new EuiccController(context);
125             } else {
126                 Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
127             }
128         }
129         return sInstance;
130     }
131 
132     /** Get an instance. Assumes one has already been initialized with {@link #init}. */
get()133     public static EuiccController get() {
134         if (sInstance == null) {
135             synchronized (EuiccController.class) {
136                 if (sInstance == null) {
137                     throw new IllegalStateException("get() called before init()");
138                 }
139             }
140         }
141         return sInstance;
142     }
143 
EuiccController(Context context)144     private EuiccController(Context context) {
145         this(context, new EuiccConnector(context));
146         TelephonyFrameworkInitializer
147                 .getTelephonyServiceManager().getEuiccControllerService().register(this);
148     }
149 
150     @VisibleForTesting
EuiccController(Context context, EuiccConnector connector)151     public EuiccController(Context context, EuiccConnector connector) {
152         mContext = context;
153         mConnector = connector;
154         mSubscriptionManager = (SubscriptionManager)
155                 context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
156         mTelephonyManager = (TelephonyManager)
157                 context.getSystemService(Context.TELEPHONY_SERVICE);
158         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
159         mPackageManager = context.getPackageManager();
160     }
161 
162     /**
163      * Continue an operation which failed with a user-resolvable error.
164      *
165      * <p>The implementation here makes a key assumption that the resolutionIntent has not been
166      * tampered with. This is guaranteed because:
167      * <UL>
168      * <LI>The intent is wrapped in a PendingIntent created by the phone process which is created
169      * with {@link #EXTRA_OPERATION} already present. This means that the operation cannot be
170      * overridden on the PendingIntent - a caller can only add new extras.
171      * <LI>The resolution activity is restricted by a privileged permission; unprivileged apps
172      * cannot start it directly. So the PendingIntent is the only way to start it.
173      * </UL>
174      */
175     @Override
continueOperation(int cardId, Intent resolutionIntent, Bundle resolutionExtras)176     public void continueOperation(int cardId, Intent resolutionIntent, Bundle resolutionExtras) {
177         if (!callerCanWriteEmbeddedSubscriptions()) {
178             throw new SecurityException(
179                     "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to continue operation");
180         }
181         long token = Binder.clearCallingIdentity();
182         try {
183             EuiccOperation op = resolutionIntent.getParcelableExtra(EXTRA_OPERATION);
184             if (op == null) {
185                 throw new IllegalArgumentException("Invalid resolution intent");
186             }
187 
188             PendingIntent callbackIntent =
189                     resolutionIntent.getParcelableExtra(
190                             EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
191             boolean usePortIndex = resolutionIntent.getBooleanExtra(
192                     EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, false);
193             resolutionExtras.putBoolean(EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, usePortIndex);
194 
195             if (!EuiccService.ACTION_RESOLVE_NO_PRIVILEGES.equals(resolutionIntent.getAction())
196                     || !resolutionExtras.containsKey(EuiccService.EXTRA_RESOLUTION_PORT_INDEX)) {
197                 // Port index resolution is requested only through the ACTION_RESOLVE_NO_PRIVILEGES
198                 // action. Therefore, if the action is not ACTION_RESOLVE_NO_PRIVILEGES, use the
199                 // port index from the resolution intent.
200                 // (OR) If the action is ACTION_RESOLVE_NO_PRIVILEGES and resolutionExtras does not
201                 // contain the EXTRA_RESOLUTION_PORT_INDEX key, retrieve the port index from
202                 // resolutionIntent.
203                 resolutionExtras.putInt(EuiccService.EXTRA_RESOLUTION_PORT_INDEX,
204                         resolutionIntent.getIntExtra(EuiccService.EXTRA_RESOLUTION_PORT_INDEX,
205                                 TelephonyManager.DEFAULT_PORT_INDEX));
206             }
207             Log.i(TAG, " continueOperation portIndex: " + resolutionExtras.getInt(
208                     EuiccService.EXTRA_RESOLUTION_PORT_INDEX) + " usePortIndex: " + usePortIndex);
209             op.continueOperation(cardId, resolutionExtras, callbackIntent);
210         } finally {
211             Binder.restoreCallingIdentity(token);
212         }
213     }
214 
215     /**
216      * Return the EID.
217      *
218      * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load,
219      * that IPC should generally be fast, and the EID shouldn't be needed in the normal course of
220      * operation.
221      */
222     @Override
getEid(int cardId, String callingPackage)223     public String getEid(int cardId, String callingPackage) {
224         boolean callerCanReadPhoneStatePrivileged = callerCanReadPhoneStatePrivileged();
225         try {
226             mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
227         } catch (SecurityException e) {
228             EventLog.writeEvent(0x534e4554, "159062405", -1, "Missing UID checking");
229             throw e;
230         }
231         long token = Binder.clearCallingIdentity();
232         try {
233             if (!callerCanReadPhoneStatePrivileged
234                     && !canManageSubscriptionOnTargetSim(cardId, callingPackage, false,
235                     TelephonyManager.INVALID_PORT_INDEX)) {
236                 throw new SecurityException(
237                         "Must have carrier privileges on subscription to read EID for cardId="
238                                 + cardId);
239             }
240 
241             return blockingGetEidFromEuiccService(cardId);
242         } finally {
243             Binder.restoreCallingIdentity(token);
244         }
245     }
246 
247     /**
248      * Return the current status of OTA update.
249      *
250      * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load,
251      * that IPC should generally be fast.
252      */
253     @Override
getOtaStatus(int cardId)254     public @OtaStatus int getOtaStatus(int cardId) {
255         if (!callerCanWriteEmbeddedSubscriptions()) {
256             throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get OTA status");
257         }
258         long token = Binder.clearCallingIdentity();
259         try {
260             return blockingGetOtaStatusFromEuiccService(cardId);
261         } finally {
262             Binder.restoreCallingIdentity(token);
263         }
264     }
265 
266     /**
267      * Start eUICC OTA update on the default eUICC if current eUICC OS is not the latest one. When
268      * OTA is started or finished, the broadcast {@link EuiccManager#ACTION_OTA_STATUS_CHANGED} will
269      * be sent.
270      *
271      * This function will only be called from phone process and isn't exposed to the other apps.
272      *
273      * (see {@link #startOtaUpdatingIfNecessary(int cardId)}).
274      */
startOtaUpdatingIfNecessary()275     public void startOtaUpdatingIfNecessary() {
276         // TODO(b/120796772) Eventually, we should use startOtaUpdatingIfNecessary(cardId)
277         startOtaUpdatingIfNecessary(mTelephonyManager.getCardIdForDefaultEuicc());
278     }
279 
280     /**
281      * Start eUICC OTA update on the given eUICC if current eUICC OS is not the latest one.
282      */
startOtaUpdatingIfNecessary(int cardId)283     public void startOtaUpdatingIfNecessary(int cardId) {
284         mConnector.startOtaIfNecessary(cardId,
285                 new OtaStatusChangedCallback() {
286                     @Override
287                     public void onOtaStatusChanged(int status) {
288                         sendOtaStatusChangedBroadcast();
289                     }
290 
291                     @Override
292                     public void onEuiccServiceUnavailable() {}
293                 });
294     }
295 
296     @Override
getDownloadableSubscriptionMetadata(int cardId, DownloadableSubscription subscription, String callingPackage, PendingIntent callbackIntent)297     public void getDownloadableSubscriptionMetadata(int cardId,
298             DownloadableSubscription subscription, String callingPackage,
299             PendingIntent callbackIntent) {
300         getDownloadableSubscriptionMetadata(cardId,
301                 subscription, false /* forceDeactivateSim */, callingPackage, callbackIntent);
302     }
303 
304     /**
305      * Sets the supported or unsupported countries for eUICC.
306      *
307      * <p>If {@code isSupported} is true, the supported country list will be replaced by
308      * {@code countriesList}. Otherwise, unsupported country list will be replaced by
309      * {@code countriesList}. For how we determine whether a country is supported by checking
310      * supported and unsupported country list please check {@link EuiccManager#isSupportedCountry}.
311      *
312      * @param isSupported should be true if caller wants to set supported country list. If
313      * isSupported is false, un-supported country list will be updated.
314      * @param countriesList is a list of strings contains country ISO codes in uppercase.
315      */
316     @Override
setSupportedCountries(boolean isSupported, @NonNull List<String> countriesList)317     public void setSupportedCountries(boolean isSupported, @NonNull List<String> countriesList) {
318         if (!callerCanWriteEmbeddedSubscriptions()) {
319             throw new SecurityException(
320                     "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to set supported countries");
321         }
322         if (isSupported) {
323             mSupportedCountries = countriesList;
324         } else {
325             mUnsupportedCountries = countriesList;
326         }
327     }
328 
329     /**
330      * Gets the supported or unsupported countries for eUICC.
331      *
332      * <p>If {@code isSupported} is true, the supported country list will be returned. Otherwise,
333      * unsupported country list will be returned.
334      *
335      * @param isSupported should be true if caller wants to get supported country list. If
336      * isSupported is false, unsupported country list will be returned.
337      * @return a list of strings contains country ISO codes in uppercase.
338      */
339     @Override
340     @NonNull
getSupportedCountries(boolean isSupported)341     public List<String> getSupportedCountries(boolean isSupported) {
342         if (!callerCanWriteEmbeddedSubscriptions()) {
343             throw new SecurityException(
344                     "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get supported countries");
345         }
346         if (isSupported && mSupportedCountries != null) {
347             return mSupportedCountries;
348         } else if (!isSupported && mUnsupportedCountries != null) {
349             return mUnsupportedCountries;
350         }
351         return Collections.emptyList();
352     }
353 
354     /**
355      * Returns whether the given country supports eUICC.
356      *
357      * <p>Supported country list has a higher prority than unsupported country list. If the
358      * supported country list is not empty, {@code countryIso} will be considered as supported when
359      * it exists in the supported country list. Otherwise {@code countryIso} is not supported. If
360      * the supported country list is empty, {@code countryIso} will be considered as supported if it
361      * does not exist in the unsupported country list. Otherwise {@code countryIso} is not
362      * supported. If both supported and unsupported country lists are empty, then all countries are
363      * consider be supported. For how to set supported and unsupported country list, please check
364      * {@link #setSupportedCountries}.
365      *
366      * @param countryIso should be the ISO-3166 country code is provided in uppercase 2 character
367      * format.
368      * @return whether the given country supports eUICC or not.
369      */
370     @Override
isSupportedCountry(@onNull String countryIso)371     public boolean isSupportedCountry(@NonNull String countryIso) {
372         if (!callerCanWriteEmbeddedSubscriptions()) {
373             throw new SecurityException(
374                     "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to check if the country is supported");
375         }
376         if (mSupportedCountries == null || mSupportedCountries.isEmpty()) {
377             Log.i(TAG, "Using deny list unsupportedCountries=" + mUnsupportedCountries);
378             return !isEsimUnsupportedCountry(countryIso);
379         } else {
380             Log.i(TAG, "Using allow list supportedCountries=" + mSupportedCountries);
381             return isEsimSupportedCountry(countryIso);
382         }
383     }
384 
isEsimSupportedCountry(String countryIso)385     private boolean isEsimSupportedCountry(String countryIso) {
386         if (mSupportedCountries == null || TextUtils.isEmpty(countryIso)) {
387             return true;
388         }
389         return mSupportedCountries.contains(countryIso);
390     }
391 
isEsimUnsupportedCountry(String countryIso)392     private boolean isEsimUnsupportedCountry(String countryIso) {
393         if (mUnsupportedCountries == null || TextUtils.isEmpty(countryIso)) {
394             return false;
395         }
396         return mUnsupportedCountries.contains(countryIso);
397     }
398 
getDownloadableSubscriptionMetadata(int cardId, DownloadableSubscription subscription, boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent)399     void getDownloadableSubscriptionMetadata(int cardId, DownloadableSubscription subscription,
400             boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) {
401         Log.d(TAG, " getDownloadableSubscriptionMetadata callingPackage: " + callingPackage);
402         if (!callerCanWriteEmbeddedSubscriptions()) {
403             throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get metadata");
404         }
405         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
406         long token = Binder.clearCallingIdentity();
407         try {
408             mConnector.getDownloadableSubscriptionMetadata(cardId,
409                     TelephonyManager.DEFAULT_PORT_INDEX, subscription,
410                     false /* switchAfterDownload */, forceDeactivateSim,
411                     new GetMetadataCommandCallback(
412                             token, subscription, callingPackage, callbackIntent));
413         } finally {
414             Binder.restoreCallingIdentity(token);
415         }
416     }
417 
418     class GetMetadataCommandCallback implements EuiccConnector.GetMetadataCommandCallback {
419         protected final long mCallingToken;
420         protected final DownloadableSubscription mSubscription;
421         protected final String mCallingPackage;
422         protected final PendingIntent mCallbackIntent;
423 
GetMetadataCommandCallback( long callingToken, DownloadableSubscription subscription, String callingPackage, PendingIntent callbackIntent)424         GetMetadataCommandCallback(
425                 long callingToken,
426                 DownloadableSubscription subscription,
427                 String callingPackage,
428                 PendingIntent callbackIntent) {
429             mCallingToken = callingToken;
430             mSubscription = subscription;
431             mCallingPackage = callingPackage;
432             mCallbackIntent = callbackIntent;
433         }
434 
435         @Override
onGetMetadataComplete(int cardId, GetDownloadableSubscriptionMetadataResult result)436         public void onGetMetadataComplete(int cardId,
437                 GetDownloadableSubscriptionMetadataResult result) {
438             Intent extrasIntent = new Intent();
439             final int resultCode;
440             switch (result.getResult()) {
441                 case EuiccService.RESULT_OK:
442                     resultCode = OK;
443                     extrasIntent.putExtra(
444                             EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
445                             result.getDownloadableSubscription());
446                     break;
447                 case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
448                     resultCode = RESOLVABLE_ERROR;
449                     addResolutionIntentWithPort(extrasIntent,
450                             EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
451                             mCallingPackage,
452                             0 /* resolvableErrors */,
453                             false /* confirmationCodeRetried */,
454                             getOperationForDeactivateSim(),
455                             cardId,
456                             TelephonyManager.DEFAULT_PORT_INDEX,   false /* usePortIndex */);
457                     break;
458                 default:
459                     resultCode = ERROR;
460                     addExtrasToResultIntent(extrasIntent, result.getResult());
461                     break;
462             }
463 
464             sendResult(mCallbackIntent, resultCode, extrasIntent);
465         }
466 
467         @Override
onEuiccServiceUnavailable()468         public void onEuiccServiceUnavailable() {
469             sendResult(mCallbackIntent, ERROR, null /* extrasIntent */);
470         }
471 
getOperationForDeactivateSim()472         protected EuiccOperation getOperationForDeactivateSim() {
473             return EuiccOperation.forGetMetadataDeactivateSim(
474                     mCallingToken, mSubscription, mCallingPackage);
475         }
476     }
477 
478     @Override
downloadSubscription(int cardId, DownloadableSubscription subscription, boolean switchAfterDownload, String callingPackage, Bundle resolvedBundle, PendingIntent callbackIntent)479     public void downloadSubscription(int cardId, DownloadableSubscription subscription,
480             boolean switchAfterDownload, String callingPackage, Bundle resolvedBundle,
481             PendingIntent callbackIntent) {
482         // If switchAfterDownload is true, set portIndex as
483         // {@link android.telephony.TelephonyManager#INVALID_PORT_INDEX} to resolve the port index.
484         int portIndex = switchAfterDownload ? TelephonyManager.INVALID_PORT_INDEX
485                 : TelephonyManager.DEFAULT_PORT_INDEX;
486         downloadSubscription(cardId, portIndex, subscription,
487                 switchAfterDownload, callingPackage, false /* forceDeactivateSim */,
488                 resolvedBundle, callbackIntent);
489     }
490 
491     /**
492      * Given encoded error code described in
493      * {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE} decode it
494      * into SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2)
495      *
496      * @param resultCode from
497      *               {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}
498      * @return a pair containing SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] from GSMA (SGP.22
499      * v2.2)
500      */
decodeSmdxSubjectAndReasonCode(int resultCode)501     Pair<String, String> decodeSmdxSubjectAndReasonCode(int resultCode) {
502         final int numOfSections = 6;
503         final int bitsPerSection = 4;
504         final int sectionMask = 0xF;
505 
506         final Stack<Integer> sections = new Stack<>();
507 
508         // Extracting each section of digits backwards.
509         for (int i = 0; i < numOfSections; ++i) {
510             int sectionDigit = resultCode & sectionMask;
511             sections.push(sectionDigit);
512             resultCode = resultCode >>> bitsPerSection;
513         }
514 
515         String subjectCode = sections.pop() + "." + sections.pop() + "." + sections.pop();
516         String reasonCode = sections.pop() + "." + sections.pop() + "." + sections.pop();
517 
518         // drop the leading zeros, e.g 0.1 -> 1, 0.0.3 -> 3, 0.5.1 -> 5.1
519         subjectCode = subjectCode.replaceAll("^(0\\.)*", "");
520         reasonCode = reasonCode.replaceAll("^(0\\.)*", "");
521 
522         return Pair.create(subjectCode, reasonCode);
523     }
524 
525     /**
526      * Add more detailed information to the resulting intent.
527      * Fields added includes(key -> value):
528      * 1. {@link EuiccManager#EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} -> original error code
529      * 2. {@link EuiccManager#EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE} ->
530      * EuiccManager.OperationCode such as {@link EuiccManager#OPERATION_DOWNLOAD}
531      * 3. if @link EuiccManager.OperationCode is not
532      * {@link EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}:
533      * {@link EuiccManager#EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE} -> @link
534      * EuiccManager.ErrorCode such as {@link EuiccManager#OPERATION_SMDX}
535      * 4. if EuiccManager.OperationCode is
536      * {@link EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}:
537      * a) {@link EuiccManager#EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE} ->
538      * SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2)
539      * b) {@link EuiccManager#EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE} ->
540      * ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2
541      */
addExtrasToResultIntent(Intent intent, int resultCode)542     private void addExtrasToResultIntent(Intent intent, int resultCode) {
543         final int firstByteBitOffset = 24;
544         int errorCodeMask = 0xFFFFFF;
545         int operationCode = resultCode >>> firstByteBitOffset;
546 
547         intent.putExtra(
548                 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, resultCode);
549 
550         intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE, operationCode);
551 
552         // check to see if the operation code is EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE
553         final boolean isSmdxSubjectReasonCode =
554                 (operationCode == EuiccManager.OPERATION_SMDX_SUBJECT_REASON_CODE);
555 
556         if (isSmdxSubjectReasonCode) {
557             final Pair<String, String> subjectReasonCode = decodeSmdxSubjectAndReasonCode(
558                     resultCode);
559             final String subjectCode = subjectReasonCode.first;
560             final String reasonCode = subjectReasonCode.second;
561             intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE,
562                     subjectCode);
563             intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE, reasonCode);
564         } else {
565             final int errorCode = resultCode & errorCodeMask;
566             intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE, errorCode);
567         }
568     }
569 
downloadSubscription(int cardId, int portIndex, DownloadableSubscription subscription, boolean switchAfterDownload, String callingPackage, boolean forceDeactivateSim, Bundle resolvedBundle, PendingIntent callbackIntent)570     void downloadSubscription(int cardId, int portIndex, DownloadableSubscription subscription,
571             boolean switchAfterDownload, String callingPackage, boolean forceDeactivateSim,
572             Bundle resolvedBundle, PendingIntent callbackIntent) {
573         boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
574         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
575         // Don't try to resolve the port index for apps which are not targeting on T for backward
576         // compatibility. instead always use default port 0.
577         boolean shouldResolvePortIndex = isCompatChangeEnabled(callingPackage,
578                 EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS);
579 
580         long token = Binder.clearCallingIdentity();
581         try {
582             boolean isConsentNeededToResolvePortIndex = false;
583             if (switchAfterDownload && portIndex == TelephonyManager.INVALID_PORT_INDEX) {
584                 // If switchAfterDownload is true, resolve the portIndex
585                 portIndex = shouldResolvePortIndex ?
586                         getResolvedPortIndexForSubscriptionSwitch(cardId)
587                         : TelephonyManager.DEFAULT_PORT_INDEX;
588                 isConsentNeededToResolvePortIndex = (portIndex
589                         == TelephonyManager.INVALID_PORT_INDEX);
590             }
591             Log.d(TAG, " downloadSubscription cardId: " + cardId + " switchAfterDownload: "
592                     + switchAfterDownload + " portIndex: " + portIndex
593                     + " forceDeactivateSim: " + forceDeactivateSim + " callingPackage: "
594                     + callingPackage
595                     + " isConsentNeededToResolvePortIndex: " + isConsentNeededToResolvePortIndex
596                     + " shouldResolvePortIndex:" + shouldResolvePortIndex);
597             if (!isConsentNeededToResolvePortIndex && callerCanWriteEmbeddedSubscriptions) {
598                 // With WRITE_EMBEDDED_SUBSCRIPTIONS, we can skip profile-specific permission checks
599                 // and move straight to the profile download.
600                 downloadSubscriptionPrivileged(cardId, portIndex, token, subscription,
601                         switchAfterDownload, forceDeactivateSim, callingPackage, resolvedBundle,
602                         callbackIntent);
603                 return;
604             }
605 
606             // Without WRITE_EMBEDDED_SUBSCRIPTIONS, we first check whether the caller can manage
607             // subscription on the target SIM (see comments below). If yes, the caller *must* be
608             // allowlisted per the metadata of the profile to be downloaded, so check the metadata;
609             // If no, ask the user's consent before proceed.
610             // On a multi-active SIM device, if the caller can manage the active subscription on the
611             // target SIM, or there is no active subscription on the target SIM and the caller can
612             // manage any active subscription on other SIMs, we perform the download silently.
613             // Otherwise, the user must provide consent. If it's a single-active SIM device,
614             // determine whether the caller can manage the current profile; if so, we can perform
615             // the download silently; if not, the user must provide consent.
616             if (!isConsentNeededToResolvePortIndex
617                     && canManageSubscriptionOnTargetSim(cardId, callingPackage, true,
618                     portIndex)) {
619                 mConnector.getDownloadableSubscriptionMetadata(cardId, portIndex,
620                         subscription, switchAfterDownload, forceDeactivateSim,
621                     new DownloadSubscriptionGetMetadataCommandCallback(token, subscription,
622                         switchAfterDownload, callingPackage, forceDeactivateSim,
623                         callbackIntent, false /* withUserConsent */, portIndex));
624             } else {
625                 Log.i(TAG, "Caller can't manage subscription on target SIM or "
626                         + " User consent is required for resolving port index. "
627                         + "Ask user's consent first");
628                 Intent extrasIntent = new Intent();
629                 addResolutionIntentWithPort(extrasIntent,
630                         EuiccService.ACTION_RESOLVE_NO_PRIVILEGES,
631                         callingPackage,
632                         0 /* resolvableErrors */,
633                         false /* confirmationCodeRetried */,
634                         EuiccOperation.forDownloadNoPrivilegesOrDeactivateSimCheckMetadata(token,
635                                 subscription, switchAfterDownload, callingPackage), cardId,
636                         portIndex, switchAfterDownload /* usePortIndex */);
637                 sendResult(callbackIntent, RESOLVABLE_ERROR, extrasIntent);
638             }
639         } finally {
640             Binder.restoreCallingIdentity(token);
641         }
642     }
643 
644     class DownloadSubscriptionGetMetadataCommandCallback extends GetMetadataCommandCallback {
645         private final boolean mSwitchAfterDownload;
646         private final boolean mForceDeactivateSim;
647         private final boolean mWithUserConsent;
648         private final int mPortIndex;
649 
DownloadSubscriptionGetMetadataCommandCallback(long callingToken, DownloadableSubscription subscription, boolean switchAfterDownload, String callingPackage, boolean forceDeactivateSim, PendingIntent callbackIntent, boolean withUserConsent, int portIndex)650         DownloadSubscriptionGetMetadataCommandCallback(long callingToken,
651                 DownloadableSubscription subscription, boolean switchAfterDownload,
652                 String callingPackage, boolean forceDeactivateSim,
653                 PendingIntent callbackIntent, boolean withUserConsent, int portIndex) {
654             super(callingToken, subscription, callingPackage, callbackIntent);
655             mSwitchAfterDownload = switchAfterDownload;
656             mForceDeactivateSim = forceDeactivateSim;
657             mWithUserConsent = withUserConsent;
658             mPortIndex = portIndex;
659         }
660 
661         @Override
onGetMetadataComplete(int cardId, GetDownloadableSubscriptionMetadataResult result)662         public void onGetMetadataComplete(int cardId,
663                 GetDownloadableSubscriptionMetadataResult result) {
664             DownloadableSubscription subscription = result.getDownloadableSubscription();
665             if (mWithUserConsent) {
666                 // We won't get RESULT_MUST_DEACTIVATE_SIM for the case with user consent.
667                 if (result.getResult() != EuiccService.RESULT_OK) {
668                     // Just propagate the error as normal.
669                     super.onGetMetadataComplete(cardId, result);
670                     return;
671                 }
672 
673                 if (checkCarrierPrivilegeInMetadata(subscription, mCallingPackage)) {
674                     // Caller can download this profile. Since we already have the user's consent,
675                     // proceed to download.
676                     downloadSubscriptionPrivileged(cardId, mPortIndex,
677                             mCallingToken, subscription, mSwitchAfterDownload,  mForceDeactivateSim,
678                             mCallingPackage, null /* resolvedBundle */,
679                             mCallbackIntent);
680                 } else {
681                     Log.e(TAG, "Caller does not have carrier privilege in metadata.");
682                     sendResult(mCallbackIntent, ERROR, null /* extrasIntent */);
683                 }
684             } else { // !mWithUserConsent
685                 if (result.getResult() == EuiccService.RESULT_MUST_DEACTIVATE_SIM) {
686                     // The caller can manage the target SIM. Ask the user's consent to deactivate
687                     // the current SIM.
688                     Intent extrasIntent = new Intent();
689                     addResolutionIntentWithPort(extrasIntent,
690                             EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
691                             mCallingPackage,
692                             0 /* resolvableErrors */,
693                             false /* confirmationCodeRetried */,
694                             EuiccOperation.forDownloadNoPrivilegesOrDeactivateSimCheckMetadata(
695                                     mCallingToken, mSubscription, mSwitchAfterDownload,
696                                     mCallingPackage),
697                             cardId,  mPortIndex,  mSwitchAfterDownload /* usePortIndex */);
698                     sendResult(mCallbackIntent, RESOLVABLE_ERROR, extrasIntent);
699                     return;
700                 }
701 
702                 if (result.getResult() != EuiccService.RESULT_OK) {
703                     // Just propagate the error as normal.
704                     super.onGetMetadataComplete(cardId, result);
705                     return;
706                 }
707 
708                 if (checkCarrierPrivilegeInMetadata(subscription, mCallingPackage)) {
709                     // Caller can download this profile per profile metadata. Also, caller can
710                     // manage the subscription on the target SIM, which is already checked.
711                     downloadSubscriptionPrivileged(cardId, mPortIndex,
712                             mCallingToken, subscription, mSwitchAfterDownload, mForceDeactivateSim,
713                             mCallingPackage, null /* resolvedBundle */,
714                             mCallbackIntent);
715                 } else {
716                     Log.e(TAG, "Caller is not permitted to download this profile per metadata");
717                     sendResult(mCallbackIntent, ERROR, null /* extrasIntent */);
718                 }
719             }
720         }
721     }
722 
723     // Already have user consent. Check metadata first before proceed to download.
downloadSubscriptionPrivilegedCheckMetadata(int cardId, int portIndex, final long callingToken, DownloadableSubscription subscription, boolean switchAfterDownload, boolean forceDeactivateSim, final String callingPackage, Bundle resolvedBundle, final PendingIntent callbackIntent)724     void downloadSubscriptionPrivilegedCheckMetadata(int cardId, int portIndex,
725             final long callingToken, DownloadableSubscription subscription,
726             boolean switchAfterDownload, boolean forceDeactivateSim, final String callingPackage,
727             Bundle resolvedBundle, final PendingIntent callbackIntent) {
728         Log.d(TAG, " downloadSubscriptionPrivilegedCheckMetadata cardId: " + cardId
729                 + " switchAfterDownload: " + switchAfterDownload + " portIndex: " + portIndex
730                 + " forceDeactivateSim: " + forceDeactivateSim);
731         mConnector.getDownloadableSubscriptionMetadata(cardId, portIndex,
732                 subscription, switchAfterDownload, forceDeactivateSim,
733                 new DownloadSubscriptionGetMetadataCommandCallback(callingToken, subscription,
734                         switchAfterDownload, callingPackage, forceDeactivateSim, callbackIntent,
735                         true /* withUserConsent */, portIndex));
736     }
737 
738     // Continue to download subscription without checking anything.
downloadSubscriptionPrivileged(int cardId, int portIndex, final long callingToken, DownloadableSubscription subscription, boolean switchAfterDownload, boolean forceDeactivateSim, final String callingPackage, Bundle resolvedBundle, final PendingIntent callbackIntent)739     void downloadSubscriptionPrivileged(int cardId, int portIndex, final long callingToken,
740             DownloadableSubscription subscription, boolean switchAfterDownload,
741             boolean forceDeactivateSim, final String callingPackage, Bundle resolvedBundle,
742             final PendingIntent callbackIntent) {
743         mConnector.downloadSubscription(
744                 cardId,
745                 portIndex,
746                 subscription,
747                 switchAfterDownload,
748                 forceDeactivateSim,
749                 resolvedBundle,
750                 new EuiccConnector.DownloadCommandCallback() {
751                     @Override
752                     public void onDownloadComplete(DownloadSubscriptionResult result) {
753                         Intent extrasIntent = new Intent();
754                         final int resultCode;
755                         switch (result.getResult()) {
756                             case EuiccService.RESULT_OK:
757                                 resultCode = OK;
758                                 // Now that a profile has been successfully downloaded, mark the
759                                 // eUICC as provisioned so it appears in settings UI as appropriate.
760                                 Settings.Global.putInt(
761                                         mContext.getContentResolver(),
762                                         Settings.Global.EUICC_PROVISIONED,
763                                         1);
764                                 extrasIntent.putExtra(
765                                         EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
766                                         subscription);
767                                 if (!switchAfterDownload) {
768                                     // Since we're not switching, nothing will trigger a
769                                     // subscription list refresh on its own, so request one here.
770                                     refreshSubscriptionsAndSendResult(
771                                             callbackIntent, resultCode, extrasIntent);
772                                     return;
773                                 }
774                                 break;
775                             case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
776                                 resultCode = RESOLVABLE_ERROR;
777                                 addResolutionIntentWithPort(extrasIntent,
778                                         EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
779                                         callingPackage,
780                                         0 /* resolvableErrors */,
781                                         false /* confirmationCodeRetried */,
782                                         EuiccOperation.forDownloadDeactivateSim(
783                                                 callingToken, subscription, switchAfterDownload,
784                                                 callingPackage),
785                                         cardId,
786                                         portIndex, switchAfterDownload /* usePortIndex */);
787                                 break;
788                             case EuiccService.RESULT_RESOLVABLE_ERRORS:
789                                 // Same value as the deprecated
790                                 // {@link EuiccService#RESULT_NEED_CONFIRMATION_CODE}. For the
791                                 // deprecated case, the resolvableErrors is set as 0 in
792                                 // EuiccService.
793                                 resultCode = RESOLVABLE_ERROR;
794                                 boolean retried = false;
795                                 if (!TextUtils.isEmpty(subscription.getConfirmationCode())) {
796                                     retried = true;
797                                 }
798                                 if (result.getResolvableErrors() != 0) {
799                                     addResolutionIntentWithPort(extrasIntent,
800                                             EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS,
801                                             callingPackage,
802                                             result.getResolvableErrors(),
803                                             retried,
804                                             EuiccOperation.forDownloadResolvableErrors(
805                                                 callingToken, subscription, switchAfterDownload,
806                                                 callingPackage, result.getResolvableErrors()),
807                                             cardId,
808                                             portIndex, switchAfterDownload /* usePortIndex */);
809                                 }  else { // Deprecated case
810                                     addResolutionIntentWithPort(extrasIntent,
811                                             EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE,
812                                             callingPackage,
813                                             0 /* resolvableErrors */,
814                                             retried /* confirmationCodeRetried */,
815                                             EuiccOperation.forDownloadConfirmationCode(
816                                                 callingToken, subscription, switchAfterDownload,
817                                                 callingPackage),
818                                             cardId,
819                                             portIndex, switchAfterDownload /* usePortIndex */);
820                                 }
821                                 break;
822                             default:
823                                 resultCode = ERROR;
824 
825                                 addExtrasToResultIntent(extrasIntent, result.getResult());
826                                 break;
827                         }
828 
829                         sendResult(callbackIntent, resultCode, extrasIntent);
830                     }
831 
832                     @Override
833                     public void onEuiccServiceUnavailable() {
834                         sendResult(callbackIntent, ERROR, null /* extrasIntent */);
835                     }
836                 });
837     }
838 
839     /**
840      * Blocking call to {@link EuiccService#onGetEuiccProfileInfoList} of the eUICC with card ID
841      * {@code cardId}.
842      *
843      * <p>Does not perform permission checks as this is not an exposed API and is only used within
844      * the phone process.
845      */
blockingGetEuiccProfileInfoList(int cardId)846     public GetEuiccProfileInfoListResult blockingGetEuiccProfileInfoList(int cardId) {
847         final CountDownLatch latch = new CountDownLatch(1);
848         final AtomicReference<GetEuiccProfileInfoListResult> resultRef = new AtomicReference<>();
849         mConnector.getEuiccProfileInfoList(
850                 cardId,
851                 new EuiccConnector.GetEuiccProfileInfoListCommandCallback() {
852                     @Override
853                     public void onListComplete(GetEuiccProfileInfoListResult result) {
854                         resultRef.set(result);
855                         latch.countDown();
856                     }
857 
858                     @Override
859                     public void onEuiccServiceUnavailable() {
860                         latch.countDown();
861                     }
862                 });
863         try {
864             latch.await();
865         } catch (InterruptedException e) {
866             Log.e(TAG, "blockingGetEuiccInfoFromEuiccService got InterruptedException e: " + e);
867             Thread.currentThread().interrupt();
868         }
869         return resultRef.get();
870     }
871 
872     @Override
getDefaultDownloadableSubscriptionList(int cardId, String callingPackage, PendingIntent callbackIntent)873     public void getDefaultDownloadableSubscriptionList(int cardId,
874             String callingPackage, PendingIntent callbackIntent) {
875         getDefaultDownloadableSubscriptionList(cardId,
876                 false /* forceDeactivateSim */, callingPackage, callbackIntent);
877     }
878 
getDefaultDownloadableSubscriptionList(int cardId, boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent)879     void getDefaultDownloadableSubscriptionList(int cardId,
880             boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent) {
881         Log.d(TAG, " getDefaultDownloadableSubscriptionList callingPackage: " + callingPackage);
882         if (!callerCanWriteEmbeddedSubscriptions()) {
883             throw new SecurityException(
884                     "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get default list");
885         }
886         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
887         long token = Binder.clearCallingIdentity();
888         try {
889             mConnector.getDefaultDownloadableSubscriptionList(cardId,
890                     forceDeactivateSim, new GetDefaultListCommandCallback(
891                             token, callingPackage, callbackIntent));
892         } finally {
893             Binder.restoreCallingIdentity(token);
894         }
895     }
896 
897     class GetDefaultListCommandCallback implements EuiccConnector.GetDefaultListCommandCallback {
898         final long mCallingToken;
899         final String mCallingPackage;
900         final PendingIntent mCallbackIntent;
901 
GetDefaultListCommandCallback(long callingToken, String callingPackage, PendingIntent callbackIntent)902         GetDefaultListCommandCallback(long callingToken, String callingPackage,
903                 PendingIntent callbackIntent) {
904             mCallingToken = callingToken;
905             mCallingPackage = callingPackage;
906             mCallbackIntent = callbackIntent;
907         }
908 
909         @Override
onGetDefaultListComplete(int cardId, GetDefaultDownloadableSubscriptionListResult result)910         public void onGetDefaultListComplete(int cardId,
911                 GetDefaultDownloadableSubscriptionListResult result) {
912             Intent extrasIntent = new Intent();
913             final int resultCode;
914             switch (result.getResult()) {
915                 case EuiccService.RESULT_OK:
916                     resultCode = OK;
917                     List<DownloadableSubscription> list = result.getDownloadableSubscriptions();
918                     if (list != null && list.size() > 0) {
919                         extrasIntent.putExtra(
920                                 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS,
921                                 list.toArray(new DownloadableSubscription[list.size()]));
922                     }
923                     break;
924                 case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
925                     resultCode = RESOLVABLE_ERROR;
926                     addResolutionIntentWithPort(extrasIntent,
927                             EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
928                             mCallingPackage,
929                             0 /* resolvableErrors */,
930                             false /* confirmationCodeRetried */,
931                             EuiccOperation.forGetDefaultListDeactivateSim(
932                                     mCallingToken, mCallingPackage),
933                             cardId,
934                             TelephonyManager.DEFAULT_PORT_INDEX, false /* usePortIndex */);
935                     break;
936                 default:
937                     resultCode = ERROR;
938                     addExtrasToResultIntent(extrasIntent, result.getResult());
939                     break;
940             }
941 
942             sendResult(mCallbackIntent, resultCode, extrasIntent);
943         }
944 
945         @Override
onEuiccServiceUnavailable()946         public void onEuiccServiceUnavailable() {
947             sendResult(mCallbackIntent, ERROR, null /* extrasIntent */);
948         }
949     }
950 
951     /**
952      * Return the {@link EuiccInfo}.
953      *
954      * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load,
955      * that IPC should generally be fast, and this info shouldn't be needed in the normal course of
956      * operation.
957      */
958     @Override
getEuiccInfo(int cardId)959     public EuiccInfo getEuiccInfo(int cardId) {
960         // No permissions required as EuiccInfo is not sensitive.
961         long token = Binder.clearCallingIdentity();
962         try {
963             return blockingGetEuiccInfoFromEuiccService(cardId);
964         } finally {
965             Binder.restoreCallingIdentity(token);
966         }
967     }
968 
969     @Override
deleteSubscription(int cardId, int subscriptionId, String callingPackage, PendingIntent callbackIntent)970     public void deleteSubscription(int cardId, int subscriptionId, String callingPackage,
971             PendingIntent callbackIntent) {
972         boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
973         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
974 
975         long token = Binder.clearCallingIdentity();
976         try {
977             SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId);
978             if (sub == null) {
979                 Log.e(TAG, "Cannot delete nonexistent subscription: " + subscriptionId);
980                 sendResult(callbackIntent, ERROR, null /* extrasIntent */);
981                 return;
982             }
983 
984             // For both single active SIM device and multi-active SIM device, if the caller is
985             // system or the caller manage the target subscription, we let it continue. This is
986             // because deleting subscription won't change status of any other subscriptions.
987             if (!callerCanWriteEmbeddedSubscriptions
988                     && !mSubscriptionManager.canManageSubscription(sub, callingPackage)) {
989                 Log.e(TAG, "No permissions: " + subscriptionId);
990                 sendResult(callbackIntent, ERROR, null /* extrasIntent */);
991                 return;
992             }
993 
994             deleteSubscriptionPrivileged(cardId, sub.getIccId(), callbackIntent);
995         } finally {
996             Binder.restoreCallingIdentity(token);
997         }
998     }
999 
deleteSubscriptionPrivileged(int cardId, String iccid, final PendingIntent callbackIntent)1000     void deleteSubscriptionPrivileged(int cardId, String iccid,
1001             final PendingIntent callbackIntent) {
1002         mConnector.deleteSubscription(
1003                 cardId,
1004                 iccid,
1005                 new EuiccConnector.DeleteCommandCallback() {
1006                     @Override
1007                     public void onDeleteComplete(int result) {
1008                         Intent extrasIntent = new Intent();
1009                         final int resultCode;
1010                         switch (result) {
1011                             case EuiccService.RESULT_OK:
1012                                 resultCode = OK;
1013                                 refreshSubscriptionsAndSendResult(
1014                                         callbackIntent, resultCode, extrasIntent);
1015                                 return;
1016                             default:
1017                                 resultCode = ERROR;
1018                                 addExtrasToResultIntent(extrasIntent, result);
1019                                 break;
1020                         }
1021 
1022                         sendResult(callbackIntent, resultCode, extrasIntent);
1023                     }
1024 
1025                     @Override
1026                     public void onEuiccServiceUnavailable() {
1027                         sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1028                     }
1029                 });
1030     }
1031 
1032     @Override
switchToSubscription(int cardId, int subscriptionId, String callingPackage, PendingIntent callbackIntent)1033     public void switchToSubscription(int cardId, int subscriptionId, String callingPackage,
1034             PendingIntent callbackIntent) {
1035         // convert PendingIntent to callback if no callback provided
1036         switchToSubscription(cardId, subscriptionId, 0, false /* forceDeactivateSim */,
1037                 callingPackage, callbackIntent, false);
1038     }
1039 
1040     @Override
switchToSubscriptionWithPort(int cardId, int subscriptionId, int portIndex, String callingPackage, PendingIntent callbackIntent)1041     public void switchToSubscriptionWithPort(int cardId, int subscriptionId, int portIndex,
1042             String callingPackage, PendingIntent callbackIntent) {
1043         switchToSubscription(cardId, subscriptionId, portIndex, false /* forceDeactivateSim */,
1044                 callingPackage, callbackIntent, true);
1045     }
1046 
switchToSubscription(int cardId, int subscriptionId, int portIndex, boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent, boolean usePortIndex)1047     void switchToSubscription(int cardId, int subscriptionId, int portIndex,
1048             boolean forceDeactivateSim, String callingPackage, PendingIntent callbackIntent,
1049             boolean usePortIndex) {
1050         boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
1051         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
1052         // Resolve the portIndex internally if apps targeting T and beyond are calling
1053         // switchToSubscription API without portIndex.
1054         boolean shouldResolvePortIndex = isCompatChangeEnabled(callingPackage,
1055                 EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS);
1056         Log.d(TAG, " subId: " + subscriptionId + " portIndex: " + portIndex
1057                 + " forceDeactivateSim: " + forceDeactivateSim + " usePortIndex: " + usePortIndex
1058                 + " callingPackage: " + callingPackage + " shouldResolvePortIndex: "
1059                 + shouldResolvePortIndex);
1060         long token = Binder.clearCallingIdentity();
1061         try {
1062             if (callerCanWriteEmbeddedSubscriptions) {
1063                 // Assume that if a privileged caller is calling us, we don't need to prompt the
1064                 // user about changing carriers, because the caller would only be acting in response
1065                 // to user action.
1066                 forceDeactivateSim = true;
1067             }
1068 
1069             final String iccid;
1070             boolean passConsent = false;
1071             boolean isConsentNeededToResolvePortIndex = false;
1072             if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1073                 if (!usePortIndex) {
1074                     // Resolve the portIndex internally if apps are calling switchToSubscription
1075                     // API without portIndex and subscription id is invalid.
1076                     portIndex = getResolvedPortIndexForDisableSubscription(cardId, callingPackage,
1077                             callerCanWriteEmbeddedSubscriptions);
1078                     if (portIndex == TelephonyManager.INVALID_PORT_INDEX) {
1079                         Log.e(TAG, "Disable is not permitted: no active subscription or cannot"
1080                                 + " manage subscription");
1081                         sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1082                         return;
1083                     }
1084                     usePortIndex = true;
1085                 }
1086                 if (callerCanWriteEmbeddedSubscriptions
1087                         || canManageActiveSubscriptionOnTargetSim(cardId, callingPackage,
1088                         usePortIndex, portIndex)) {
1089                     passConsent = true;
1090                 } else {
1091                     Log.e(TAG, "Not permitted to switch to empty subscription");
1092                     sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1093                     return;
1094                 }
1095                 iccid = null;
1096             } else {
1097                 SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId);
1098                 if (sub == null) {
1099                     Log.e(TAG, "Cannot switch to nonexistent sub: " + subscriptionId);
1100                     sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1101                     return;
1102                 }
1103                 if (callerCanWriteEmbeddedSubscriptions) {
1104                     passConsent = true;
1105                 } else {
1106                     if (!mSubscriptionManager.canManageSubscription(sub, callingPackage)) {
1107                         Log.e(TAG, "Not permitted to switch to sub: " + subscriptionId);
1108                         sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1109                         return;
1110                     }
1111 
1112                     if (canManageSubscriptionOnTargetSim(cardId, callingPackage, usePortIndex,
1113                             portIndex)) {
1114                         passConsent = true;
1115                     }
1116                 }
1117                 iccid = sub.getIccId();
1118                 if (usePortIndex) {
1119                     boolean hasValidPortIndex = isTargetPortIndexValid(cardId, portIndex);
1120                     if (!hasValidPortIndex) {
1121                         // Return permanent error.
1122                         Log.e(TAG, "Not permitted to switch to invalid portIndex");
1123                         Intent extrasIntent = new Intent();
1124                         extrasIntent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE,
1125                                 EuiccManager.ERROR_INVALID_PORT);
1126                         sendResult(callbackIntent, ERROR, extrasIntent /* extrasIntent */);
1127                         return;
1128                     }
1129                 } else {
1130                     // Resolve the portIndex internally if apps targeting T and beyond are calling
1131                     // switchToSubscription API without portIndex.
1132                     portIndex = shouldResolvePortIndex ?
1133                             getResolvedPortIndexForSubscriptionSwitch(cardId)
1134                             : TelephonyManager.DEFAULT_PORT_INDEX;
1135                     isConsentNeededToResolvePortIndex = (portIndex
1136                             == TelephonyManager.INVALID_PORT_INDEX);
1137                     usePortIndex = true;
1138                     Log.d(TAG, " Resolved portIndex: " + portIndex);
1139                 }
1140             }
1141             if (!passConsent || isConsentNeededToResolvePortIndex) {
1142                 // Switch needs consent.
1143                 Intent extrasIntent = new Intent();
1144                 addResolutionIntent(extrasIntent,
1145                         EuiccService.ACTION_RESOLVE_NO_PRIVILEGES,
1146                         callingPackage,
1147                         0 /* resolvableErrors */,
1148                         false /* confirmationCodeRetried */,
1149                         EuiccOperation.forSwitchNoPrivileges(
1150                                 token, subscriptionId, callingPackage),
1151                         cardId, portIndex, usePortIndex, subscriptionId);
1152                 sendResult(callbackIntent, RESOLVABLE_ERROR, extrasIntent);
1153                 return;
1154             }
1155 
1156             switchToSubscriptionPrivileged(cardId, portIndex, token, subscriptionId, iccid,
1157                     forceDeactivateSim, callingPackage, callbackIntent, usePortIndex);
1158         } finally {
1159             Binder.restoreCallingIdentity(token);
1160         }
1161     }
1162 
1163     /**
1164      * Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if calling
1165      * cannot manage any active subscription.
1166      */
1167     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getResolvedPortIndexForDisableSubscription(int cardId, String callingPackage, boolean callerCanWriteEmbeddedSubscriptions)1168     public int getResolvedPortIndexForDisableSubscription(int cardId, String callingPackage,
1169             boolean callerCanWriteEmbeddedSubscriptions) {
1170         List<SubscriptionInfo> subInfoList = mSubscriptionManager
1171                 .getActiveSubscriptionInfoList(/* userVisibleOnly */false);
1172         if (subInfoList == null || subInfoList.size() == 0) {
1173             // No active subscription on any SIM.
1174             return TelephonyManager.INVALID_PORT_INDEX;
1175         }
1176         // Return the portIndex of the first active subscription managed by the calling app.
1177         for (SubscriptionInfo subInfo : subInfoList) {
1178             // If cardId == TelephonyManager.UNSUPPORTED_CARD_ID, we assume it does not support
1179             // multiple eSIMs. There are older multi-active SIM devices which do not implement HAL
1180             // 1.2 and if they have multiple eSIMs, we let it pass if the app can manage an active
1181             // subscription on any eSIM. That's the best we can do here.
1182             if ((cardId == TelephonyManager.UNSUPPORTED_CARD_ID || subInfo.getCardId() == cardId)
1183                     && subInfo.isEmbedded()
1184                     && (callerCanWriteEmbeddedSubscriptions
1185                     || mSubscriptionManager.canManageSubscription(subInfo, callingPackage))) {
1186                 return subInfo.getPortIndex();
1187             }
1188         }
1189         return TelephonyManager.INVALID_PORT_INDEX;
1190     }
1191 
1192     /**
1193      * Returns the resolved portIndex or {@link TelephonyManager#INVALID_PORT_INDEX} if no port
1194      * is available without user consent.
1195      */
1196     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getResolvedPortIndexForSubscriptionSwitch(int cardId)1197     public int getResolvedPortIndexForSubscriptionSwitch(int cardId) {
1198         int slotIndex = getSlotIndexFromCardId(cardId);
1199         // Euicc Slot
1200         UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex);
1201         if (slot == null) {
1202             // Check is to make sure crash is avoided in case of slot is null.
1203             Log.d(TAG, "Switch to inactive slot, return default port index. slotIndex: "
1204                     + slotIndex);
1205             return TelephonyManager.DEFAULT_PORT_INDEX;
1206         }
1207         if (!slot.isMultipleEnabledProfileSupported()) {
1208             Log.d(TAG, "Multiple enabled profiles is not supported, return default port index");
1209             return TelephonyManager.DEFAULT_PORT_INDEX;
1210         }
1211         boolean isPsimActive = getRemovableNonEuiccSlot() != null
1212                 && getRemovableNonEuiccSlot().isActive();
1213         if (mTelephonyManager.getActiveModemCount() == 1) {
1214             // SS Mode
1215             if (isPsimActive) {
1216                 // In case of SS Mode and pSim is active, return default port index for
1217                 // two reasons.
1218                 // 1. If psim and esim share the same carrier privilege, then users wouldn't need
1219                 // to consent, the switch should be seamless.
1220                 // 2. If psim is active and empty or psim and esim doesn't share the same carrier
1221                 // privilege, then permission check dialog will be shown anyway.
1222                 return TelephonyManager.DEFAULT_PORT_INDEX;
1223             }
1224             // If esim port is active, return the active portIndex irrespective of whether port is
1225             // empty or has active subscription.
1226             for (int portIndex : slot.getPortList()) {
1227                 if (slot.isPortActive(portIndex)) {
1228                     return portIndex;
1229                 }
1230             }
1231         } else {
1232             // DSDS Mode
1233             for (int portIndex : slot.getPortList()) {
1234                 if (slot.isPortActive(portIndex)) {
1235                     SubscriptionInfo subscriptionInfo =
1236                               mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
1237                                     slot.getPhoneIdFromPortIndex(portIndex));
1238                     if (subscriptionInfo == null || subscriptionInfo.isOpportunistic()) {
1239                             // If the port is active and empty/opportunistic, return the portIndex.
1240                         return portIndex;
1241                     }
1242                 }
1243             }
1244             // Check whether the pSim is active and empty
1245             boolean isPsimEmpty = isPsimActive && !isRemovalNonEuiccSlotHasActiveSubscription();
1246             if (isPsimEmpty) {
1247                 // This logic will execute only if below two conditions are true.
1248                 // 1. pSim is active and empty
1249                 // 2. eSim has active subscription
1250                 // Return the next available inactive eSim portIndex.
1251                 return getNextAvailableInActivePortIndex(slot);
1252             }
1253         }
1254         return TelephonyManager.INVALID_PORT_INDEX;
1255     }
1256 
1257     /**
1258      * Returns true if the target port index is valid.
1259      * 1. Port index is valid if it is non-negative and less than the total port count.
1260      * 2. In SS Mode, port index is invalid if the embedded slot already has an active port
1261      * with different port index than the target port index.
1262      * 3. In DSDS mode, port index is invalid if the pSim slot is active and the embedded slot
1263      * already has an active empty port with different port index than the target port index.
1264      */
isTargetPortIndexValid(int cardId, int targetPortIndex)1265     private boolean isTargetPortIndexValid(int cardId, int targetPortIndex) {
1266         if (targetPortIndex < 0) {
1267             Log.e(TAG, "Invalid portIndex: " + targetPortIndex);
1268             return false;
1269         }
1270         int slotIndex = getSlotIndexFromCardId(cardId);
1271         UiccSlot slot = UiccController.getInstance().getUiccSlot(slotIndex);
1272         if (slot == null || slot.getPortList().length == 0
1273                 || targetPortIndex >= slot.getPortList().length) {
1274             Log.e(TAG, "Invalid portIndex");
1275             return false;
1276         }
1277 
1278         if (mTelephonyManager.getActiveModemCount() == 1) {
1279             // SS Mode
1280             for (int portIndex : slot.getPortList()) {
1281                 if (slot.isPortActive(portIndex) && portIndex != targetPortIndex) {
1282                     // if there is an active esim port, should not try to enable the
1283                     // profile on other inactive port.
1284                     Log.e(TAG, "In SS Mode, slot already has active port on portIndex " + portIndex
1285                             + " , reject the switch request to portIndex " + targetPortIndex);
1286                     return false;
1287                 }
1288             }
1289         } else if (mTelephonyManager.getActiveModemCount() > 1) {
1290             // DSDS Mode
1291             // If physical slot has active subscription and eSim has active port (without active
1292             // subscription), should not try to enable the profile on other inactive port.
1293             boolean isPsimActive = isRemovalNonEuiccSlotHasActiveSubscription();
1294             if (isPsimActive) {
1295                 for (int portIndex : slot.getPortList()) {
1296                     if (slot.isPortActive(portIndex)
1297                             && mSubscriptionManager
1298                             .getActiveSubscriptionInfoForSimSlotIndex(
1299                                     slot.getPhoneIdFromPortIndex(portIndex)) == null
1300                             && portIndex != targetPortIndex) {
1301                         Log.e(TAG, "In DSDS Mode, pSim has active subscription, eSim has empty"
1302                                 + " active port on portIndex " + portIndex
1303                                 + " , reject the switch request to portIndex " + targetPortIndex);
1304                         return false;
1305                     }
1306                 }
1307             }
1308         }
1309         return true;
1310     }
1311 
getNextAvailableInActivePortIndex(UiccSlot slot)1312     private int getNextAvailableInActivePortIndex(UiccSlot slot) {
1313         if (slot != null) {
1314             for (int portIndex : slot.getPortList()) {
1315                 if (!slot.isPortActive(portIndex)) {
1316                     return portIndex;
1317                 }
1318             }
1319         }
1320         return TelephonyManager.INVALID_PORT_INDEX;
1321     }
1322 
1323     /**
1324      * Gets the slot index from the card ID.
1325      */
getSlotIndexFromCardId(int cardId)1326     private int getSlotIndexFromCardId(int cardId) {
1327         UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
1328         if (slotInfos == null || slotInfos.length == 0) {
1329             Log.e(TAG, "UiccSlotInfo is null or empty");
1330             return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
1331         }
1332         String cardIdString = UiccController.getInstance().convertToCardString(cardId);
1333         for (int slotIndex = 0; slotIndex < slotInfos.length; slotIndex++) {
1334             if (slotInfos[slotIndex] == null) {
1335                 AnomalyReporter.reportAnomaly(
1336                         UUID.fromString("e9517acf-e1a1-455f-9231-1b5515a0d0eb"),
1337                         "EuiccController: Found UiccSlotInfo Null object.");
1338             }
1339             String retrievedCardId = slotInfos[slotIndex] != null
1340                     ? slotInfos[slotIndex].getCardId() : null;
1341             if (IccUtils.compareIgnoreTrailingFs(cardIdString, retrievedCardId)) {
1342                 return slotIndex;
1343             }
1344         }
1345         Log.i(TAG, "No UiccSlotInfo found for cardId: " + cardId);
1346         return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
1347     }
1348 
isRemovalNonEuiccSlotHasActiveSubscription()1349     private boolean isRemovalNonEuiccSlotHasActiveSubscription() {
1350         UiccSlot uiccSlot = getRemovableNonEuiccSlot();
1351         if (uiccSlot != null) {
1352             for (int portIndex : uiccSlot.getPortList()) {
1353                 if (uiccSlot.isPortActive(portIndex)
1354                         && mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
1355                                 uiccSlot.getPhoneIdFromPortIndex(portIndex)) != null) {
1356                     return true;
1357                 }
1358             }
1359         }
1360         return false;
1361     }
1362 
getRemovableNonEuiccSlot()1363     private UiccSlot getRemovableNonEuiccSlot() {
1364         UiccSlot[] uiccSlots = UiccController.getInstance().getUiccSlots();
1365         if (uiccSlots != null) {
1366             for (int i = 0; i < uiccSlots.length; i++) {
1367                 if (uiccSlots[i] != null && uiccSlots[i].isRemovable()
1368                         && !uiccSlots[i].isEuicc()) {
1369                     return uiccSlots[i];
1370                 }
1371             }
1372         }
1373         return null;
1374     }
1375 
switchToSubscriptionPrivileged(int cardId, int portIndex, final long callingToken, int subscriptionId, boolean forceDeactivateSim, final String callingPackage, final PendingIntent callbackIntent, boolean usePortIndex)1376     void switchToSubscriptionPrivileged(int cardId, int portIndex, final long callingToken,
1377             int subscriptionId, boolean forceDeactivateSim, final String callingPackage,
1378             final PendingIntent callbackIntent, boolean usePortIndex) {
1379         String iccid = null;
1380         SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId);
1381         if (sub != null) {
1382             iccid = sub.getIccId();
1383         }
1384         switchToSubscriptionPrivileged(cardId, portIndex, callingToken, subscriptionId, iccid,
1385                 forceDeactivateSim, callingPackage, callbackIntent, usePortIndex);
1386     }
1387 
switchToSubscriptionPrivileged(int cardId, int portIndex, final long callingToken, int subscriptionId, @Nullable String iccid, boolean forceDeactivateSim, final String callingPackage, final PendingIntent callbackIntent, boolean usePortIndex)1388     void switchToSubscriptionPrivileged(int cardId, int portIndex, final long callingToken,
1389             int subscriptionId, @Nullable String iccid, boolean forceDeactivateSim,
1390             final String callingPackage, final PendingIntent callbackIntent, boolean usePortIndex) {
1391         mConnector.switchToSubscription(
1392                 cardId,
1393                 portIndex,
1394                 iccid,
1395                 forceDeactivateSim,
1396                 new EuiccConnector.SwitchCommandCallback() {
1397                     @Override
1398                     public void onSwitchComplete(int result) {
1399                         Intent extrasIntent = new Intent();
1400                         final int resultCode;
1401                         switch (result) {
1402                             case EuiccService.RESULT_OK:
1403                                 resultCode = OK;
1404                                 break;
1405                             case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
1406                                 resultCode = RESOLVABLE_ERROR;
1407                                 addResolutionIntent(extrasIntent,
1408                                         EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM,
1409                                         callingPackage,
1410                                         0 /* resolvableErrors */,
1411                                         false /* confirmationCodeRetried */,
1412                                         EuiccOperation.forSwitchDeactivateSim(
1413                                                 callingToken, subscriptionId, callingPackage),
1414                                         cardId, portIndex, usePortIndex, subscriptionId);
1415                                 break;
1416                             default:
1417                                 resultCode = ERROR;
1418                                 addExtrasToResultIntent(extrasIntent, result);
1419                                 break;
1420                         }
1421                         sendResult(callbackIntent, resultCode, extrasIntent);
1422                     }
1423 
1424                     @Override
1425                     public void onEuiccServiceUnavailable() {
1426                         sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1427                     }
1428                 },
1429                 usePortIndex);
1430     }
1431 
1432     @Override
updateSubscriptionNickname(int cardId, int subscriptionId, String nickname, String callingPackage, PendingIntent callbackIntent)1433     public void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname,
1434             String callingPackage, PendingIntent callbackIntent) {
1435         boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
1436         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
1437 
1438         long token = Binder.clearCallingIdentity();
1439         try {
1440             SubscriptionInfo sub = getSubscriptionForSubscriptionId(subscriptionId);
1441             if (sub == null) {
1442                 Log.e(TAG, "Cannot update nickname to nonexistent sub: " + subscriptionId);
1443                 sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1444                 return;
1445             }
1446 
1447             // For both single active SIM device and multi-active SIM device, if the caller is
1448             // system or the caller can manage the target subscription, we let it continue. This is
1449             // because updating subscription nickname won't affect any other subscriptions.
1450             if (!callerCanWriteEmbeddedSubscriptions
1451                     && !mSubscriptionManager.canManageSubscription(sub, callingPackage)) {
1452                 Log.e(TAG, "No permissions: " + subscriptionId);
1453                 sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1454                 return;
1455             }
1456 
1457             mConnector.updateSubscriptionNickname(cardId,
1458                     sub.getIccId(), nickname,
1459                     new EuiccConnector.UpdateNicknameCommandCallback() {
1460                         @Override
1461                         public void onUpdateNicknameComplete(int result) {
1462                             Intent extrasIntent = new Intent();
1463                             final int resultCode;
1464                             switch (result) {
1465                                 case EuiccService.RESULT_OK:
1466                                     resultCode = OK;
1467                                     refreshSubscriptionsAndSendResult(
1468                                             callbackIntent, resultCode, extrasIntent);
1469                                     return;
1470                                 default:
1471                                     resultCode = ERROR;
1472                                     addExtrasToResultIntent(extrasIntent, result);
1473                                     break;
1474                             }
1475 
1476                             sendResult(callbackIntent, resultCode, extrasIntent);
1477                         }
1478 
1479                         @Override
1480                         public void onEuiccServiceUnavailable() {
1481                             sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1482                         }
1483                     });
1484         } finally {
1485             Binder.restoreCallingIdentity(token);
1486         }
1487     }
1488 
1489     @Override
eraseSubscriptions(int cardId, PendingIntent callbackIntent)1490     public void eraseSubscriptions(int cardId, PendingIntent callbackIntent) {
1491         if (!callerCanWriteEmbeddedSubscriptions()) {
1492             throw new SecurityException(
1493                     "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to erase subscriptions");
1494         }
1495         long token = Binder.clearCallingIdentity();
1496         try {
1497             mConnector.eraseSubscriptions(
1498                     cardId, new EuiccConnector.EraseCommandCallback() {
1499                         @Override
1500                         public void onEraseComplete(int result) {
1501                             Intent extrasIntent = new Intent();
1502                             final int resultCode;
1503                             switch (result) {
1504                                 case EuiccService.RESULT_OK:
1505                                     resultCode = OK;
1506                                     refreshSubscriptionsAndSendResult(
1507                                             callbackIntent, resultCode, extrasIntent);
1508                                     return;
1509                                 default:
1510                                     resultCode = ERROR;
1511                                     addExtrasToResultIntent(extrasIntent, result);
1512                                     break;
1513                             }
1514 
1515                             sendResult(callbackIntent, resultCode, extrasIntent);
1516                         }
1517 
1518                         @Override
1519                         public void onEuiccServiceUnavailable() {
1520                             sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1521                         }
1522                     });
1523         } finally {
1524             Binder.restoreCallingIdentity(token);
1525         }
1526     }
1527 
1528     @Override
eraseSubscriptionsWithOptions( int cardId, @ResetOption int options, PendingIntent callbackIntent)1529     public void eraseSubscriptionsWithOptions(
1530             int cardId, @ResetOption int options, PendingIntent callbackIntent) {
1531         if (!callerCanWriteEmbeddedSubscriptions()) {
1532             throw new SecurityException(
1533                     "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to erase subscriptions");
1534         }
1535         long token = Binder.clearCallingIdentity();
1536         try {
1537             mConnector.eraseSubscriptionsWithOptions(
1538                     cardId, options, new EuiccConnector.EraseCommandCallback() {
1539                 @Override
1540                 public void onEraseComplete(int result) {
1541                     Intent extrasIntent = new Intent();
1542                     final int resultCode;
1543                     switch (result) {
1544                         case EuiccService.RESULT_OK:
1545                             resultCode = OK;
1546                             refreshSubscriptionsAndSendResult(
1547                                     callbackIntent, resultCode, extrasIntent);
1548                             return;
1549                         default:
1550                             resultCode = ERROR;
1551                                     addExtrasToResultIntent(extrasIntent, result);
1552                             break;
1553                     }
1554 
1555                     sendResult(callbackIntent, resultCode, extrasIntent);
1556                 }
1557 
1558                 @Override
1559                 public void onEuiccServiceUnavailable() {
1560                     sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1561                 }
1562             });
1563         } finally {
1564             Binder.restoreCallingIdentity(token);
1565         }
1566     }
1567 
1568     @Override
retainSubscriptionsForFactoryReset(int cardId, PendingIntent callbackIntent)1569     public void retainSubscriptionsForFactoryReset(int cardId, PendingIntent callbackIntent) {
1570         mContext.enforceCallingPermission(Manifest.permission.MASTER_CLEAR,
1571                 "Must have MASTER_CLEAR to retain subscriptions for factory reset");
1572         long token = Binder.clearCallingIdentity();
1573         try {
1574             mConnector.retainSubscriptions(cardId,
1575                     new EuiccConnector.RetainSubscriptionsCommandCallback() {
1576                         @Override
1577                         public void onRetainSubscriptionsComplete(int result) {
1578                             Intent extrasIntent = new Intent();
1579                             final int resultCode;
1580                             switch (result) {
1581                                 case EuiccService.RESULT_OK:
1582                                     resultCode = OK;
1583                                     break;
1584                                 default:
1585                                     resultCode = ERROR;
1586                                     addExtrasToResultIntent(extrasIntent, result);
1587                                     break;
1588                             }
1589 
1590                             sendResult(callbackIntent, resultCode, extrasIntent);
1591                         }
1592 
1593                         @Override
1594                         public void onEuiccServiceUnavailable() {
1595                             sendResult(callbackIntent, ERROR, null /* extrasIntent */);
1596                         }
1597                     });
1598         } finally {
1599             Binder.restoreCallingIdentity(token);
1600         }
1601     }
1602 
1603     /** Refresh the embedded subscription list and dispatch the given result upon completion. */
1604     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
refreshSubscriptionsAndSendResult( PendingIntent callbackIntent, int resultCode, Intent extrasIntent)1605     public void refreshSubscriptionsAndSendResult(
1606             PendingIntent callbackIntent, int resultCode, Intent extrasIntent) {
1607         SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
1608                 List.of(mTelephonyManager.getCardIdForDefaultEuicc()),
1609                 () -> sendResult(callbackIntent, resultCode, extrasIntent));
1610     }
1611 
1612     /** Dispatch the given callback intent with the given result code and data. */
1613     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent)1614     public void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) {
1615         try {
1616             callbackIntent.send(mContext, resultCode, extrasIntent);
1617         } catch (PendingIntent.CanceledException e) {
1618             // Caller canceled the callback; do nothing.
1619         }
1620     }
1621 
1622     /** Add a resolution intent to the given extras intent with invalid subscriptionId */
addResolutionIntentWithPort(Intent extrasIntent, String resolutionAction, String callingPackage, int resolvableErrors, boolean confirmationCodeRetried, EuiccOperation op, int cardId, int portIndex, boolean usePortIndex)1623     public void addResolutionIntentWithPort(Intent extrasIntent, String resolutionAction,
1624             String callingPackage, int resolvableErrors, boolean confirmationCodeRetried,
1625             EuiccOperation op, int cardId, int portIndex, boolean usePortIndex) {
1626         // use invalid subscriptionId in case of download/metadata flow
1627         addResolutionIntent(extrasIntent, resolutionAction, callingPackage, resolvableErrors,
1628                 confirmationCodeRetried, op, cardId, portIndex,
1629                 usePortIndex /* usePortIndex */, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
1630     }
1631 
1632     /** Add a resolution intent to the given extras intent. */
1633     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
addResolutionIntent(Intent extrasIntent, String resolutionAction, String callingPackage, int resolvableErrors, boolean confirmationCodeRetried, EuiccOperation op, int cardId, int portIndex, boolean usePortIndex, int subscriptionId)1634     public void addResolutionIntent(Intent extrasIntent, String resolutionAction,
1635             String callingPackage, int resolvableErrors, boolean confirmationCodeRetried,
1636             EuiccOperation op, int cardId, int portIndex, boolean usePortIndex,
1637             int subscriptionId) {
1638         Intent intent = new Intent(EuiccManager.ACTION_RESOLVE_ERROR);
1639         intent.setPackage(RESOLUTION_ACTIVITY_PACKAGE_NAME);
1640         intent.setComponent(new ComponentName(
1641                         RESOLUTION_ACTIVITY_PACKAGE_NAME, RESOLUTION_ACTIVITY_CLASS_NAME));
1642         intent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION,
1643                 resolutionAction);
1644         intent.putExtra(EuiccService.EXTRA_RESOLUTION_CALLING_PACKAGE, callingPackage);
1645         intent.putExtra(EuiccService.EXTRA_RESOLVABLE_ERRORS, resolvableErrors);
1646         intent.putExtra(EuiccService.EXTRA_RESOLUTION_CARD_ID, cardId);
1647         intent.putExtra(EuiccService.EXTRA_RESOLUTION_SUBSCRIPTION_ID, subscriptionId);
1648         intent.putExtra(EuiccService.EXTRA_RESOLUTION_PORT_INDEX, portIndex);
1649         intent.putExtra(EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, usePortIndex);
1650         intent.putExtra(EuiccService.EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED,
1651                 confirmationCodeRetried);
1652         intent.putExtra(EXTRA_OPERATION, op);
1653         PendingIntent resolutionIntent = PendingIntent.getActivity(
1654                 mContext,
1655                 0 /* requestCode */,
1656                 intent,
1657                 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
1658         extrasIntent.putExtra(
1659                 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT, resolutionIntent);
1660     }
1661 
1662     @Override
dump(FileDescriptor fd, final PrintWriter pw, String[] args)1663     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1664         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP");
1665         final long token = Binder.clearCallingIdentity();
1666         pw.println("===== BEGIN EUICC CLINIC =====");
1667         try {
1668             pw.println("===== EUICC CONNECTOR =====");
1669             mConnector.dump(fd, pw, args);
1670             final CountDownLatch countDownLatch = new CountDownLatch(1);
1671             mConnector.dumpEuiccService(new EuiccConnector.DumpEuiccServiceCommandCallback() {
1672                 @Override
1673                 public void onDumpEuiccServiceComplete(String logs) {
1674                     pw.println("===== EUICC SERVICE =====");
1675                     pw.println(logs);
1676                     countDownLatch.countDown();
1677                 }
1678 
1679                 @Override
1680                 public void onEuiccServiceUnavailable() {
1681                     pw.println("===== EUICC SERVICE UNAVAILABLE =====");
1682                     countDownLatch.countDown();
1683                 }
1684             });
1685 
1686             // Wait up to 5 seconds
1687             if (!countDownLatch.await(EUICC_DUMP_TIME_OUT_SECONDS, TimeUnit.SECONDS)) {
1688                 pw.println("===== EUICC SERVICE TIMEOUT =====");
1689             }
1690         } catch (InterruptedException e) {
1691             pw.println("===== EUICC SERVICE INTERRUPTED =====");
1692         } finally {
1693             pw.println("===== END EUICC CLINIC =====");
1694             Binder.restoreCallingIdentity(token);
1695         }
1696     }
1697 
1698     /**
1699      * Send broadcast {@link EuiccManager#ACTION_OTA_STATUS_CHANGED} for OTA status
1700      * changed.
1701      */
1702     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
sendOtaStatusChangedBroadcast()1703     public void sendOtaStatusChangedBroadcast() {
1704         Intent intent = new Intent(EuiccManager.ACTION_OTA_STATUS_CHANGED);
1705         ComponentInfo bestComponent = mConnector.findBestComponent(mContext.getPackageManager());
1706         if (bestComponent != null) {
1707             intent.setPackage(bestComponent.packageName);
1708         }
1709         mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
1710     }
1711 
1712     @Nullable
getSubscriptionForSubscriptionId(int subscriptionId)1713     private SubscriptionInfo getSubscriptionForSubscriptionId(int subscriptionId) {
1714         List<SubscriptionInfo> subs = mSubscriptionManager.getAvailableSubscriptionInfoList();
1715         int subCount = (subs != null) ? subs.size() : 0;
1716         for (int i = 0; i < subCount; i++) {
1717             SubscriptionInfo sub = subs.get(i);
1718             if (subscriptionId == sub.getSubscriptionId()) {
1719                 return sub;
1720             }
1721         }
1722         return null;
1723     }
1724 
1725     @Nullable
blockingGetEidFromEuiccService(int cardId)1726     private String blockingGetEidFromEuiccService(int cardId) {
1727         CountDownLatch latch = new CountDownLatch(1);
1728         AtomicReference<String> eidRef = new AtomicReference<>();
1729         mConnector.getEid(cardId, new EuiccConnector.GetEidCommandCallback() {
1730             @Override
1731             public void onGetEidComplete(String eid) {
1732                 eidRef.set(eid);
1733                 latch.countDown();
1734             }
1735 
1736             @Override
1737             public void onEuiccServiceUnavailable() {
1738                 latch.countDown();
1739             }
1740         });
1741         return awaitResult(latch, eidRef);
1742     }
1743 
blockingGetOtaStatusFromEuiccService(int cardId)1744     private @OtaStatus int blockingGetOtaStatusFromEuiccService(int cardId) {
1745         CountDownLatch latch = new CountDownLatch(1);
1746         AtomicReference<Integer> statusRef =
1747                 new AtomicReference<>(EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE);
1748         mConnector.getOtaStatus(cardId, new EuiccConnector.GetOtaStatusCommandCallback() {
1749             @Override
1750             public void onGetOtaStatusComplete(@OtaStatus int status) {
1751                 statusRef.set(status);
1752                 latch.countDown();
1753             }
1754 
1755             @Override
1756             public void onEuiccServiceUnavailable() {
1757                 latch.countDown();
1758             }
1759         });
1760         return awaitResult(latch, statusRef);
1761     }
1762 
1763     @Nullable
blockingGetEuiccInfoFromEuiccService(int cardId)1764     private EuiccInfo blockingGetEuiccInfoFromEuiccService(int cardId) {
1765         CountDownLatch latch = new CountDownLatch(1);
1766         AtomicReference<EuiccInfo> euiccInfoRef = new AtomicReference<>();
1767         mConnector.getEuiccInfo(cardId, new EuiccConnector.GetEuiccInfoCommandCallback() {
1768             @Override
1769             public void onGetEuiccInfoComplete(EuiccInfo euiccInfo) {
1770                 euiccInfoRef.set(euiccInfo);
1771                 latch.countDown();
1772             }
1773 
1774             @Override
1775             public void onEuiccServiceUnavailable() {
1776                 latch.countDown();
1777             }
1778         });
1779         return awaitResult(latch, euiccInfoRef);
1780     }
1781 
awaitResult(CountDownLatch latch, AtomicReference<T> resultRef)1782     private static <T> T awaitResult(CountDownLatch latch, AtomicReference<T> resultRef) {
1783         try {
1784             latch.await();
1785         } catch (InterruptedException e) {
1786             Thread.currentThread().interrupt();
1787         }
1788         return resultRef.get();
1789     }
1790 
1791     // Returns whether the caller has carrier privilege on the given subscription.
checkCarrierPrivilegeInMetadata(DownloadableSubscription subscription, String callingPackage)1792     private boolean checkCarrierPrivilegeInMetadata(DownloadableSubscription subscription,
1793             String callingPackage) {
1794         UiccAccessRule[] rules = null;
1795         List<UiccAccessRule> rulesList = subscription.getAccessRules();
1796         if (rulesList != null) {
1797             rules = rulesList.toArray(new UiccAccessRule[rulesList.size()]);
1798         }
1799         if (rules == null) {
1800             Log.e(TAG, "No access rules but caller is unprivileged");
1801             return false;
1802         }
1803 
1804         final PackageInfo info;
1805         try {
1806             info = mPackageManager.getPackageInfo(callingPackage,
1807                 PackageManager.GET_SIGNING_CERTIFICATES);
1808         } catch (PackageManager.NameNotFoundException e) {
1809             Log.e(TAG, "Calling package valid but gone");
1810             return false;
1811         }
1812 
1813         for (int i = 0; i < rules.length; i++) {
1814             if (rules[i].getCarrierPrivilegeStatus(info)
1815                     == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
1816                 Log.i(TAG, "Calling package has carrier privilege to this profile");
1817                 return true;
1818             }
1819         }
1820         Log.e(TAG, "Calling package doesn't have carrier privilege to this profile");
1821         return false;
1822     }
1823 
supportMultiActiveSlots()1824     private boolean supportMultiActiveSlots() {
1825         return mTelephonyManager.getSupportedModemCount() > 1;
1826     }
1827 
1828     // Checks whether the caller can manage the active embedded subscription on the SIM with the
1829     // given cardId.
1830     // From Android T, if usePortIndex is true then should check if the calling app has carrier
1831     // privilege over the subscription on the target port index.
canManageActiveSubscriptionOnTargetSim(int cardId, String callingPackage, boolean usePortIndex, int targetPortIndex)1832     private boolean canManageActiveSubscriptionOnTargetSim(int cardId, String callingPackage,
1833             boolean usePortIndex, int targetPortIndex) {
1834         List<SubscriptionInfo> subInfoList = mSubscriptionManager
1835                 .getActiveSubscriptionInfoList(/* userVisibleOnly */false);
1836         if (subInfoList == null || subInfoList.size() == 0) {
1837             // No active subscription on any SIM.
1838             return false;
1839         }
1840         for (SubscriptionInfo subInfo : subInfoList) {
1841             // If cardId == TelephonyManager.UNSUPPORTED_CARD_ID, we assume it does not support
1842             // multiple eSIMs. There are older multi-active SIM devices which do not implement HAL
1843             // 1.2 and if they have multiple eSIMs, we let it pass if the app can manage an active
1844             // subscription on any eSIM. That's the best we can do here.
1845             if ((cardId == TelephonyManager.UNSUPPORTED_CARD_ID || subInfo.getCardId() == cardId)
1846                     && subInfo.isEmbedded()
1847                     && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex)
1848                     && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) {
1849                 return true;
1850             }
1851         }
1852         return false;
1853     }
1854 
1855     // For a multi-active subscriptions phone, checks whether the caller can manage subscription on
1856     // the target SIM with the given cardId. The caller can only manage subscription on the target
1857     // SIM if it can manage the active subscription on the target SIM or there is no active
1858     // subscription on the target SIM, and the caller can manage any active subscription on any
1859     // other SIM. The target SIM should be an eUICC.
1860     // For a single-active subscription phone, checks whether the caller can manage any active
1861     // embedded subscription.
1862     // From Android T, if usePortIndex is true then verify whether the calling app has carrier
1863     // privilege over the active embedded subscription on the target port index.
1864     // If usePortIndex is false then check whether the calling app can manage any active
1865     // subscription on any of the active ports, if there are no active embedded subscriptions then
1866     // verify whether the calling app can manage any active subscription on any of the other SIM.
canManageSubscriptionOnTargetSim(int cardId, String callingPackage, boolean usePortIndex, int targetPortIndex)1867     private boolean canManageSubscriptionOnTargetSim(int cardId, String callingPackage,
1868             boolean usePortIndex, int targetPortIndex) {
1869         List<SubscriptionInfo> subInfoList = mSubscriptionManager
1870                 .getActiveSubscriptionInfoList(false /* userVisibleonly */);
1871         // No active subscription on any SIM.
1872         if (subInfoList == null || subInfoList.size() == 0) {
1873             return false;
1874         }
1875         // If it's a multi-active SIM device, we assume it's above HAL 1.2 which supports cardId.
1876         // There are older multi-active SIM devices but don't implement HAL 1.2. In this case,
1877         // platform can't even detect UiccCardInfo#isEuicc as true for eSIM, which won't let the
1878         // below check pass. That's the best we can do here.
1879         if (supportMultiActiveSlots()) {
1880             // The target card should be an eUICC.
1881             List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
1882             if (cardInfos == null || cardInfos.isEmpty()) {
1883                 return false;
1884             }
1885             boolean isEuicc = false;
1886             for (UiccCardInfo info : cardInfos) {
1887                 if (info != null && info.getCardId() == cardId && info.isEuicc()) {
1888                     isEuicc = true;
1889                     break;
1890                 }
1891             }
1892             if (!isEuicc) {
1893                 Log.i(TAG, "The target SIM is not an eUICC.");
1894                 return false;
1895             }
1896 
1897             // If the caller can't manage the active embedded subscription on the target SIM port,
1898             // return false. If the caller can manage the active embedded subscription on the
1899             // target SIM port, return true directly.
1900             boolean hasActiveEmbeddedSubscription = subInfoList.stream().anyMatch(
1901                     subInfo -> subInfo.isEmbedded() && subInfo.getCardId() == cardId
1902                             && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex));
1903             if (hasActiveEmbeddedSubscription) {
1904                 // hasActiveEmbeddedSubscription is true if there is an active embedded subscription
1905                 // on the target port(in case of usePortIndex is true) or if there is an active
1906                 // embedded subscription on any of the active ports.
1907 
1908                 // 1. If usePortIndex is true, check whether the caller can manage subscription on
1909                 // the target port.
1910                 // 2. If usePortIndex is false, check whether the caller can manage subscription on
1911                 // any of the active ports.
1912                 for (SubscriptionInfo subInfo : subInfoList) {
1913                     // subInfo.isEmbedded() can only be true for the target SIM.
1914                     if (subInfo.isEmbedded()
1915                             && subInfo.getCardId() == cardId
1916                             && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex)
1917                             && mSubscriptionManager.canManageSubscription(
1918                             subInfo, callingPackage)) {
1919                         return true;
1920                     }
1921                 }
1922                 Log.i(TAG, "canManageSubscriptionOnTargetSim cannot manage embedded subscription");
1923                 return false;
1924             }
1925             // There is no active subscription on the target SIM, checks whether the caller can
1926             // manage any active subscription on any other SIM.
1927             final long token = Binder.clearCallingIdentity();
1928             try {
1929                 return mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
1930                     == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
1931             } finally {
1932                 Binder.restoreCallingIdentity(token);
1933             }
1934         } else {
1935             for (SubscriptionInfo subInfo : subInfoList) {
1936                 if (subInfo.isEmbedded()
1937                         && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) {
1938                     return true;
1939                 }
1940             }
1941             return false;
1942         }
1943     }
1944 
callerCanReadPhoneStatePrivileged()1945     private boolean callerCanReadPhoneStatePrivileged() {
1946         return mContext.checkCallingOrSelfPermission(
1947                 Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
1948                 == PackageManager.PERMISSION_GRANTED;
1949     }
1950 
callerCanWriteEmbeddedSubscriptions()1951     private boolean callerCanWriteEmbeddedSubscriptions() {
1952         return mContext.checkCallingOrSelfPermission(
1953                 Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
1954                 == PackageManager.PERMISSION_GRANTED;
1955     }
1956 
1957     @Override
isSimPortAvailable(int cardId, int portIndex, String callingPackage)1958     public boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage) {
1959         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
1960         // If calling app is targeted for Android U and beyond, check for other conditions
1961         // to decide the port availability.
1962         boolean shouldCheckConditionsForInactivePort = isCompatChangeEnabled(callingPackage,
1963                 EuiccManager.INACTIVE_PORT_AVAILABILITY_CHECK);
1964         // In the event that this check is coming from ONS, WRITE_EMBEDDED_SUBSCRIPTIONS will be
1965         // required for the case where a port is inactive but could trivially be enabled without
1966         // requiring user consent.
1967         boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
1968         final long token = Binder.clearCallingIdentity();
1969         try {
1970             List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
1971             for (UiccCardInfo info : cardInfos) {
1972                 if (info == null || info.getCardId() != cardId) {
1973                     continue;
1974                 }
1975                 // Return false in case of non esim or passed port index is greater than
1976                 // the available ports.
1977                 if (!info.isEuicc() || (portIndex == TelephonyManager.INVALID_PORT_INDEX)
1978                         || portIndex >= info.getPorts().size()) {
1979                     return false;
1980                 }
1981                 for (UiccPortInfo portInfo : info.getPorts()) {
1982                     if (portInfo == null || portInfo.getPortIndex() != portIndex) {
1983                         continue;
1984                     }
1985                     if (!portInfo.isActive()) {
1986                         // port is inactive, check whether the caller can activate a new profile
1987                         // seamlessly. This is possible in below condition:
1988                         // 1. Device in DSDS Mode(P+E).
1989                         // 2. pSIM slot is active but no active subscription.
1990                         // 3. Caller has carrier privileges on any phone or has
1991                         // WRITE_EMBEDDED_SUBSCRIPTIONS. The latter covers calls from ONS
1992                         // which does not have carrier privileges.
1993                         if (!shouldCheckConditionsForInactivePort) {
1994                             return false;
1995                         }
1996                         boolean hasActiveRemovableNonEuiccSlot = getRemovableNonEuiccSlot() != null
1997                                 && getRemovableNonEuiccSlot().isActive();
1998                         boolean hasCarrierPrivileges = mTelephonyManager
1999                                 .checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
2000                                 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
2001                         return mTelephonyManager.isMultiSimEnabled()
2002                                 && hasActiveRemovableNonEuiccSlot
2003                                 && !isRemovalNonEuiccSlotHasActiveSubscription()
2004                                 && (hasCarrierPrivileges || callerCanWriteEmbeddedSubscriptions);
2005                     }
2006                     // A port is available if it has no profiles enabled on it or calling app has
2007                     // Carrier privilege over the profile installed on the selected port.
2008                     if (TextUtils.isEmpty(portInfo.getIccId())) {
2009                         return true;
2010                     }
2011                     UiccPort uiccPort =
2012                             UiccController.getInstance().getUiccPortForSlot(
2013                                     info.getPhysicalSlotIndex(), portIndex);
2014                     // Some eSim Vendors return boot profile iccid if no profile is installed.
2015                     // So in this case if profile is empty, port is available.
2016                     if (uiccPort != null
2017                             && uiccPort.getUiccProfile() != null
2018                             && uiccPort.getUiccProfile().isEmptyProfile()) {
2019                         return true;
2020                     }
2021                     Phone phone = PhoneFactory.getPhone(portInfo.getLogicalSlotIndex());
2022                     if (phone == null) {
2023                         Log.e(TAG, "Invalid logical slot: " + portInfo.getLogicalSlotIndex());
2024                         return false;
2025                     }
2026                     CarrierPrivilegesTracker cpt = phone.getCarrierPrivilegesTracker();
2027                     if (cpt == null) {
2028                         Log.e(TAG, "No CarrierPrivilegesTracker");
2029                         return false;
2030                     }
2031                     return (cpt.getCarrierPrivilegeStatusForPackage(callingPackage)
2032                             == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
2033                 }
2034             }
2035         } finally {
2036             Binder.restoreCallingIdentity(token);
2037         }
2038         return false;
2039     }
2040 
2041     @Override
hasCarrierPrivilegesForPackageOnAnyPhone(String callingPackage)2042     public boolean hasCarrierPrivilegesForPackageOnAnyPhone(String callingPackage) {
2043         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
2044         final long token = Binder.clearCallingIdentity();
2045         try {
2046             // checkCarrierPrivilegesForPackageAnyPhone API requires READ_PHONE_STATE permission,
2047             // hence cannot call directly from EuiccManager switchToSubscription
2048             return mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
2049                     == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
2050         } finally {
2051             Binder.restoreCallingIdentity(token);
2052         }
2053     }
2054 
2055     @Override
isCompatChangeEnabled(String callingPackage, long changeId)2056     public boolean isCompatChangeEnabled(String callingPackage, long changeId) {
2057         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
2058         // Platform compat framework kills the callingPackage app to ensure that the change
2059         // takes affect immediately. So the corresponding compat checking is moved to controller.
2060         boolean changeEnabled = CompatChanges.isChangeEnabled(changeId, callingPackage,
2061                 Binder.getCallingUserHandle());
2062         Log.i(TAG, "isCompatChangeEnabled changeId: " + changeId
2063                 + " changeEnabled: " + changeEnabled);
2064         return changeEnabled;
2065     }
2066 }
2067