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