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