1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.service.euicc;
17 
18 import static android.telephony.euicc.EuiccCardManager.ResetOption;
19 
20 import android.annotation.CallSuper;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SdkConstant;
25 import android.annotation.SystemApi;
26 import android.app.Service;
27 import android.content.Intent;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.telephony.TelephonyManager;
32 import android.telephony.euicc.DownloadableSubscription;
33 import android.telephony.euicc.EuiccInfo;
34 import android.telephony.euicc.EuiccManager;
35 import android.telephony.euicc.EuiccManager.OtaStatus;
36 import android.text.TextUtils;
37 import android.util.Log;
38 
39 import java.io.PrintWriter;
40 import java.io.StringWriter;
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.util.concurrent.LinkedBlockingQueue;
44 import java.util.concurrent.ThreadFactory;
45 import java.util.concurrent.ThreadPoolExecutor;
46 import java.util.concurrent.TimeUnit;
47 import java.util.concurrent.atomic.AtomicInteger;
48 
49 /**
50  * Service interface linking the system with an eUICC local profile assistant (LPA) application.
51  *
52  * <p>An LPA consists of two separate components (which may both be implemented in the same APK):
53  * the LPA backend, and the LPA UI or LUI.
54  *
55  * <p>To implement the LPA backend, you must extend this class and declare this service in your
56  * manifest file. The service must require the
57  * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter
58  * with the {@link #EUICC_SERVICE_INTERFACE} action. It's suggested that the priority of the intent
59  * filter to be set to a non-zero value in case multiple implementations are present on the device.
60  * See the below example. Note that there will be problem if two LPAs are present and they have the
61  * same priority.
62  * Example:
63  *
64  * <pre>{@code
65  * <service android:name=".MyEuiccService"
66  *          android:permission="android.permission.BIND_EUICC_SERVICE">
67  *     <intent-filter android:priority="100">
68  *         <action android:name="android.service.euicc.EuiccService" />
69  *     </intent-filter>
70  * </service>
71  * }</pre>
72  *
73  * <p>To implement the LUI, you must provide an activity for the following actions:
74  *
75  * <ul>
76  * <li>{@link #ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS}
77  * <li>{@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION}
78  * </ul>
79  *
80  * <p>As with the service, each activity must require the
81  * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission. Each should have an intent
82  * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
83  * priority.
84  *
85  * <p>Old implementations of EuiccService may support passing in slot IDs equal to
86  * {@link android.telephony.SubscriptionManager#INVALID_SIM_SLOT_INDEX}, which allows the LPA to
87  * decide which eUICC to target when there are multiple eUICCs. This behavior is not supported in
88  * Android Q or later.
89  *
90  * @hide
91  */
92 @SystemApi
93 public abstract class EuiccService extends Service {
94     private static final String TAG = "EuiccService";
95 
96     /** Action which must be included in this service's intent filter. */
97     public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
98 
99     /** Category which must be defined to all UI actions, for efficient lookup. */
100     public static final String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
101 
102     // LUI actions. These are passthroughs of the corresponding EuiccManager actions.
103 
104     /**
105      * Action used to bind the carrier app and get the activation code from the carrier app. This
106      * activation code will be used to download the eSIM profile during eSIM activation flow.
107      */
108     public static final String ACTION_BIND_CARRIER_PROVISIONING_SERVICE =
109             "android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE";
110 
111     /**
112      * Intent action sent by the LPA to launch a carrier app Activity for eSIM activation, e.g. a
113      * carrier login screen. Carrier apps wishing to support this activation method must implement
114      * an Activity that responds to this intent action. Upon completion, the Activity must return
115      * one of the following results to the LPA:
116      *
117      * <p>{@code Activity.RESULT_CANCELED}: The LPA should treat this as an back button and abort
118      * the activation flow.
119      * <p>{@code Activity.RESULT_OK}: The LPA should try to get an activation code from the carrier
120      * app by binding to the carrier app service implementing
121      * {@link #ACTION_BIND_CARRIER_PROVISIONING_SERVICE}.
122      * <p>{@code Activity.RESULT_OK} with
123      * {@link android.telephony.euicc.EuiccManager#EXTRA_USE_QR_SCANNER} set to true: The LPA should
124      * start a QR scanner for the user to scan an eSIM profile QR code.
125      * <p>For other results: The LPA should treat this as an error.
126      **/
127     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
128     public static final String ACTION_START_CARRIER_ACTIVATION =
129             "android.service.euicc.action.START_CARRIER_ACTIVATION";
130 
131     /**
132      * @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS
133      * The difference is this one is used by system to bring up the LUI.
134      */
135     public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
136             "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
137 
138     /** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
139     public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
140             "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
141 
142     /**
143      * @see android.telephony.euicc.EuiccManager#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED. This is
144      * a protected intent that can only be sent by the system, and requires the
145      * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission.
146      */
147     public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
148             "android.service.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
149 
150     /**
151      * @see android.telephony.euicc.EuiccManager#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED. This is
152      * a protected intent that can only be sent by the system, and requires the
153      * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission.
154      */
155     public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
156             "android.service.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
157 
158     /**
159      * @see android.telephony.euicc.EuiccManager#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED. This is
160      * a protected intent that can only be sent by the system, and requires the
161      * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission.
162      */
163     public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
164             "android.service.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
165 
166     /**
167      * @see android.telephony.euicc.EuiccManager#ACTION_START_EUICC_ACTIVATION. This is
168      * a protected intent that can only be sent by the system, and requires the
169      * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission.
170      */
171     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
172     public static final String ACTION_START_EUICC_ACTIVATION =
173             "android.service.euicc.action.START_EUICC_ACTIVATION";
174 
175     // LUI resolution actions. These are called by the platform to resolve errors in situations that
176     // require user interaction.
177     // TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
178     // more scoped out.
179     /**
180      * Alert the user that this action will result in an active SIM being deactivated.
181      * To implement the LUI triggered by the system, you need to define this in AndroidManifest.xml.
182      */
183     public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
184             "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
185     /**
186      * Alert the user about a download/switch being done for an app that doesn't currently have
187      * carrier privileges.
188      */
189     public static final String ACTION_RESOLVE_NO_PRIVILEGES =
190             "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
191 
192     /**
193      * Ask the user to input carrier confirmation code.
194      *
195      * @deprecated From Q, the resolvable errors happened in the download step are presented as
196      * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding action would be
197      * {@link #ACTION_RESOLVE_RESOLVABLE_ERRORS}.
198      */
199     @Deprecated
200     public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
201             "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
202 
203     /** Ask the user to resolve all the resolvable errors. */
204     public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS =
205             "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
206 
207     /** @hide */
208     @Retention(RetentionPolicy.SOURCE)
209     @IntDef(flag = true, prefix = { "RESOLVABLE_ERROR_" }, value = {
210             RESOLVABLE_ERROR_CONFIRMATION_CODE,
211             RESOLVABLE_ERROR_POLICY_RULES,
212     })
213     public @interface ResolvableError {}
214 
215     /**
216      * Possible value for the bit map of resolvable errors indicating the download process needs
217      * the user to input confirmation code.
218      */
219     public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1 << 0;
220     /**
221      * Possible value for the bit map of resolvable errors indicating the download process needs
222      * the user's consent to allow profile policy rules.
223      */
224     public static final int RESOLVABLE_ERROR_POLICY_RULES = 1 << 1;
225 
226     /**
227      * Intent extra set for resolution requests containing the package name of the calling app.
228      * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM,
229      * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_RESOLVABLE_ERRORS.
230      */
231     public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
232             "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
233 
234     /**
235      * Intent extra set for resolution requests containing the list of resolvable errors to be
236      * resolved. Each resolvable error is an integer. Its possible values include:
237      * <UL>
238      * <LI>{@link #RESOLVABLE_ERROR_CONFIRMATION_CODE}
239      * <LI>{@link #RESOLVABLE_ERROR_POLICY_RULES}
240      * </UL>
241      */
242     public static final String EXTRA_RESOLVABLE_ERRORS =
243             "android.service.euicc.extra.RESOLVABLE_ERRORS";
244 
245     /**
246      * Intent extra set for resolution requests containing a boolean indicating whether to ask the
247      * user to retry another confirmation code.
248      */
249     public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED =
250             "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
251 
252     /**
253      * Intent extra set for resolution requests containing an int indicating the current card Id.
254      */
255     public static final String EXTRA_RESOLUTION_CARD_ID =
256             "android.service.euicc.extra.RESOLUTION_CARD_ID";
257 
258     /** @hide */
259     @Retention(RetentionPolicy.SOURCE)
260     @IntDef(prefix = { "RESULT_" }, value = {
261             RESULT_OK,
262             RESULT_MUST_DEACTIVATE_SIM,
263             RESULT_RESOLVABLE_ERRORS,
264             RESULT_NEED_CONFIRMATION_CODE,
265             RESULT_FIRST_USER,
266     })
267     public @interface Result {}
268 
269     /** Result code for a successful operation. */
270     public static final int RESULT_OK = 0;
271     /** Result code indicating that an active SIM must be deactivated to perform the operation. */
272     public static final int RESULT_MUST_DEACTIVATE_SIM = -1;
273     /** Result code indicating that the user must resolve resolvable errors. */
274     public static final int RESULT_RESOLVABLE_ERRORS = -2;
275     /**
276      * Result code indicating that the user must input a carrier confirmation code.
277      *
278      * @deprecated From Q, the resolvable errors happened in the download step are presented as
279      * bit map in {@link #EXTRA_RESOLVABLE_ERRORS}. The corresponding result would be
280      * {@link #RESULT_RESOLVABLE_ERRORS}.
281      */
282     @Deprecated
283     public static final int RESULT_NEED_CONFIRMATION_CODE = -2;
284     // New predefined codes should have negative values.
285 
286     /** Start of implementation-specific error results. */
287     public static final int RESULT_FIRST_USER = 1;
288 
289     /**
290      * Boolean extra for resolution actions indicating whether the user granted consent.
291      * This is used and set by the implementation and used in {@code EuiccOperation}.
292      */
293     public static final String EXTRA_RESOLUTION_CONSENT =
294             "android.service.euicc.extra.RESOLUTION_CONSENT";
295     /**
296      * String extra for resolution actions indicating the carrier confirmation code.
297      * This is used and set by the implementation and used in {@code EuiccOperation}.
298      */
299     public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE =
300             "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
301     /**
302      * String extra for resolution actions indicating whether the user allows policy rules.
303      * This is used and set by the implementation and used in {@code EuiccOperation}.
304      */
305     public static final String EXTRA_RESOLUTION_ALLOW_POLICY_RULES =
306             "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
307 
308     private final IEuiccService.Stub mStubWrapper;
309 
310     private ThreadPoolExecutor mExecutor;
311 
EuiccService()312     public EuiccService() {
313         mStubWrapper = new IEuiccServiceWrapper();
314     }
315 
316     /**
317      * Given a SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2), encode it to
318      * the format described in
319      * {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}
320      *
321      * @param subjectCode SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2)
322      * @param reasonCode  ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2)
323      * @return encoded error code described in
324      * {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}
325      * @throws NumberFormatException         when the Subject/Reason code contains non digits
326      * @throws IllegalArgumentException      when Subject/Reason code is null/empty
327      * @throws UnsupportedOperationException when sections has more than four layers (e.g 5.8.1.2)
328      *                                       or when an number is bigger than 15
329      */
encodeSmdxSubjectAndReasonCode(@onNull String subjectCode, @NonNull String reasonCode)330     public int encodeSmdxSubjectAndReasonCode(@NonNull String subjectCode,
331             @NonNull String reasonCode) {
332         final int maxSupportedSection = 3;
333         final int maxSupportedDigit = 15;
334         final int bitsPerSection = 4;
335 
336         if (TextUtils.isEmpty(subjectCode) || TextUtils.isEmpty(reasonCode)) {
337             throw new IllegalArgumentException("SubjectCode/ReasonCode is empty");
338         }
339 
340         final String[] subjectCodeToken = subjectCode.split("\\.");
341         final String[] reasonCodeToken = reasonCode.split("\\.");
342 
343         if (subjectCodeToken.length > maxSupportedSection
344                 || reasonCodeToken.length > maxSupportedSection) {
345             throw new UnsupportedOperationException("Only three nested layer is supported.");
346         }
347 
348         int result = EuiccManager.OPERATION_SMDX_SUBJECT_REASON_CODE;
349 
350         // Pad the 0s needed for subject code
351         result = result << (maxSupportedSection - subjectCodeToken.length) * bitsPerSection;
352 
353         for (String digitString : subjectCodeToken) {
354             int num = Integer.parseInt(digitString);
355             if (num > maxSupportedDigit) {
356                 throw new UnsupportedOperationException("SubjectCode exceeds " + maxSupportedDigit);
357             }
358             result = (result << bitsPerSection) + num;
359         }
360 
361         // Pad the 0s needed for reason code
362         result = result << (maxSupportedSection - reasonCodeToken.length) * bitsPerSection;
363         for (String digitString : reasonCodeToken) {
364             int num = Integer.parseInt(digitString);
365             if (num > maxSupportedDigit) {
366                 throw new UnsupportedOperationException("ReasonCode exceeds " + maxSupportedDigit);
367             }
368             result = (result << bitsPerSection) + num;
369         }
370 
371         return result;
372     }
373 
374     @Override
375     @CallSuper
onCreate()376     public void onCreate() {
377         super.onCreate();
378         // We use a oneway AIDL interface to avoid blocking phone process binder threads on IPCs to
379         // an external process, but doing so means the requests are serialized by binder, which is
380         // not desired. Spin up a background thread pool to allow requests to be parallelized.
381         // TODO(b/38206971): Consider removing this if basic card-level functions like listing
382         // profiles are moved to the platform.
383         mExecutor = new ThreadPoolExecutor(
384                 4 /* corePoolSize */,
385                 4 /* maxPoolSize */,
386                 30, TimeUnit.SECONDS, /* keepAliveTime */
387                 new LinkedBlockingQueue<>(), /* workQueue */
388                 new ThreadFactory() {
389                     private final AtomicInteger mCount = new AtomicInteger(1);
390 
391                     @Override
392                     public Thread newThread(Runnable r) {
393                         return new Thread(r, "EuiccService #" + mCount.getAndIncrement());
394                     }
395                 }
396         );
397         mExecutor.allowCoreThreadTimeOut(true);
398     }
399 
400     @Override
401     @CallSuper
onDestroy()402     public void onDestroy() {
403         mExecutor.shutdownNow();
404         super.onDestroy();
405     }
406 
407     /**
408      * If overriding this method, call through to the super method for any unknown actions.
409      * {@inheritDoc}
410      */
411     @Override
412     @CallSuper
onBind(Intent intent)413     public IBinder onBind(Intent intent) {
414         return mStubWrapper;
415     }
416 
417     /**
418      * Callback class for {@link #onStartOtaIfNecessary(int, OtaStatusChangedCallback)}
419      *
420      * The status of OTA which can be {@code android.telephony.euicc.EuiccManager#EUICC_OTA_}
421      *
422      * @see IEuiccService#startOtaIfNecessary
423      */
424     public abstract static class OtaStatusChangedCallback {
425         /** Called when OTA status is changed. */
onOtaStatusChanged(int status)426         public abstract void onOtaStatusChanged(int status);
427     }
428 
429     /**
430      * Return the EID of the eUICC.
431      *
432      * @param slotId ID of the SIM slot being queried.
433      * @return the EID.
434      * @see android.telephony.euicc.EuiccManager#getEid
435      */
436     // TODO(b/36260308): Update doc when we have multi-SIM support.
onGetEid(int slotId)437     public abstract String onGetEid(int slotId);
438 
439     /**
440      * Return the status of OTA update.
441      *
442      * @param slotId ID of the SIM slot to use for the operation.
443      * @return The status of Euicc OTA update.
444      * @see android.telephony.euicc.EuiccManager#getOtaStatus
445      */
onGetOtaStatus(int slotId)446     public abstract @OtaStatus int onGetOtaStatus(int slotId);
447 
448     /**
449      * Perform OTA if current OS is not the latest one.
450      *
451      * @param slotId ID of the SIM slot to use for the operation.
452      * @param statusChangedCallback Function called when OTA status changed.
453      */
onStartOtaIfNecessary( int slotId, OtaStatusChangedCallback statusChangedCallback)454     public abstract void onStartOtaIfNecessary(
455             int slotId, OtaStatusChangedCallback statusChangedCallback);
456 
457     /**
458      * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription.
459      *
460      * @param slotId ID of the SIM slot to use for the operation.
461      * @param subscription A subscription whose metadata needs to be populated.
462      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
463      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
464      *     should be returned to allow the user to consent to this operation first.
465      * @return The result of the operation.
466      * @see android.telephony.euicc.EuiccManager#getDownloadableSubscriptionMetadata
467      */
onGetDownloadableSubscriptionMetadata( int slotId, DownloadableSubscription subscription, boolean forceDeactivateSim)468     public abstract GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(
469             int slotId, DownloadableSubscription subscription, boolean forceDeactivateSim);
470 
471     /**
472      * Return metadata for subscriptions which are available for download for this device.
473      *
474      * @param slotId ID of the SIM slot to use for the operation.
475      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
476      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
477      *     should be returned to allow the user to consent to this operation first.
478      * @return The result of the list operation.
479      * @see android.telephony.euicc.EuiccManager#getDefaultDownloadableSubscriptionList
480      */
481     public abstract GetDefaultDownloadableSubscriptionListResult
onGetDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim)482             onGetDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim);
483 
484     /**
485      * Download the given subscription.
486      *
487      * @param slotId ID of the SIM slot to use for the operation.
488      * @param subscription The subscription to download.
489      * @param switchAfterDownload If true, the subscription should be enabled upon successful
490      *     download.
491      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
492      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
493      *     should be returned to allow the user to consent to this operation first.
494      * @param resolvedBundle The bundle containing information on resolved errors. It can contain
495      *     a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE},
496      *     and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether
497      *     the user allows profile policy rules or not.
498      * @return a DownloadSubscriptionResult instance including a result code, a resolvable errors
499      *     bit map, and original the card Id. The result code may be one of the predefined
500      *     {@code RESULT_} constants or any implementation-specific code starting with
501      *     {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values
502      *     defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise,
503      *     this method does nothing and returns null by default.
504      * @see android.telephony.euicc.EuiccManager#downloadSubscription
505      */
onDownloadSubscription(int slotId, @NonNull DownloadableSubscription subscription, boolean switchAfterDownload, boolean forceDeactivateSim, @Nullable Bundle resolvedBundle)506     public DownloadSubscriptionResult onDownloadSubscription(int slotId,
507             @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
508             boolean forceDeactivateSim, @Nullable Bundle resolvedBundle) {
509         return null;
510     }
511 
512     /**
513      * Download the given subscription.
514      *
515      * @param slotId ID of the SIM slot to use for the operation.
516      * @param subscription The subscription to download.
517      * @param switchAfterDownload If true, the subscription should be enabled upon successful
518      *     download.
519      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
520      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
521      *     should be returned to allow the user to consent to this operation first.
522      * @return the result of the download operation. May be one of the predefined {@code RESULT_}
523      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
524      * @see android.telephony.euicc.EuiccManager#downloadSubscription
525      *
526      * @deprecated From Q, a subclass should use and override the above
527      * {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. The
528      * default return value for this one is Integer.MIN_VALUE.
529      */
onDownloadSubscription(int slotId, @NonNull DownloadableSubscription subscription, boolean switchAfterDownload, boolean forceDeactivateSim)530     @Deprecated public @Result int onDownloadSubscription(int slotId,
531             @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
532             boolean forceDeactivateSim) {
533         return Integer.MIN_VALUE;
534     }
535 
536     /**
537      * Return a list of all @link EuiccProfileInfo}s.
538      *
539      * @param slotId ID of the SIM slot to use for the operation.
540      * @return The result of the operation.
541      * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
542      * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
543      */
onGetEuiccProfileInfoList(int slotId)544     public abstract @NonNull GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId);
545 
546     /**
547      * Return info about the eUICC chip/device.
548      *
549      * @param slotId ID of the SIM slot to use for the operation.
550      * @return the {@link EuiccInfo} for the eUICC chip/device.
551      * @see android.telephony.euicc.EuiccManager#getEuiccInfo
552      */
onGetEuiccInfo(int slotId)553     public abstract @NonNull EuiccInfo onGetEuiccInfo(int slotId);
554 
555     /**
556      * Delete the given subscription.
557      *
558      * <p>If the subscription is currently active, it should be deactivated first (equivalent to a
559      * physical SIM being ejected).
560      *
561      * @param slotId ID of the SIM slot to use for the operation.
562      * @param iccid the ICCID of the subscription to delete.
563      * @return the result of the delete operation. May be one of the predefined {@code RESULT_}
564      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
565      * @see android.telephony.euicc.EuiccManager#deleteSubscription
566      */
onDeleteSubscription(int slotId, String iccid)567     public abstract @Result int onDeleteSubscription(int slotId, String iccid);
568 
569     /**
570      * Switch to the given subscription.
571      *
572      * @param slotId ID of the SIM slot to use for the operation.
573      * @param iccid the ICCID of the subscription to enable. May be null, in which case the current
574      *     profile should be deactivated and no profile should be activated to replace it - this is
575      *     equivalent to a physical SIM being ejected.
576      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
577      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
578      *     should be returned to allow the user to consent to this operation first.
579      * @return the result of the switch operation. May be one of the predefined {@code RESULT_}
580      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
581      * @see android.telephony.euicc.EuiccManager#switchToSubscription
582      */
onSwitchToSubscription(int slotId, @Nullable String iccid, boolean forceDeactivateSim)583     public abstract @Result int onSwitchToSubscription(int slotId, @Nullable String iccid,
584             boolean forceDeactivateSim);
585 
586     /**
587      * Update the nickname of the given subscription.
588      *
589      * @param slotId ID of the SIM slot to use for the operation.
590      * @param iccid the ICCID of the subscription to update.
591      * @param nickname the new nickname to apply.
592      * @return the result of the update operation. May be one of the predefined {@code RESULT_}
593      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
594      * @see android.telephony.euicc.EuiccManager#updateSubscriptionNickname
595      */
onUpdateSubscriptionNickname(int slotId, String iccid, String nickname)596     public abstract int onUpdateSubscriptionNickname(int slotId, String iccid,
597             String nickname);
598 
599     /**
600      * Erase all operational subscriptions on the device.
601      *
602      * <p>This is intended to be used for device resets. As such, the reset should be performed even
603      * if an active SIM must be deactivated in order to access the eUICC.
604      *
605      * @param slotId ID of the SIM slot to use for the operation.
606      * @return the result of the erase operation. May be one of the predefined {@code RESULT_}
607      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
608      * @see android.telephony.euicc.EuiccManager#eraseSubscriptions
609      *
610      * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
611      * and use {@link #onEraseSubscriptions(int, int)} instead
612      */
613     @Deprecated
onEraseSubscriptions(int slotId)614     public abstract int onEraseSubscriptions(int slotId);
615 
616     /**
617      * Erase specific subscriptions on the device.
618      *
619      * <p>This is intended to be used for device resets. As such, the reset should be performed even
620      * if an active SIM must be deactivated in order to access the eUICC.
621      *
622      * @param slotIndex index of the SIM slot to use for the operation.
623      * @param options flag for specific group of subscriptions to erase
624      * @return the result of the erase operation. May be one of the predefined {@code RESULT_}
625      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
626      * @see android.telephony.euicc.EuiccManager#eraseSubscriptionsWithOptions
627      */
onEraseSubscriptions(int slotIndex, @ResetOption int options)628     public int onEraseSubscriptions(int slotIndex, @ResetOption int options) {
629         throw new UnsupportedOperationException(
630                 "This method must be overridden to enable the ResetOption parameter");
631     }
632 
633     /**
634      * Ensure that subscriptions will be retained on the next factory reset.
635      *
636      * <p>Called directly before a factory reset. Assumes that a normal factory reset will lead to
637      * profiles being erased on first boot (to cover fastboot/recovery wipes), so the implementation
638      * should persist some bit that will remain accessible after the factory reset to bypass this
639      * flow when this method is called.
640      *
641      * @param slotId ID of the SIM slot to use for the operation.
642      * @return the result of the operation. May be one of the predefined {@code RESULT_} constants
643      *     or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
644      */
onRetainSubscriptionsForFactoryReset(int slotId)645     public abstract int onRetainSubscriptionsForFactoryReset(int slotId);
646 
647     /**
648      * Dump to a provided printWriter.
649      */
dump(@onNull PrintWriter printWriter)650     public void dump(@NonNull PrintWriter printWriter) {
651         printWriter.println("The connected LPA does not implement EuiccService#dump()");
652     }
653 
654     /**
655      * Wrapper around IEuiccService that forwards calls to implementations of {@link EuiccService}.
656      */
657     private class IEuiccServiceWrapper extends IEuiccService.Stub {
658         @Override
downloadSubscription(int slotId, DownloadableSubscription subscription, boolean switchAfterDownload, boolean forceDeactivateSim, Bundle resolvedBundle, IDownloadSubscriptionCallback callback)659         public void downloadSubscription(int slotId, DownloadableSubscription subscription,
660                 boolean switchAfterDownload, boolean forceDeactivateSim, Bundle resolvedBundle,
661                 IDownloadSubscriptionCallback callback) {
662             mExecutor.execute(new Runnable() {
663                 @Override
664                 public void run() {
665                     DownloadSubscriptionResult result;
666                     try {
667                         result =
668                             EuiccService.this.onDownloadSubscription(
669                                 slotId, subscription, switchAfterDownload, forceDeactivateSim,
670                                 resolvedBundle);
671                     } catch (AbstractMethodError e) {
672                         Log.w(TAG, "The new onDownloadSubscription(int, "
673                                 + "DownloadableSubscription, boolean, boolean, Bundle) is not "
674                                 + "implemented. Fall back to the old one.", e);
675                         int resultCode = EuiccService.this.onDownloadSubscription(
676                                 slotId, subscription, switchAfterDownload, forceDeactivateSim);
677                         result = new DownloadSubscriptionResult(resultCode,
678                             0 /* resolvableErrors */, TelephonyManager.UNSUPPORTED_CARD_ID);
679                     }
680                     try {
681                         callback.onComplete(result);
682                     } catch (RemoteException e) {
683                         // Can't communicate with the phone process; ignore.
684                     }
685                 }
686             });
687         }
688 
689         @Override
getEid(int slotId, IGetEidCallback callback)690         public void getEid(int slotId, IGetEidCallback callback) {
691             mExecutor.execute(new Runnable() {
692                 @Override
693                 public void run() {
694                     String eid = EuiccService.this.onGetEid(slotId);
695                     try {
696                         callback.onSuccess(eid);
697                     } catch (RemoteException e) {
698                         // Can't communicate with the phone process; ignore.
699                     }
700                 }
701             });
702         }
703 
704         @Override
startOtaIfNecessary( int slotId, IOtaStatusChangedCallback statusChangedCallback)705         public void startOtaIfNecessary(
706                 int slotId, IOtaStatusChangedCallback statusChangedCallback) {
707             mExecutor.execute(new Runnable() {
708                 @Override
709                 public void run() {
710                     EuiccService.this.onStartOtaIfNecessary(slotId, new OtaStatusChangedCallback() {
711                         @Override
712                         public void onOtaStatusChanged(int status) {
713                             try {
714                                 statusChangedCallback.onOtaStatusChanged(status);
715                             } catch (RemoteException e) {
716                                 // Can't communicate with the phone process; ignore.
717                             }
718                         }
719                     });
720                 }
721             });
722         }
723 
724         @Override
getOtaStatus(int slotId, IGetOtaStatusCallback callback)725         public void getOtaStatus(int slotId, IGetOtaStatusCallback callback) {
726             mExecutor.execute(new Runnable() {
727                 @Override
728                 public void run() {
729                     int status = EuiccService.this.onGetOtaStatus(slotId);
730                     try {
731                         callback.onSuccess(status);
732                     } catch (RemoteException e) {
733                         // Can't communicate with the phone process; ignore.
734                     }
735                 }
736             });
737         }
738 
739         @Override
getDownloadableSubscriptionMetadata(int slotId, DownloadableSubscription subscription, boolean forceDeactivateSim, IGetDownloadableSubscriptionMetadataCallback callback)740         public void getDownloadableSubscriptionMetadata(int slotId,
741                 DownloadableSubscription subscription,
742                 boolean forceDeactivateSim,
743                 IGetDownloadableSubscriptionMetadataCallback callback) {
744             mExecutor.execute(new Runnable() {
745                 @Override
746                 public void run() {
747                     GetDownloadableSubscriptionMetadataResult result =
748                             EuiccService.this.onGetDownloadableSubscriptionMetadata(
749                                     slotId, subscription, forceDeactivateSim);
750                     try {
751                         callback.onComplete(result);
752                     } catch (RemoteException e) {
753                         // Can't communicate with the phone process; ignore.
754                     }
755                 }
756             });
757         }
758 
759         @Override
getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim, IGetDefaultDownloadableSubscriptionListCallback callback)760         public void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim,
761                 IGetDefaultDownloadableSubscriptionListCallback callback) {
762             mExecutor.execute(new Runnable() {
763                 @Override
764                 public void run() {
765                     GetDefaultDownloadableSubscriptionListResult result =
766                             EuiccService.this.onGetDefaultDownloadableSubscriptionList(
767                                     slotId, forceDeactivateSim);
768                     try {
769                         callback.onComplete(result);
770                     } catch (RemoteException e) {
771                         // Can't communicate with the phone process; ignore.
772                     }
773                 }
774             });
775         }
776 
777         @Override
getEuiccProfileInfoList(int slotId, IGetEuiccProfileInfoListCallback callback)778         public void getEuiccProfileInfoList(int slotId, IGetEuiccProfileInfoListCallback callback) {
779             mExecutor.execute(new Runnable() {
780                 @Override
781                 public void run() {
782                     GetEuiccProfileInfoListResult result =
783                             EuiccService.this.onGetEuiccProfileInfoList(slotId);
784                     try {
785                         callback.onComplete(result);
786                     } catch (RemoteException e) {
787                         // Can't communicate with the phone process; ignore.
788                     }
789                 }
790             });
791         }
792 
793         @Override
getEuiccInfo(int slotId, IGetEuiccInfoCallback callback)794         public void getEuiccInfo(int slotId, IGetEuiccInfoCallback callback) {
795             mExecutor.execute(new Runnable() {
796                 @Override
797                 public void run() {
798                     EuiccInfo euiccInfo = EuiccService.this.onGetEuiccInfo(slotId);
799                     try {
800                         callback.onSuccess(euiccInfo);
801                     } catch (RemoteException e) {
802                         // Can't communicate with the phone process; ignore.
803                     }
804                 }
805             });
806 
807         }
808 
809         @Override
deleteSubscription(int slotId, String iccid, IDeleteSubscriptionCallback callback)810         public void deleteSubscription(int slotId, String iccid,
811                 IDeleteSubscriptionCallback callback) {
812             mExecutor.execute(new Runnable() {
813                 @Override
814                 public void run() {
815                     int result = EuiccService.this.onDeleteSubscription(slotId, iccid);
816                     try {
817                         callback.onComplete(result);
818                     } catch (RemoteException e) {
819                         // Can't communicate with the phone process; ignore.
820                     }
821                 }
822             });
823         }
824 
825         @Override
switchToSubscription(int slotId, String iccid, boolean forceDeactivateSim, ISwitchToSubscriptionCallback callback)826         public void switchToSubscription(int slotId, String iccid, boolean forceDeactivateSim,
827                 ISwitchToSubscriptionCallback callback) {
828             mExecutor.execute(new Runnable() {
829                 @Override
830                 public void run() {
831                     int result =
832                             EuiccService.this.onSwitchToSubscription(
833                                     slotId, iccid, forceDeactivateSim);
834                     try {
835                         callback.onComplete(result);
836                     } catch (RemoteException e) {
837                         // Can't communicate with the phone process; ignore.
838                     }
839                 }
840             });
841         }
842 
843         @Override
updateSubscriptionNickname(int slotId, String iccid, String nickname, IUpdateSubscriptionNicknameCallback callback)844         public void updateSubscriptionNickname(int slotId, String iccid, String nickname,
845                 IUpdateSubscriptionNicknameCallback callback) {
846             mExecutor.execute(new Runnable() {
847                 @Override
848                 public void run() {
849                     int result =
850                             EuiccService.this.onUpdateSubscriptionNickname(slotId, iccid, nickname);
851                     try {
852                         callback.onComplete(result);
853                     } catch (RemoteException e) {
854                         // Can't communicate with the phone process; ignore.
855                     }
856                 }
857             });
858         }
859 
860         @Override
eraseSubscriptions(int slotId, IEraseSubscriptionsCallback callback)861         public void eraseSubscriptions(int slotId, IEraseSubscriptionsCallback callback) {
862             mExecutor.execute(new Runnable() {
863                 @Override
864                 public void run() {
865                     int result = EuiccService.this.onEraseSubscriptions(slotId);
866                     try {
867                         callback.onComplete(result);
868                     } catch (RemoteException e) {
869                         // Can't communicate with the phone process; ignore.
870                     }
871                 }
872             });
873         }
874 
875         @Override
eraseSubscriptionsWithOptions( int slotIndex, @ResetOption int options, IEraseSubscriptionsCallback callback)876         public void eraseSubscriptionsWithOptions(
877                 int slotIndex, @ResetOption int options, IEraseSubscriptionsCallback callback) {
878             mExecutor.execute(new Runnable() {
879                 @Override
880                 public void run() {
881                     int result = EuiccService.this.onEraseSubscriptions(slotIndex, options);
882                     try {
883                         callback.onComplete(result);
884                     } catch (RemoteException e) {
885                         // Can't communicate with the phone process; ignore.
886                     }
887                 }
888             });
889         }
890 
891         @Override
retainSubscriptionsForFactoryReset(int slotId, IRetainSubscriptionsForFactoryResetCallback callback)892         public void retainSubscriptionsForFactoryReset(int slotId,
893                 IRetainSubscriptionsForFactoryResetCallback callback) {
894             mExecutor.execute(new Runnable() {
895                 @Override
896                 public void run() {
897                     int result = EuiccService.this.onRetainSubscriptionsForFactoryReset(slotId);
898                     try {
899                         callback.onComplete(result);
900                     } catch (RemoteException e) {
901                         // Can't communicate with the phone process; ignore.
902                     }
903                 }
904             });
905         }
906 
907         @Override
dump(IEuiccServiceDumpResultCallback callback)908         public void dump(IEuiccServiceDumpResultCallback callback) throws RemoteException {
909             mExecutor.execute(new Runnable() {
910                 @Override
911                 public void run() {
912                     try {
913                         final StringWriter sw = new StringWriter();
914                         final PrintWriter pw = new PrintWriter(sw);
915                         EuiccService.this.dump(pw);
916                         callback.onComplete(sw.toString());
917                     } catch (RemoteException e) {
918                         // Can't communicate with the phone process; ignore.
919                     }
920                 }
921             });
922         }
923     }
924 }
925