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