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