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