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