Home | History | Annotate | Download | only in euicc
      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 android.annotation.CallSuper;
     19 import android.annotation.Nullable;
     20 import android.app.Service;
     21 import android.content.Intent;
     22 import android.os.IBinder;
     23 import android.os.RemoteException;
     24 import android.telephony.euicc.DownloadableSubscription;
     25 import android.telephony.euicc.EuiccInfo;
     26 import android.util.ArraySet;
     27 
     28 import java.util.concurrent.LinkedBlockingQueue;
     29 import java.util.concurrent.ThreadFactory;
     30 import java.util.concurrent.ThreadPoolExecutor;
     31 import java.util.concurrent.TimeUnit;
     32 import java.util.concurrent.atomic.AtomicInteger;
     33 
     34 /**
     35  * Service interface linking the system with an eUICC local profile assistant (LPA) application.
     36  *
     37  * <p>An LPA consists of two separate components (which may both be implemented in the same APK):
     38  * the LPA backend, and the LPA UI or LUI.
     39  *
     40  * <p>To implement the LPA backend, you must extend this class and declare this service in your
     41  * manifest file. The service must require the
     42  * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter
     43  * with the {@link #EUICC_SERVICE_INTERFACE} action. The priority of the intent filter must be set
     44  * to a non-zero value in case multiple implementations are present on the device. For example:
     45  *
     46  * <pre>{@code
     47  * <service android:name=".MyEuiccService"
     48  *          android:permission="android.permission.BIND_EUICC_SERVICE">
     49  *     <intent-filter android:priority="100">
     50  *         <action android:name="android.service.euicc.EuiccService" />
     51  *     </intent-filter>
     52  * </service>
     53  * }</pre>
     54  *
     55  * <p>To implement the LUI, you must provide an activity for the following actions:
     56  *
     57  * <ul>
     58  * <li>{@link #ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS}
     59  * <li>{@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION}
     60  * </ul>
     61  *
     62  * <p>As with the service, each activity must require the
     63  * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission. Each should have an intent
     64  * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
     65  * priority.
     66  *
     67  * TODO(b/35851809): Make this a SystemApi.
     68  * @hide
     69  */
     70 public abstract class EuiccService extends Service {
     71     /** Action which must be included in this service's intent filter. */
     72     public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
     73 
     74     /** Category which must be defined to all UI actions, for efficient lookup. */
     75     public static final String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
     76 
     77     // LUI actions. These are passthroughs of the corresponding EuiccManager actions.
     78 
     79     /** @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS */
     80     public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
     81             "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
     82     /** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
     83     public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
     84             "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
     85 
     86     // LUI resolution actions. These are called by the platform to resolve errors in situations that
     87     // require user interaction.
     88     // TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
     89     // more scoped out.
     90     /** Alert the user that this action will result in an active SIM being deactivated. */
     91     public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
     92             "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
     93     /**
     94      * Alert the user about a download/switch being done for an app that doesn't currently have
     95      * carrier privileges.
     96      */
     97     public static final String ACTION_RESOLVE_NO_PRIVILEGES =
     98             "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
     99 
    100     /** Intent extra set for resolution requests containing the package name of the calling app. */
    101     public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
    102             "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
    103 
    104     /** Result code for a successful operation. */
    105     public static final int RESULT_OK = 0;
    106     /** Result code indicating that an active SIM must be deactivated to perform the operation. */
    107     public static final int RESULT_MUST_DEACTIVATE_SIM = -1;
    108     // New predefined codes should have negative values.
    109 
    110     /** Start of implementation-specific error results. */
    111     public static final int RESULT_FIRST_USER = 1;
    112 
    113     /**
    114      * List of all valid resolution actions for validation purposes.
    115      * @hide
    116      */
    117     public static final ArraySet<String> RESOLUTION_ACTIONS;
    118     static {
    119         RESOLUTION_ACTIONS = new ArraySet<>();
    120         RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
    121         RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
    122     }
    123 
    124     /** Boolean extra for resolution actions indicating whether the user granted consent. */
    125     public static final String RESOLUTION_EXTRA_CONSENT = "consent";
    126 
    127     private final IEuiccService.Stub mStubWrapper;
    128 
    129     private ThreadPoolExecutor mExecutor;
    130 
    131     public EuiccService() {
    132         mStubWrapper = new IEuiccServiceWrapper();
    133     }
    134 
    135     @Override
    136     @CallSuper
    137     public void onCreate() {
    138         super.onCreate();
    139         // We use a oneway AIDL interface to avoid blocking phone process binder threads on IPCs to
    140         // an external process, but doing so means the requests are serialized by binder, which is
    141         // not desired. Spin up a background thread pool to allow requests to be parallelized.
    142         // TODO(b/38206971): Consider removing this if basic card-level functions like listing
    143         // profiles are moved to the platform.
    144         mExecutor = new ThreadPoolExecutor(
    145                 4 /* corePoolSize */,
    146                 4 /* maxPoolSize */,
    147                 30, TimeUnit.SECONDS, /* keepAliveTime */
    148                 new LinkedBlockingQueue<>(), /* workQueue */
    149                 new ThreadFactory() {
    150                     private final AtomicInteger mCount = new AtomicInteger(1);
    151 
    152                     @Override
    153                     public Thread newThread(Runnable r) {
    154                         return new Thread(r, "EuiccService #" + mCount.getAndIncrement());
    155                     }
    156                 }
    157         );
    158         mExecutor.allowCoreThreadTimeOut(true);
    159     }
    160 
    161     @Override
    162     @CallSuper
    163     public void onDestroy() {
    164         mExecutor.shutdownNow();
    165         super.onDestroy();
    166     }
    167 
    168     /**
    169      * If overriding this method, call through to the super method for any unknown actions.
    170      * {@inheritDoc}
    171      */
    172     @Override
    173     @CallSuper
    174     public IBinder onBind(Intent intent) {
    175         return mStubWrapper;
    176     }
    177 
    178     /**
    179      * Return the EID of the eUICC.
    180      *
    181      * @param slotId ID of the SIM slot being queried. This is currently not populated but is here
    182      *     to future-proof the APIs.
    183      * @return the EID.
    184      * @see android.telephony.euicc.EuiccManager#getEid
    185      */
    186     // TODO(b/36260308): Update doc when we have multi-SIM support.
    187     public abstract String onGetEid(int slotId);
    188 
    189     /**
    190      * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription.
    191      *
    192      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    193      *     but is here to future-proof the APIs.
    194      * @param subscription A subscription whose metadata needs to be populated.
    195      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
    196      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
    197      *     should be returned to allow the user to consent to this operation first.
    198      * @return The result of the operation.
    199      * @see android.telephony.euicc.EuiccManager#getDownloadableSubscriptionMetadata
    200      */
    201     public abstract GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(
    202             int slotId, DownloadableSubscription subscription, boolean forceDeactivateSim);
    203 
    204     /**
    205      * Return metadata for subscriptions which are available for download for this device.
    206      *
    207      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    208      *     but is here to future-proof the APIs.
    209      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
    210      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
    211      *     should be returned to allow the user to consent to this operation first.
    212      * @return The result of the list operation.
    213      * @see android.telephony.euicc.EuiccManager#getDefaultDownloadableSubscriptionList
    214      */
    215     public abstract GetDefaultDownloadableSubscriptionListResult
    216             onGetDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim);
    217 
    218     /**
    219      * Download the given subscription.
    220      *
    221      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    222      *     but is here to future-proof the APIs.
    223      * @param subscription The subscription to download.
    224      * @param switchAfterDownload If true, the subscription should be enabled upon successful
    225      *     download.
    226      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
    227      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
    228      *     should be returned to allow the user to consent to this operation first.
    229      * @return the result of the download operation. May be one of the predefined {@code RESULT_}
    230      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
    231      * @see android.telephony.euicc.EuiccManager#downloadSubscription
    232      */
    233     public abstract int onDownloadSubscription(int slotId,
    234             DownloadableSubscription subscription, boolean switchAfterDownload,
    235             boolean forceDeactivateSim);
    236 
    237     /**
    238      * Return a list of all @link EuiccProfileInfo}s.
    239      *
    240      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    241      *     but is here to future-proof the APIs.
    242      * @return The result of the operation.
    243      * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
    244      * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
    245      */
    246     public abstract GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId);
    247 
    248     /**
    249      * Return info about the eUICC chip/device.
    250      *
    251      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    252      *     but is here to future-proof the APIs.
    253      * @return the {@link EuiccInfo} for the eUICC chip/device.
    254      * @see android.telephony.euicc.EuiccManager#getEuiccInfo
    255      */
    256     public abstract EuiccInfo onGetEuiccInfo(int slotId);
    257 
    258     /**
    259      * Delete the given subscription.
    260      *
    261      * <p>If the subscription is currently active, it should be deactivated first (equivalent to a
    262      * physical SIM being ejected).
    263      *
    264      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    265      *     but is here to future-proof the APIs.
    266      * @param iccid the ICCID of the subscription to delete.
    267      * @return the result of the delete operation. May be one of the predefined {@code RESULT_}
    268      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
    269      * @see android.telephony.euicc.EuiccManager#deleteSubscription
    270      */
    271     public abstract int onDeleteSubscription(int slotId, String iccid);
    272 
    273     /**
    274      * Switch to the given subscription.
    275      *
    276      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    277      *     but is here to future-proof the APIs.
    278      * @param iccid the ICCID of the subscription to enable. May be null, in which case the current
    279      *     profile should be deactivated and no profile should be activated to replace it - this is
    280      *     equivalent to a physical SIM being ejected.
    281      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
    282      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
    283      *     should be returned to allow the user to consent to this operation first.
    284      * @return the result of the switch operation. May be one of the predefined {@code RESULT_}
    285      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
    286      * @see android.telephony.euicc.EuiccManager#switchToSubscription
    287      */
    288     public abstract int onSwitchToSubscription(int slotId, @Nullable String iccid,
    289             boolean forceDeactivateSim);
    290 
    291     /**
    292      * Update the nickname of the given subscription.
    293      *
    294      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    295      *     but is here to future-proof the APIs.
    296      * @param iccid the ICCID of the subscription to update.
    297      * @param nickname the new nickname to apply.
    298      * @return the result of the update operation. May be one of the predefined {@code RESULT_}
    299      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
    300      * @see android.telephony.euicc.EuiccManager#updateSubscriptionNickname
    301      */
    302     public abstract int onUpdateSubscriptionNickname(int slotId, String iccid,
    303             String nickname);
    304 
    305     /**
    306      * Erase all of the subscriptions on the device.
    307      *
    308      * <p>This is intended to be used for device resets. As such, the reset should be performed even
    309      * if an active SIM must be deactivated in order to access the eUICC.
    310      *
    311      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    312      *     but is here to future-proof the APIs.
    313      * @return the result of the erase operation. May be one of the predefined {@code RESULT_}
    314      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
    315      * @see android.telephony.euicc.EuiccManager#eraseSubscriptions
    316      */
    317     public abstract int onEraseSubscriptions(int slotId);
    318 
    319     /**
    320      * Ensure that subscriptions will be retained on the next factory reset.
    321      *
    322      * <p>Called directly before a factory reset. Assumes that a normal factory reset will lead to
    323      * profiles being erased on first boot (to cover fastboot/recovery wipes), so the implementation
    324      * should persist some bit that will remain accessible after the factory reset to bypass this
    325      * flow when this method is called.
    326      *
    327      * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
    328      *     but is here to future-proof the APIs.
    329      * @return the result of the operation. May be one of the predefined {@code RESULT_} constants
    330      *     or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
    331      */
    332     public abstract int onRetainSubscriptionsForFactoryReset(int slotId);
    333 
    334     /**
    335      * Wrapper around IEuiccService that forwards calls to implementations of {@link EuiccService}.
    336      */
    337     private class IEuiccServiceWrapper extends IEuiccService.Stub {
    338         @Override
    339         public void downloadSubscription(int slotId, DownloadableSubscription subscription,
    340                 boolean switchAfterDownload, boolean forceDeactivateSim,
    341                 IDownloadSubscriptionCallback callback) {
    342             mExecutor.execute(new Runnable() {
    343                 @Override
    344                 public void run() {
    345                     int result = EuiccService.this.onDownloadSubscription(
    346                             slotId, subscription, switchAfterDownload, forceDeactivateSim);
    347                     try {
    348                         callback.onComplete(result);
    349                     } catch (RemoteException e) {
    350                         // Can't communicate with the phone process; ignore.
    351                     }
    352                 }
    353             });
    354         }
    355 
    356         @Override
    357         public void getEid(int slotId, IGetEidCallback callback) {
    358             mExecutor.execute(new Runnable() {
    359                 @Override
    360                 public void run() {
    361                     String eid = EuiccService.this.onGetEid(slotId);
    362                     try {
    363                         callback.onSuccess(eid);
    364                     } catch (RemoteException e) {
    365                         // Can't communicate with the phone process; ignore.
    366                     }
    367                 }
    368             });
    369         }
    370 
    371         @Override
    372         public void getDownloadableSubscriptionMetadata(int slotId,
    373                 DownloadableSubscription subscription,
    374                 boolean forceDeactivateSim,
    375                 IGetDownloadableSubscriptionMetadataCallback callback) {
    376             mExecutor.execute(new Runnable() {
    377                 @Override
    378                 public void run() {
    379                     GetDownloadableSubscriptionMetadataResult result =
    380                             EuiccService.this.onGetDownloadableSubscriptionMetadata(
    381                                     slotId, subscription, forceDeactivateSim);
    382                     try {
    383                         callback.onComplete(result);
    384                     } catch (RemoteException e) {
    385                         // Can't communicate with the phone process; ignore.
    386                     }
    387                 }
    388             });
    389         }
    390 
    391         @Override
    392         public void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim,
    393                 IGetDefaultDownloadableSubscriptionListCallback callback) {
    394             mExecutor.execute(new Runnable() {
    395                 @Override
    396                 public void run() {
    397                     GetDefaultDownloadableSubscriptionListResult result =
    398                             EuiccService.this.onGetDefaultDownloadableSubscriptionList(
    399                                     slotId, forceDeactivateSim);
    400                     try {
    401                         callback.onComplete(result);
    402                     } catch (RemoteException e) {
    403                         // Can't communicate with the phone process; ignore.
    404                     }
    405                 }
    406             });
    407         }
    408 
    409         @Override
    410         public void getEuiccProfileInfoList(int slotId, IGetEuiccProfileInfoListCallback callback) {
    411             mExecutor.execute(new Runnable() {
    412                 @Override
    413                 public void run() {
    414                     GetEuiccProfileInfoListResult result =
    415                             EuiccService.this.onGetEuiccProfileInfoList(slotId);
    416                     try {
    417                         callback.onComplete(result);
    418                     } catch (RemoteException e) {
    419                         // Can't communicate with the phone process; ignore.
    420                     }
    421                 }
    422             });
    423         }
    424 
    425         @Override
    426         public void getEuiccInfo(int slotId, IGetEuiccInfoCallback callback) {
    427             mExecutor.execute(new Runnable() {
    428                 @Override
    429                 public void run() {
    430                     EuiccInfo euiccInfo = EuiccService.this.onGetEuiccInfo(slotId);
    431                     try {
    432                         callback.onSuccess(euiccInfo);
    433                     } catch (RemoteException e) {
    434                         // Can't communicate with the phone process; ignore.
    435                     }
    436                 }
    437             });
    438 
    439         }
    440 
    441         @Override
    442         public void deleteSubscription(int slotId, String iccid,
    443                 IDeleteSubscriptionCallback callback) {
    444             mExecutor.execute(new Runnable() {
    445                 @Override
    446                 public void run() {
    447                     int result = EuiccService.this.onDeleteSubscription(slotId, iccid);
    448                     try {
    449                         callback.onComplete(result);
    450                     } catch (RemoteException e) {
    451                         // Can't communicate with the phone process; ignore.
    452                     }
    453                 }
    454             });
    455         }
    456 
    457         @Override
    458         public void switchToSubscription(int slotId, String iccid, boolean forceDeactivateSim,
    459                 ISwitchToSubscriptionCallback callback) {
    460             mExecutor.execute(new Runnable() {
    461                 @Override
    462                 public void run() {
    463                     int result =
    464                             EuiccService.this.onSwitchToSubscription(
    465                                     slotId, iccid, forceDeactivateSim);
    466                     try {
    467                         callback.onComplete(result);
    468                     } catch (RemoteException e) {
    469                         // Can't communicate with the phone process; ignore.
    470                     }
    471                 }
    472             });
    473         }
    474 
    475         @Override
    476         public void updateSubscriptionNickname(int slotId, String iccid, String nickname,
    477                 IUpdateSubscriptionNicknameCallback callback) {
    478             mExecutor.execute(new Runnable() {
    479                 @Override
    480                 public void run() {
    481                     int result =
    482                             EuiccService.this.onUpdateSubscriptionNickname(slotId, iccid, nickname);
    483                     try {
    484                         callback.onComplete(result);
    485                     } catch (RemoteException e) {
    486                         // Can't communicate with the phone process; ignore.
    487                     }
    488                 }
    489             });
    490         }
    491 
    492         @Override
    493         public void eraseSubscriptions(int slotId, IEraseSubscriptionsCallback callback) {
    494             mExecutor.execute(new Runnable() {
    495                 @Override
    496                 public void run() {
    497                     int result = EuiccService.this.onEraseSubscriptions(slotId);
    498                     try {
    499                         callback.onComplete(result);
    500                     } catch (RemoteException e) {
    501                         // Can't communicate with the phone process; ignore.
    502                     }
    503                 }
    504             });
    505         }
    506 
    507         @Override
    508         public void retainSubscriptionsForFactoryReset(int slotId,
    509                 IRetainSubscriptionsForFactoryResetCallback callback) {
    510             mExecutor.execute(new Runnable() {
    511                 @Override
    512                 public void run() {
    513                     int result = EuiccService.this.onRetainSubscriptionsForFactoryReset(slotId);
    514                     try {
    515                         callback.onComplete(result);
    516                     } catch (RemoteException e) {
    517                         // Can't communicate with the phone process; ignore.
    518                     }
    519                 }
    520             });
    521         }
    522     }
    523 }
    524