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