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 com.android.internal.telephony.euicc;
     17 
     18 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
     19 
     20 import android.Manifest;
     21 import android.annotation.Nullable;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.ServiceConnection;
     28 import android.content.pm.ActivityInfo;
     29 import android.content.pm.ComponentInfo;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.ResolveInfo;
     32 import android.content.pm.ServiceInfo;
     33 import android.os.IBinder;
     34 import android.os.Looper;
     35 import android.os.Message;
     36 import android.os.RemoteException;
     37 import android.service.euicc.EuiccService;
     38 import android.service.euicc.GetDefaultDownloadableSubscriptionListResult;
     39 import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
     40 import android.service.euicc.GetEuiccProfileInfoListResult;
     41 import android.service.euicc.IDeleteSubscriptionCallback;
     42 import android.service.euicc.IDownloadSubscriptionCallback;
     43 import android.service.euicc.IEraseSubscriptionsCallback;
     44 import android.service.euicc.IEuiccService;
     45 import android.service.euicc.IGetDefaultDownloadableSubscriptionListCallback;
     46 import android.service.euicc.IGetDownloadableSubscriptionMetadataCallback;
     47 import android.service.euicc.IGetEidCallback;
     48 import android.service.euicc.IGetEuiccInfoCallback;
     49 import android.service.euicc.IGetEuiccProfileInfoListCallback;
     50 import android.service.euicc.IGetOtaStatusCallback;
     51 import android.service.euicc.IOtaStatusChangedCallback;
     52 import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
     53 import android.service.euicc.ISwitchToSubscriptionCallback;
     54 import android.service.euicc.IUpdateSubscriptionNicknameCallback;
     55 import android.telephony.SubscriptionManager;
     56 import android.telephony.euicc.DownloadableSubscription;
     57 import android.telephony.euicc.EuiccInfo;
     58 import android.telephony.euicc.EuiccManager;
     59 import android.telephony.euicc.EuiccManager.OtaStatus;
     60 import android.text.TextUtils;
     61 import android.util.ArraySet;
     62 import android.util.Log;
     63 
     64 import com.android.internal.annotations.VisibleForTesting;
     65 import com.android.internal.content.PackageMonitor;
     66 import com.android.internal.util.IState;
     67 import com.android.internal.util.State;
     68 import com.android.internal.util.StateMachine;
     69 
     70 import java.io.FileDescriptor;
     71 import java.io.PrintWriter;
     72 import java.util.List;
     73 import java.util.Objects;
     74 import java.util.Set;
     75 
     76 /**
     77  * State machine which maintains the binding to the EuiccService implementation and issues commands.
     78  *
     79  * <p>Keeps track of the highest-priority EuiccService implementation to use. When a command comes
     80  * in, brings up a binding to that service, issues the command, and lingers the binding as long as
     81  * more commands are coming in. The binding is dropped after an idle timeout.
     82  */
     83 public class EuiccConnector extends StateMachine implements ServiceConnection {
     84     private static final String TAG = "EuiccConnector";
     85 
     86     /**
     87      * Maximum amount of time to wait for a connection to be established after bindService returns
     88      * true or onServiceDisconnected is called (and no package change has occurred which should
     89      * force us to reestablish the binding).
     90      */
     91     private static final int BIND_TIMEOUT_MILLIS = 30000;
     92 
     93     /**
     94      * Maximum amount of idle time to hold the binding while in {@link ConnectedState}. After this,
     95      * the binding is dropped to free up memory as the EuiccService is not expected to be used
     96      * frequently as part of ongoing device operation.
     97      */
     98     @VisibleForTesting
     99     static final int LINGER_TIMEOUT_MILLIS = 60000;
    100 
    101     /**
    102      * Command indicating that a package change has occurred.
    103      *
    104      * <p>{@link Message#obj} is an optional package name. If set, this package has changed in a
    105      * way that will permanently sever any open bindings, and if we're bound to it, the binding must
    106      * be forcefully reestablished.
    107      */
    108     private static final int CMD_PACKAGE_CHANGE = 1;
    109     /** Command indicating that {@link #BIND_TIMEOUT_MILLIS} has been reached. */
    110     private static final int CMD_CONNECT_TIMEOUT = 2;
    111     /** Command indicating that {@link #LINGER_TIMEOUT_MILLIS} has been reached. */
    112     private static final int CMD_LINGER_TIMEOUT = 3;
    113     /**
    114      * Command indicating that the service has connected.
    115      *
    116      * <p>{@link Message#obj} is the connected {@link IEuiccService} implementation.
    117      */
    118     private static final int CMD_SERVICE_CONNECTED = 4;
    119     /** Command indicating that the service has disconnected. */
    120     private static final int CMD_SERVICE_DISCONNECTED = 5;
    121     /**
    122      * Command indicating that a command has completed and the callback should be executed.
    123      *
    124      * <p>{@link Message#obj} is a {@link Runnable} which will trigger the callback.
    125      */
    126     private static final int CMD_COMMAND_COMPLETE = 6;
    127 
    128     // Commands corresponding with EuiccService APIs. Keep isEuiccCommand in sync with any changes.
    129     private static final int CMD_GET_EID = 100;
    130     private static final int CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA = 101;
    131     private static final int CMD_DOWNLOAD_SUBSCRIPTION = 102;
    132     private static final int CMD_GET_EUICC_PROFILE_INFO_LIST = 103;
    133     private static final int CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST = 104;
    134     private static final int CMD_GET_EUICC_INFO = 105;
    135     private static final int CMD_DELETE_SUBSCRIPTION = 106;
    136     private static final int CMD_SWITCH_TO_SUBSCRIPTION = 107;
    137     private static final int CMD_UPDATE_SUBSCRIPTION_NICKNAME = 108;
    138     private static final int CMD_ERASE_SUBSCRIPTIONS = 109;
    139     private static final int CMD_RETAIN_SUBSCRIPTIONS = 110;
    140     private static final int CMD_GET_OTA_STATUS = 111;
    141     private static final int CMD_START_OTA_IF_NECESSARY = 112;
    142 
    143     private static boolean isEuiccCommand(int what) {
    144         return what >= CMD_GET_EID;
    145     }
    146 
    147     /** Flags to use when querying PackageManager for Euicc component implementations. */
    148     private static final int EUICC_QUERY_FLAGS =
    149             PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
    150                     | PackageManager.GET_RESOLVED_FILTER;
    151 
    152     /**
    153      * Return the activity info of the activity to start for the given intent, or null if none
    154      * was found.
    155      */
    156     public static ActivityInfo findBestActivity(PackageManager packageManager, Intent intent) {
    157         List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent,
    158                 EUICC_QUERY_FLAGS);
    159         ActivityInfo bestComponent =
    160                 (ActivityInfo) findBestComponent(packageManager, resolveInfoList);
    161         if (bestComponent == null) {
    162             Log.w(TAG, "No valid component found for intent: " + intent);
    163         }
    164         return bestComponent;
    165     }
    166 
    167     /**
    168      * Return the component info of the EuiccService to bind to, or null if none were found.
    169      */
    170     public static ComponentInfo findBestComponent(PackageManager packageManager) {
    171         Intent intent = new Intent(EuiccService.EUICC_SERVICE_INTERFACE);
    172         List<ResolveInfo> resolveInfoList =
    173                 packageManager.queryIntentServices(intent, EUICC_QUERY_FLAGS);
    174         ComponentInfo bestComponent = findBestComponent(packageManager, resolveInfoList);
    175         if (bestComponent == null) {
    176             Log.w(TAG, "No valid EuiccService implementation found");
    177         }
    178         return bestComponent;
    179     }
    180 
    181     /** Base class for all command callbacks. */
    182     @VisibleForTesting(visibility = PACKAGE)
    183     public interface BaseEuiccCommandCallback {
    184         /** Called when a command fails because the service is or became unavailable. */
    185         void onEuiccServiceUnavailable();
    186     }
    187 
    188     /** Callback class for {@link #getEid}. */
    189     @VisibleForTesting(visibility = PACKAGE)
    190     public interface GetEidCommandCallback extends BaseEuiccCommandCallback {
    191         /** Called when the EID lookup has completed. */
    192         void onGetEidComplete(String eid);
    193     }
    194 
    195     /** Callback class for {@link #getOtaStatus}. */
    196     @VisibleForTesting(visibility = PACKAGE)
    197     public interface GetOtaStatusCommandCallback extends BaseEuiccCommandCallback {
    198         /** Called when the getting OTA status lookup has completed. */
    199         void onGetOtaStatusComplete(@OtaStatus int status);
    200     }
    201 
    202     /** Callback class for {@link #startOtaIfNecessary}. */
    203     @VisibleForTesting(visibility = PACKAGE)
    204     public interface OtaStatusChangedCallback extends BaseEuiccCommandCallback {
    205         /**
    206          * Called when OTA status is changed to {@link EuiccM}. */
    207         void onOtaStatusChanged(int status);
    208     }
    209 
    210     static class GetMetadataRequest {
    211         DownloadableSubscription mSubscription;
    212         boolean mForceDeactivateSim;
    213         GetMetadataCommandCallback mCallback;
    214     }
    215 
    216     /** Callback class for {@link #getDownloadableSubscriptionMetadata}. */
    217     @VisibleForTesting(visibility = PACKAGE)
    218     public interface GetMetadataCommandCallback extends BaseEuiccCommandCallback {
    219         /** Called when the metadata lookup has completed (though it may have failed). */
    220         void onGetMetadataComplete(GetDownloadableSubscriptionMetadataResult result);
    221     }
    222 
    223     static class DownloadRequest {
    224         DownloadableSubscription mSubscription;
    225         boolean mSwitchAfterDownload;
    226         boolean mForceDeactivateSim;
    227         DownloadCommandCallback mCallback;
    228     }
    229 
    230     /** Callback class for {@link #downloadSubscription}. */
    231     @VisibleForTesting(visibility = PACKAGE)
    232     public interface DownloadCommandCallback extends BaseEuiccCommandCallback {
    233         /** Called when the download has completed (though it may have failed). */
    234         void onDownloadComplete(int result);
    235     }
    236 
    237     interface GetEuiccProfileInfoListCommandCallback extends BaseEuiccCommandCallback {
    238         /** Called when the list has completed (though it may have failed). */
    239         void onListComplete(GetEuiccProfileInfoListResult result);
    240     }
    241 
    242     static class GetDefaultListRequest {
    243         boolean mForceDeactivateSim;
    244         GetDefaultListCommandCallback mCallback;
    245     }
    246 
    247     /** Callback class for {@link #getDefaultDownloadableSubscriptionList}. */
    248     @VisibleForTesting(visibility = PACKAGE)
    249     public interface GetDefaultListCommandCallback extends BaseEuiccCommandCallback {
    250         /** Called when the list has completed (though it may have failed). */
    251         void onGetDefaultListComplete(GetDefaultDownloadableSubscriptionListResult result);
    252     }
    253 
    254     /** Callback class for {@link #getEuiccInfo}. */
    255     @VisibleForTesting(visibility = PACKAGE)
    256     public interface GetEuiccInfoCommandCallback extends BaseEuiccCommandCallback {
    257         /** Called when the EuiccInfo lookup has completed. */
    258         void onGetEuiccInfoComplete(EuiccInfo euiccInfo);
    259     }
    260 
    261     static class DeleteRequest {
    262         String mIccid;
    263         DeleteCommandCallback mCallback;
    264     }
    265 
    266     /** Callback class for {@link #deleteSubscription}. */
    267     @VisibleForTesting(visibility = PACKAGE)
    268     public interface DeleteCommandCallback extends BaseEuiccCommandCallback {
    269         /** Called when the delete has completed (though it may have failed). */
    270         void onDeleteComplete(int result);
    271     }
    272 
    273     static class SwitchRequest {
    274         @Nullable String mIccid;
    275         boolean mForceDeactivateSim;
    276         SwitchCommandCallback mCallback;
    277     }
    278 
    279     /** Callback class for {@link #switchToSubscription}. */
    280     @VisibleForTesting(visibility = PACKAGE)
    281     public interface SwitchCommandCallback extends BaseEuiccCommandCallback {
    282         /** Called when the switch has completed (though it may have failed). */
    283         void onSwitchComplete(int result);
    284     }
    285 
    286     static class UpdateNicknameRequest {
    287         String mIccid;
    288         String mNickname;
    289         UpdateNicknameCommandCallback mCallback;
    290     }
    291 
    292     /** Callback class for {@link #updateSubscriptionNickname}. */
    293     @VisibleForTesting(visibility = PACKAGE)
    294     public interface UpdateNicknameCommandCallback extends BaseEuiccCommandCallback {
    295         /** Called when the update has completed (though it may have failed). */
    296         void onUpdateNicknameComplete(int result);
    297     }
    298 
    299     /** Callback class for {@link #eraseSubscriptions}. */
    300     @VisibleForTesting(visibility = PACKAGE)
    301     public interface EraseCommandCallback extends BaseEuiccCommandCallback {
    302         /** Called when the erase has completed (though it may have failed). */
    303         void onEraseComplete(int result);
    304     }
    305 
    306     /** Callback class for {@link #retainSubscriptions}. */
    307     @VisibleForTesting(visibility = PACKAGE)
    308     public interface RetainSubscriptionsCommandCallback extends BaseEuiccCommandCallback {
    309         /** Called when the retain command has completed (though it may have failed). */
    310         void onRetainSubscriptionsComplete(int result);
    311     }
    312 
    313     private Context mContext;
    314     private PackageManager mPm;
    315 
    316     private final PackageMonitor mPackageMonitor = new EuiccPackageMonitor();
    317     private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
    318         @Override
    319         public void onReceive(Context context, Intent intent) {
    320             if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
    321                 // On user unlock, new components might become available, so rebind if needed. This
    322                 // can never make a component unavailable so there's never a need to force a
    323                 // rebind.
    324                 sendMessage(CMD_PACKAGE_CHANGE);
    325             }
    326         }
    327     };
    328 
    329     /** Set to the current component we should bind to except in {@link UnavailableState}. */
    330     private @Nullable ServiceInfo mSelectedComponent;
    331 
    332     /** Set to the currently connected EuiccService implementation in {@link ConnectedState}. */
    333     private @Nullable IEuiccService mEuiccService;
    334 
    335     /** The callbacks for all (asynchronous) commands which are currently in flight. */
    336     private Set<BaseEuiccCommandCallback> mActiveCommandCallbacks = new ArraySet<>();
    337 
    338     @VisibleForTesting(visibility = PACKAGE) public UnavailableState mUnavailableState;
    339     @VisibleForTesting(visibility = PACKAGE) public AvailableState mAvailableState;
    340     @VisibleForTesting(visibility = PACKAGE) public BindingState mBindingState;
    341     @VisibleForTesting(visibility = PACKAGE) public DisconnectedState mDisconnectedState;
    342     @VisibleForTesting(visibility = PACKAGE) public ConnectedState mConnectedState;
    343 
    344     EuiccConnector(Context context) {
    345         super(TAG);
    346         init(context);
    347     }
    348 
    349     @VisibleForTesting(visibility = PACKAGE)
    350     public EuiccConnector(Context context, Looper looper) {
    351         super(TAG, looper);
    352         init(context);
    353     }
    354 
    355     private void init(Context context) {
    356         mContext = context;
    357         mPm = context.getPackageManager();
    358 
    359         // Unavailable/Available both monitor for package changes and update mSelectedComponent but
    360         // do not need to adjust the binding.
    361         mUnavailableState = new UnavailableState();
    362         addState(mUnavailableState);
    363         mAvailableState = new AvailableState();
    364         addState(mAvailableState, mUnavailableState);
    365 
    366         mBindingState = new BindingState();
    367         addState(mBindingState);
    368 
    369         // Disconnected/Connected both monitor for package changes and reestablish the active
    370         // binding if necessary.
    371         mDisconnectedState = new DisconnectedState();
    372         addState(mDisconnectedState);
    373         mConnectedState = new ConnectedState();
    374         addState(mConnectedState, mDisconnectedState);
    375 
    376         mSelectedComponent = findBestComponent();
    377         setInitialState(mSelectedComponent != null ? mAvailableState : mUnavailableState);
    378 
    379         mPackageMonitor.register(mContext, null /* thread */, false /* externalStorage */);
    380         mContext.registerReceiver(
    381                 mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
    382 
    383         start();
    384     }
    385 
    386     @Override
    387     public void onHalting() {
    388         mPackageMonitor.unregister();
    389         mContext.unregisterReceiver(mUserUnlockedReceiver);
    390     }
    391 
    392     /** Asynchronously fetch the EID. */
    393     @VisibleForTesting(visibility = PACKAGE)
    394     public void getEid(GetEidCommandCallback callback) {
    395         sendMessage(CMD_GET_EID, callback);
    396     }
    397 
    398     /** Asynchronously get OTA status. */
    399     @VisibleForTesting(visibility = PACKAGE)
    400     public void getOtaStatus(GetOtaStatusCommandCallback callback) {
    401         sendMessage(CMD_GET_OTA_STATUS, callback);
    402     }
    403 
    404     /** Asynchronously perform OTA update. */
    405     @VisibleForTesting(visibility = PACKAGE)
    406     public void startOtaIfNecessary(OtaStatusChangedCallback callback) {
    407         sendMessage(CMD_START_OTA_IF_NECESSARY, callback);
    408     }
    409 
    410     /** Asynchronously fetch metadata for the given downloadable subscription. */
    411     @VisibleForTesting(visibility = PACKAGE)
    412     public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
    413             boolean forceDeactivateSim, GetMetadataCommandCallback callback) {
    414         GetMetadataRequest request =
    415                 new GetMetadataRequest();
    416         request.mSubscription = subscription;
    417         request.mForceDeactivateSim = forceDeactivateSim;
    418         request.mCallback = callback;
    419         sendMessage(CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA, request);
    420     }
    421 
    422     /** Asynchronously download the given subscription. */
    423     @VisibleForTesting(visibility = PACKAGE)
    424     public void downloadSubscription(DownloadableSubscription subscription,
    425             boolean switchAfterDownload, boolean forceDeactivateSim,
    426             DownloadCommandCallback callback) {
    427         DownloadRequest request = new DownloadRequest();
    428         request.mSubscription = subscription;
    429         request.mSwitchAfterDownload = switchAfterDownload;
    430         request.mForceDeactivateSim = forceDeactivateSim;
    431         request.mCallback = callback;
    432         sendMessage(CMD_DOWNLOAD_SUBSCRIPTION, request);
    433     }
    434 
    435     void getEuiccProfileInfoList(GetEuiccProfileInfoListCommandCallback callback) {
    436         sendMessage(CMD_GET_EUICC_PROFILE_INFO_LIST, callback);
    437     }
    438 
    439     /** Asynchronously fetch the default downloadable subscription list. */
    440     @VisibleForTesting(visibility = PACKAGE)
    441     public void getDefaultDownloadableSubscriptionList(
    442             boolean forceDeactivateSim, GetDefaultListCommandCallback callback) {
    443         GetDefaultListRequest request = new GetDefaultListRequest();
    444         request.mForceDeactivateSim = forceDeactivateSim;
    445         request.mCallback = callback;
    446         sendMessage(CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST, request);
    447     }
    448 
    449     /** Asynchronously fetch the {@link EuiccInfo}. */
    450     @VisibleForTesting(visibility = PACKAGE)
    451     public void getEuiccInfo(GetEuiccInfoCommandCallback callback) {
    452         sendMessage(CMD_GET_EUICC_INFO, callback);
    453     }
    454 
    455     /** Asynchronously delete the given subscription. */
    456     @VisibleForTesting(visibility = PACKAGE)
    457     public void deleteSubscription(String iccid, DeleteCommandCallback callback) {
    458         DeleteRequest request = new DeleteRequest();
    459         request.mIccid = iccid;
    460         request.mCallback = callback;
    461         sendMessage(CMD_DELETE_SUBSCRIPTION, request);
    462     }
    463 
    464     /** Asynchronously switch to the given subscription. */
    465     @VisibleForTesting(visibility = PACKAGE)
    466     public void switchToSubscription(@Nullable String iccid, boolean forceDeactivateSim,
    467             SwitchCommandCallback callback) {
    468         SwitchRequest request = new SwitchRequest();
    469         request.mIccid = iccid;
    470         request.mForceDeactivateSim = forceDeactivateSim;
    471         request.mCallback = callback;
    472         sendMessage(CMD_SWITCH_TO_SUBSCRIPTION, request);
    473     }
    474 
    475     /** Asynchronously update the nickname of the given subscription. */
    476     @VisibleForTesting(visibility = PACKAGE)
    477     public void updateSubscriptionNickname(
    478             String iccid, String nickname, UpdateNicknameCommandCallback callback) {
    479         UpdateNicknameRequest request = new UpdateNicknameRequest();
    480         request.mIccid = iccid;
    481         request.mNickname = nickname;
    482         request.mCallback = callback;
    483         sendMessage(CMD_UPDATE_SUBSCRIPTION_NICKNAME, request);
    484     }
    485 
    486     /** Asynchronously erase all profiles on the eUICC. */
    487     @VisibleForTesting(visibility = PACKAGE)
    488     public void eraseSubscriptions(EraseCommandCallback callback) {
    489         sendMessage(CMD_ERASE_SUBSCRIPTIONS, callback);
    490     }
    491 
    492     /** Asynchronously ensure that all profiles will be retained on the next factory reset. */
    493     @VisibleForTesting(visibility = PACKAGE)
    494     public void retainSubscriptions(RetainSubscriptionsCommandCallback callback) {
    495         sendMessage(CMD_RETAIN_SUBSCRIPTIONS, callback);
    496     }
    497 
    498     /**
    499      * State in which no EuiccService is available.
    500      *
    501      * <p>All incoming commands will be rejected through
    502      * {@link BaseEuiccCommandCallback#onEuiccServiceUnavailable()}.
    503      *
    504      * <p>Package state changes will lead to transitions between {@link UnavailableState} and
    505      * {@link AvailableState} depending on whether an EuiccService becomes unavailable or
    506      * available.
    507      */
    508     private class UnavailableState extends State {
    509         @Override
    510         public boolean processMessage(Message message) {
    511             if (message.what == CMD_PACKAGE_CHANGE) {
    512                 mSelectedComponent = findBestComponent();
    513                 if (mSelectedComponent != null) {
    514                     transitionTo(mAvailableState);
    515                 } else if (getCurrentState() != mUnavailableState) {
    516                     transitionTo(mUnavailableState);
    517                 }
    518                 return HANDLED;
    519             } else if (isEuiccCommand(message.what)) {
    520                 BaseEuiccCommandCallback callback = getCallback(message);
    521                 callback.onEuiccServiceUnavailable();
    522                 return HANDLED;
    523             }
    524 
    525             return NOT_HANDLED;
    526         }
    527     }
    528 
    529     /**
    530      * State in which a EuiccService is available, but no binding is established or in the process
    531      * of being established.
    532      *
    533      * <p>If a command is received, this state will defer the message and enter {@link BindingState}
    534      * to bring up the binding.
    535      */
    536     private class AvailableState extends State {
    537         @Override
    538         public boolean processMessage(Message message) {
    539             if (isEuiccCommand(message.what)) {
    540                 deferMessage(message);
    541                 transitionTo(mBindingState);
    542                 return HANDLED;
    543             }
    544 
    545             return NOT_HANDLED;
    546         }
    547     }
    548 
    549     /**
    550      * State in which we are binding to the current EuiccService.
    551      *
    552      * <p>This is a transient state. If bindService returns true, we enter {@link DisconnectedState}
    553      * while waiting for the binding to be established. If it returns false, we move back to
    554      * {@link AvailableState}.
    555      *
    556      * <p>Any received messages will be deferred.
    557      */
    558     private class BindingState extends State {
    559         @Override
    560         public void enter() {
    561             if (createBinding()) {
    562                 transitionTo(mDisconnectedState);
    563             } else {
    564                 // createBinding() should generally not return false since we've already performed
    565                 // Intent resolution, but it's always possible that the package state changes
    566                 // asynchronously. Transition to available for now, and if the package state has
    567                 // changed, we'll process that event and move to mUnavailableState as needed.
    568                 transitionTo(mAvailableState);
    569             }
    570         }
    571 
    572         @Override
    573         public boolean processMessage(Message message) {
    574             deferMessage(message);
    575             return HANDLED;
    576         }
    577     }
    578 
    579     /**
    580      * State in which a binding is established, but not currently connected.
    581      *
    582      * <p>We wait up to {@link #BIND_TIMEOUT_MILLIS} for the binding to establish. If it doesn't,
    583      * we go back to {@link AvailableState} to try again.
    584      *
    585      * <p>Package state changes will cause us to unbind and move to {@link BindingState} to
    586      * reestablish the binding if the selected component has changed or if a forced rebind is
    587      * necessary.
    588      *
    589      * <p>Any received commands will be deferred.
    590      */
    591     private class DisconnectedState extends State {
    592         @Override
    593         public void enter() {
    594             sendMessageDelayed(CMD_CONNECT_TIMEOUT, BIND_TIMEOUT_MILLIS);
    595         }
    596 
    597         @Override
    598         public boolean processMessage(Message message) {
    599             if (message.what == CMD_SERVICE_CONNECTED) {
    600                 mEuiccService = (IEuiccService) message.obj;
    601                 transitionTo(mConnectedState);
    602                 return HANDLED;
    603             } else if (message.what == CMD_PACKAGE_CHANGE) {
    604                 ServiceInfo bestComponent = findBestComponent();
    605                 String affectedPackage = (String) message.obj;
    606                 boolean isSameComponent;
    607                 if (bestComponent == null) {
    608                     isSameComponent = mSelectedComponent != null;
    609                 } else {
    610                     isSameComponent = mSelectedComponent == null
    611                             || Objects.equals(
    612                                     bestComponent.getComponentName(),
    613                                     mSelectedComponent.getComponentName());
    614                 }
    615                 boolean forceRebind = bestComponent != null
    616                         && Objects.equals(bestComponent.packageName, affectedPackage);
    617                 if (!isSameComponent || forceRebind) {
    618                     unbind();
    619                     mSelectedComponent = bestComponent;
    620                     if (mSelectedComponent == null) {
    621                         transitionTo(mUnavailableState);
    622                     } else {
    623                         transitionTo(mBindingState);
    624                     }
    625                 }
    626                 return HANDLED;
    627             } else if (message.what == CMD_CONNECT_TIMEOUT) {
    628                 transitionTo(mAvailableState);
    629                 return HANDLED;
    630             } else if (isEuiccCommand(message.what)) {
    631                 deferMessage(message);
    632                 return HANDLED;
    633             }
    634 
    635             return NOT_HANDLED;
    636         }
    637     }
    638 
    639     /**
    640      * State in which the binding is connected.
    641      *
    642      * <p>Commands will be processed as long as we're in this state. We wait up to
    643      * {@link #LINGER_TIMEOUT_MILLIS} between commands; if this timeout is reached, we will drop the
    644      * binding until the next command is received.
    645      */
    646     private class ConnectedState extends State {
    647         @Override
    648         public void enter() {
    649             removeMessages(CMD_CONNECT_TIMEOUT);
    650             sendMessageDelayed(CMD_LINGER_TIMEOUT, LINGER_TIMEOUT_MILLIS);
    651         }
    652 
    653         @Override
    654         public boolean processMessage(Message message) {
    655             if (message.what == CMD_SERVICE_DISCONNECTED) {
    656                 mEuiccService = null;
    657                 transitionTo(mDisconnectedState);
    658                 return HANDLED;
    659             } else if (message.what == CMD_LINGER_TIMEOUT) {
    660                 unbind();
    661                 transitionTo(mAvailableState);
    662                 return HANDLED;
    663             } else if (message.what == CMD_COMMAND_COMPLETE) {
    664                 Runnable runnable = (Runnable) message.obj;
    665                 runnable.run();
    666                 return HANDLED;
    667             } else if (isEuiccCommand(message.what)) {
    668                 final BaseEuiccCommandCallback callback = getCallback(message);
    669                 onCommandStart(callback);
    670                 // TODO(b/36260308): Plumb through an actual SIM slot ID.
    671                 int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
    672                 try {
    673                     switch (message.what) {
    674                         case CMD_GET_EID: {
    675                             mEuiccService.getEid(slotId,
    676                                     new IGetEidCallback.Stub() {
    677                                         @Override
    678                                         public void onSuccess(String eid) {
    679                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    680                                                 ((GetEidCommandCallback) callback)
    681                                                         .onGetEidComplete(eid);
    682                                                 onCommandEnd(callback);
    683                                             });
    684                                         }
    685                                     });
    686                             break;
    687                         }
    688                         case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA: {
    689                             GetMetadataRequest request = (GetMetadataRequest) message.obj;
    690                             mEuiccService.getDownloadableSubscriptionMetadata(slotId,
    691                                     request.mSubscription,
    692                                     request.mForceDeactivateSim,
    693                                     new IGetDownloadableSubscriptionMetadataCallback.Stub() {
    694                                         @Override
    695                                         public void onComplete(
    696                                                 GetDownloadableSubscriptionMetadataResult result) {
    697                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    698                                                 ((GetMetadataCommandCallback) callback)
    699                                                         .onGetMetadataComplete(result);
    700                                                 onCommandEnd(callback);
    701                                             });
    702                                         }
    703                                     });
    704                             break;
    705                         }
    706                         case CMD_DOWNLOAD_SUBSCRIPTION: {
    707                             DownloadRequest request = (DownloadRequest) message.obj;
    708                             mEuiccService.downloadSubscription(slotId,
    709                                     request.mSubscription,
    710                                     request.mSwitchAfterDownload,
    711                                     request.mForceDeactivateSim,
    712                                     new IDownloadSubscriptionCallback.Stub() {
    713                                         @Override
    714                                         public void onComplete(int result) {
    715                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    716                                                 ((DownloadCommandCallback) callback)
    717                                                         .onDownloadComplete(result);
    718                                                 onCommandEnd(callback);
    719                                             });
    720                                         }
    721                                     });
    722                             break;
    723                         }
    724                         case CMD_GET_EUICC_PROFILE_INFO_LIST: {
    725                             mEuiccService.getEuiccProfileInfoList(slotId,
    726                                     new IGetEuiccProfileInfoListCallback.Stub() {
    727                                         @Override
    728                                         public void onComplete(
    729                                                 GetEuiccProfileInfoListResult result) {
    730                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    731                                                 ((GetEuiccProfileInfoListCommandCallback) callback)
    732                                                         .onListComplete(result);
    733                                                 onCommandEnd(callback);
    734                                             });
    735                                         }
    736                                     });
    737                             break;
    738                         }
    739                         case CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST: {
    740                             GetDefaultListRequest request = (GetDefaultListRequest) message.obj;
    741                             mEuiccService.getDefaultDownloadableSubscriptionList(slotId,
    742                                     request.mForceDeactivateSim,
    743                                     new IGetDefaultDownloadableSubscriptionListCallback.Stub() {
    744                                         @Override
    745                                         public void onComplete(
    746                                                 GetDefaultDownloadableSubscriptionListResult result
    747                                         ) {
    748                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    749                                                 ((GetDefaultListCommandCallback) callback)
    750                                                         .onGetDefaultListComplete(result);
    751                                                 onCommandEnd(callback);
    752                                             });
    753                                         }
    754                                     });
    755                             break;
    756                         }
    757                         case CMD_GET_EUICC_INFO: {
    758                             mEuiccService.getEuiccInfo(slotId,
    759                                     new IGetEuiccInfoCallback.Stub() {
    760                                         @Override
    761                                         public void onSuccess(EuiccInfo euiccInfo) {
    762                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    763                                                 ((GetEuiccInfoCommandCallback) callback)
    764                                                         .onGetEuiccInfoComplete(euiccInfo);
    765                                                 onCommandEnd(callback);
    766                                             });
    767                                         }
    768                                     });
    769                             break;
    770                         }
    771                         case CMD_DELETE_SUBSCRIPTION: {
    772                             DeleteRequest request = (DeleteRequest) message.obj;
    773                             mEuiccService.deleteSubscription(slotId, request.mIccid,
    774                                     new IDeleteSubscriptionCallback.Stub() {
    775                                         @Override
    776                                         public void onComplete(int result) {
    777                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    778                                                 ((DeleteCommandCallback) callback)
    779                                                         .onDeleteComplete(result);
    780                                                 onCommandEnd(callback);
    781                                             });
    782                                         }
    783                                     });
    784                             break;
    785                         }
    786                         case CMD_SWITCH_TO_SUBSCRIPTION: {
    787                             SwitchRequest request = (SwitchRequest) message.obj;
    788                             mEuiccService.switchToSubscription(slotId, request.mIccid,
    789                                     request.mForceDeactivateSim,
    790                                     new ISwitchToSubscriptionCallback.Stub() {
    791                                         @Override
    792                                         public void onComplete(int result) {
    793                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    794                                                 ((SwitchCommandCallback) callback)
    795                                                         .onSwitchComplete(result);
    796                                                 onCommandEnd(callback);
    797                                             });
    798                                         }
    799                                     });
    800                             break;
    801                         }
    802                         case CMD_UPDATE_SUBSCRIPTION_NICKNAME: {
    803                             UpdateNicknameRequest request = (UpdateNicknameRequest) message.obj;
    804                             mEuiccService.updateSubscriptionNickname(slotId, request.mIccid,
    805                                     request.mNickname,
    806                                     new IUpdateSubscriptionNicknameCallback.Stub() {
    807                                         @Override
    808                                         public void onComplete(int result) {
    809                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    810                                                 ((UpdateNicknameCommandCallback) callback)
    811                                                         .onUpdateNicknameComplete(result);
    812                                                 onCommandEnd(callback);
    813                                             });
    814                                         }
    815                                     });
    816                             break;
    817                         }
    818                         case CMD_ERASE_SUBSCRIPTIONS: {
    819                             mEuiccService.eraseSubscriptions(slotId,
    820                                     new IEraseSubscriptionsCallback.Stub() {
    821                                         @Override
    822                                         public void onComplete(int result) {
    823                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    824                                                 ((EraseCommandCallback) callback)
    825                                                         .onEraseComplete(result);
    826                                                 onCommandEnd(callback);
    827                                             });
    828                                         }
    829                                     });
    830                             break;
    831                         }
    832                         case CMD_RETAIN_SUBSCRIPTIONS: {
    833                             mEuiccService.retainSubscriptionsForFactoryReset(slotId,
    834                                     new IRetainSubscriptionsForFactoryResetCallback.Stub() {
    835                                         @Override
    836                                         public void onComplete(int result) {
    837                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    838                                                 ((RetainSubscriptionsCommandCallback) callback)
    839                                                         .onRetainSubscriptionsComplete(result);
    840                                                 onCommandEnd(callback);
    841                                             });
    842                                         }
    843                                     });
    844                             break;
    845                         }
    846                         case CMD_GET_OTA_STATUS: {
    847                             mEuiccService.getOtaStatus(slotId,
    848                                     new IGetOtaStatusCallback.Stub() {
    849                                         @Override
    850                                         public void onSuccess(@OtaStatus int status) {
    851                                             sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    852                                                 ((GetOtaStatusCommandCallback) callback)
    853                                                         .onGetOtaStatusComplete(status);
    854                                                 onCommandEnd(callback);
    855                                             });
    856                                         }
    857                                     });
    858                             break;
    859                         }
    860                         case CMD_START_OTA_IF_NECESSARY: {
    861                             mEuiccService.startOtaIfNecessary(slotId,
    862                                     new IOtaStatusChangedCallback.Stub() {
    863                                         @Override
    864                                         public void onOtaStatusChanged(int status)
    865                                                 throws RemoteException {
    866                                             if (status == EuiccManager.EUICC_OTA_IN_PROGRESS) {
    867                                                 sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    868                                                     ((OtaStatusChangedCallback) callback)
    869                                                             .onOtaStatusChanged(status);
    870                                                 });
    871                                             } else {
    872                                                 sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
    873                                                     ((OtaStatusChangedCallback) callback)
    874                                                             .onOtaStatusChanged(status);
    875                                                     onCommandEnd(callback);
    876                                                 });
    877                                             }
    878                                         }
    879                                     });
    880                             break;
    881                         }
    882                         default: {
    883                             Log.wtf(TAG, "Unimplemented eUICC command: " + message.what);
    884                             callback.onEuiccServiceUnavailable();
    885                             onCommandEnd(callback);
    886                             return HANDLED;
    887                         }
    888                     }
    889                 } catch (Exception e) {
    890                     // If this is a RemoteException, we expect to be disconnected soon. For other
    891                     // exceptions, this is a bug in the EuiccService implementation, but we must
    892                     // not let it crash the phone process.
    893                     Log.w(TAG, "Exception making binder call to EuiccService", e);
    894                     callback.onEuiccServiceUnavailable();
    895                     onCommandEnd(callback);
    896                 }
    897 
    898                 return HANDLED;
    899             }
    900 
    901             return NOT_HANDLED;
    902         }
    903 
    904         @Override
    905         public void exit() {
    906             removeMessages(CMD_LINGER_TIMEOUT);
    907             // Dispatch callbacks for all in-flight commands; they will no longer succeed. (The
    908             // remote process cannot possibly trigger a callback at this stage because the
    909             // connection has dropped).
    910             for (BaseEuiccCommandCallback callback : mActiveCommandCallbacks) {
    911                 callback.onEuiccServiceUnavailable();
    912             }
    913             mActiveCommandCallbacks.clear();
    914         }
    915     }
    916 
    917     private static BaseEuiccCommandCallback getCallback(Message message) {
    918         switch (message.what) {
    919             case CMD_GET_EID:
    920             case CMD_GET_EUICC_PROFILE_INFO_LIST:
    921             case CMD_GET_EUICC_INFO:
    922             case CMD_ERASE_SUBSCRIPTIONS:
    923             case CMD_RETAIN_SUBSCRIPTIONS:
    924             case CMD_GET_OTA_STATUS:
    925             case CMD_START_OTA_IF_NECESSARY:
    926                 return (BaseEuiccCommandCallback) message.obj;
    927             case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA:
    928                 return ((GetMetadataRequest) message.obj).mCallback;
    929             case CMD_DOWNLOAD_SUBSCRIPTION:
    930                 return ((DownloadRequest) message.obj).mCallback;
    931             case CMD_GET_DEFAULT_DOWNLOADABLE_SUBSCRIPTION_LIST:
    932                 return ((GetDefaultListRequest) message.obj).mCallback;
    933             case CMD_DELETE_SUBSCRIPTION:
    934                 return ((DeleteRequest) message.obj).mCallback;
    935             case CMD_SWITCH_TO_SUBSCRIPTION:
    936                 return ((SwitchRequest) message.obj).mCallback;
    937             case CMD_UPDATE_SUBSCRIPTION_NICKNAME:
    938                 return ((UpdateNicknameRequest) message.obj).mCallback;
    939             default:
    940                 throw new IllegalArgumentException("Unsupported message: " + message.what);
    941         }
    942     }
    943 
    944     /** Call this at the beginning of the execution of any command. */
    945     private void onCommandStart(BaseEuiccCommandCallback callback) {
    946         mActiveCommandCallbacks.add(callback);
    947         removeMessages(CMD_LINGER_TIMEOUT);
    948     }
    949 
    950     /** Call this at the end of execution of any command (whether or not it succeeded). */
    951     private void onCommandEnd(BaseEuiccCommandCallback callback) {
    952         if (!mActiveCommandCallbacks.remove(callback)) {
    953             Log.wtf(TAG, "Callback already removed from mActiveCommandCallbacks");
    954         }
    955         if (mActiveCommandCallbacks.isEmpty()) {
    956             sendMessageDelayed(CMD_LINGER_TIMEOUT, LINGER_TIMEOUT_MILLIS);
    957         }
    958     }
    959 
    960     /** Return the service info of the EuiccService to bind to, or null if none were found. */
    961     @Nullable
    962     private ServiceInfo findBestComponent() {
    963         return (ServiceInfo) findBestComponent(mPm);
    964     }
    965 
    966     /**
    967      * Bring up a binding to the currently-selected component.
    968      *
    969      * <p>Returns true if we've successfully bound to the service.
    970      */
    971     private boolean createBinding() {
    972         if (mSelectedComponent == null) {
    973             Log.wtf(TAG, "Attempting to create binding but no component is selected");
    974             return false;
    975         }
    976         Intent intent = new Intent(EuiccService.EUICC_SERVICE_INTERFACE);
    977         intent.setComponent(mSelectedComponent.getComponentName());
    978         // We bind this as a foreground service because it is operating directly on the SIM, and we
    979         // do not want it subjected to power-savings restrictions while doing so.
    980         return mContext.bindService(intent, this,
    981                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
    982     }
    983 
    984     private void unbind() {
    985         mEuiccService = null;
    986         mContext.unbindService(this);
    987     }
    988 
    989     private static ComponentInfo findBestComponent(
    990             PackageManager packageManager, List<ResolveInfo> resolveInfoList) {
    991         int bestPriority = Integer.MIN_VALUE;
    992         ComponentInfo bestComponent = null;
    993         if (resolveInfoList != null) {
    994             for (ResolveInfo resolveInfo : resolveInfoList) {
    995                 if (!isValidEuiccComponent(packageManager, resolveInfo)) {
    996                     continue;
    997                 }
    998 
    999                 if (resolveInfo.filter.getPriority() > bestPriority) {
   1000                     bestPriority = resolveInfo.filter.getPriority();
   1001                     bestComponent = resolveInfo.getComponentInfo();
   1002                 }
   1003             }
   1004         }
   1005 
   1006         return bestComponent;
   1007     }
   1008 
   1009     private static boolean isValidEuiccComponent(
   1010             PackageManager packageManager, ResolveInfo resolveInfo) {
   1011         ComponentInfo componentInfo = resolveInfo.getComponentInfo();
   1012         String packageName = componentInfo.getComponentName().getPackageName();
   1013 
   1014         // Verify that the app is privileged (via granting of a privileged permission).
   1015         if (packageManager.checkPermission(
   1016                 Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, packageName)
   1017                         != PackageManager.PERMISSION_GRANTED) {
   1018             Log.wtf(TAG, "Package " + packageName
   1019                     + " does not declare WRITE_EMBEDDED_SUBSCRIPTIONS");
   1020             return false;
   1021         }
   1022 
   1023         // Verify that only the system can access the component.
   1024         final String permission;
   1025         if (componentInfo instanceof ServiceInfo) {
   1026             permission = ((ServiceInfo) componentInfo).permission;
   1027         } else if (componentInfo instanceof ActivityInfo) {
   1028             permission = ((ActivityInfo) componentInfo).permission;
   1029         } else {
   1030             throw new IllegalArgumentException("Can only verify services/activities");
   1031         }
   1032         if (!TextUtils.equals(permission, Manifest.permission.BIND_EUICC_SERVICE)) {
   1033             Log.wtf(TAG, "Package " + packageName
   1034                     + " does not require the BIND_EUICC_SERVICE permission");
   1035             return false;
   1036         }
   1037 
   1038         // Verify that the component declares a priority.
   1039         if (resolveInfo.filter == null || resolveInfo.filter.getPriority() == 0) {
   1040             Log.wtf(TAG, "Package " + packageName + " does not specify a priority");
   1041             return false;
   1042         }
   1043         return true;
   1044     }
   1045 
   1046     @Override
   1047     public void onServiceConnected(ComponentName name, IBinder service) {
   1048         IEuiccService euiccService = IEuiccService.Stub.asInterface(service);
   1049         sendMessage(CMD_SERVICE_CONNECTED, euiccService);
   1050     }
   1051 
   1052     @Override
   1053     public void onServiceDisconnected(ComponentName name) {
   1054         sendMessage(CMD_SERVICE_DISCONNECTED);
   1055     }
   1056 
   1057     private class EuiccPackageMonitor extends PackageMonitor {
   1058         @Override
   1059         public void onPackageAdded(String packageName, int reason) {
   1060             sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
   1061         }
   1062 
   1063         @Override
   1064         public void onPackageRemoved(String packageName, int reason) {
   1065             sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
   1066         }
   1067 
   1068         @Override
   1069         public void onPackageUpdateFinished(String packageName, int uid) {
   1070             sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
   1071         }
   1072 
   1073         @Override
   1074         public void onPackageModified(String packageName) {
   1075             sendPackageChange(packageName, false /* forceUnbindForThisPackage */);
   1076         }
   1077 
   1078         @Override
   1079         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
   1080             if (doit) {
   1081                 for (String packageName : packages) {
   1082                     sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
   1083                 }
   1084             }
   1085             return super.onHandleForceStop(intent, packages, uid, doit);
   1086         }
   1087 
   1088         private void sendPackageChange(String packageName, boolean forceUnbindForThisPackage) {
   1089             sendMessage(CMD_PACKAGE_CHANGE, forceUnbindForThisPackage ? packageName : null);
   1090         }
   1091     }
   1092 
   1093     @Override
   1094     protected void unhandledMessage(Message msg) {
   1095         IState state = getCurrentState();
   1096         Log.wtf(TAG, "Unhandled message " + msg.what + " in state "
   1097                 + (state == null ? "null" : state.getName()));
   1098     }
   1099 
   1100     @Override
   1101     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1102         super.dump(fd, pw, args);
   1103         pw.println("mSelectedComponent=" + mSelectedComponent);
   1104         pw.println("mEuiccService=" + mEuiccService);
   1105         pw.println("mActiveCommandCount=" + mActiveCommandCallbacks.size());
   1106     }
   1107 }
   1108