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