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