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