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