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