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