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 android.service.euicc; 17 18 import android.annotation.CallSuper; 19 import android.annotation.Nullable; 20 import android.app.Service; 21 import android.content.Intent; 22 import android.os.IBinder; 23 import android.os.RemoteException; 24 import android.telephony.euicc.DownloadableSubscription; 25 import android.telephony.euicc.EuiccInfo; 26 import android.util.ArraySet; 27 28 import java.util.concurrent.LinkedBlockingQueue; 29 import java.util.concurrent.ThreadFactory; 30 import java.util.concurrent.ThreadPoolExecutor; 31 import java.util.concurrent.TimeUnit; 32 import java.util.concurrent.atomic.AtomicInteger; 33 34 /** 35 * Service interface linking the system with an eUICC local profile assistant (LPA) application. 36 * 37 * <p>An LPA consists of two separate components (which may both be implemented in the same APK): 38 * the LPA backend, and the LPA UI or LUI. 39 * 40 * <p>To implement the LPA backend, you must extend this class and declare this service in your 41 * manifest file. The service must require the 42 * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter 43 * with the {@link #EUICC_SERVICE_INTERFACE} action. The priority of the intent filter must be set 44 * to a non-zero value in case multiple implementations are present on the device. For example: 45 * 46 * <pre>{@code 47 * <service android:name=".MyEuiccService" 48 * android:permission="android.permission.BIND_EUICC_SERVICE"> 49 * <intent-filter android:priority="100"> 50 * <action android:name="android.service.euicc.EuiccService" /> 51 * </intent-filter> 52 * </service> 53 * }</pre> 54 * 55 * <p>To implement the LUI, you must provide an activity for the following actions: 56 * 57 * <ul> 58 * <li>{@link #ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS} 59 * <li>{@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} 60 * </ul> 61 * 62 * <p>As with the service, each activity must require the 63 * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission. Each should have an intent 64 * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero 65 * priority. 66 * 67 * TODO(b/35851809): Make this a SystemApi. 68 * @hide 69 */ 70 public abstract class EuiccService extends Service { 71 /** Action which must be included in this service's intent filter. */ 72 public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; 73 74 /** Category which must be defined to all UI actions, for efficient lookup. */ 75 public static final String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI"; 76 77 // LUI actions. These are passthroughs of the corresponding EuiccManager actions. 78 79 /** @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS */ 80 public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = 81 "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; 82 /** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */ 83 public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = 84 "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; 85 86 // LUI resolution actions. These are called by the platform to resolve errors in situations that 87 // require user interaction. 88 // TODO(b/33075886): Define extras for any input parameters to these dialogs once they are 89 // more scoped out. 90 /** Alert the user that this action will result in an active SIM being deactivated. */ 91 public static final String ACTION_RESOLVE_DEACTIVATE_SIM = 92 "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM"; 93 /** 94 * Alert the user about a download/switch being done for an app that doesn't currently have 95 * carrier privileges. 96 */ 97 public static final String ACTION_RESOLVE_NO_PRIVILEGES = 98 "android.service.euicc.action.RESOLVE_NO_PRIVILEGES"; 99 100 /** Intent extra set for resolution requests containing the package name of the calling app. */ 101 public static final String EXTRA_RESOLUTION_CALLING_PACKAGE = 102 "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; 103 104 /** Result code for a successful operation. */ 105 public static final int RESULT_OK = 0; 106 /** Result code indicating that an active SIM must be deactivated to perform the operation. */ 107 public static final int RESULT_MUST_DEACTIVATE_SIM = -1; 108 // New predefined codes should have negative values. 109 110 /** Start of implementation-specific error results. */ 111 public static final int RESULT_FIRST_USER = 1; 112 113 /** 114 * List of all valid resolution actions for validation purposes. 115 * @hide 116 */ 117 public static final ArraySet<String> RESOLUTION_ACTIONS; 118 static { 119 RESOLUTION_ACTIONS = new ArraySet<>(); 120 RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM); 121 RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES); 122 } 123 124 /** Boolean extra for resolution actions indicating whether the user granted consent. */ 125 public static final String RESOLUTION_EXTRA_CONSENT = "consent"; 126 127 private final IEuiccService.Stub mStubWrapper; 128 129 private ThreadPoolExecutor mExecutor; 130 131 public EuiccService() { 132 mStubWrapper = new IEuiccServiceWrapper(); 133 } 134 135 @Override 136 @CallSuper 137 public void onCreate() { 138 super.onCreate(); 139 // We use a oneway AIDL interface to avoid blocking phone process binder threads on IPCs to 140 // an external process, but doing so means the requests are serialized by binder, which is 141 // not desired. Spin up a background thread pool to allow requests to be parallelized. 142 // TODO(b/38206971): Consider removing this if basic card-level functions like listing 143 // profiles are moved to the platform. 144 mExecutor = new ThreadPoolExecutor( 145 4 /* corePoolSize */, 146 4 /* maxPoolSize */, 147 30, TimeUnit.SECONDS, /* keepAliveTime */ 148 new LinkedBlockingQueue<>(), /* workQueue */ 149 new ThreadFactory() { 150 private final AtomicInteger mCount = new AtomicInteger(1); 151 152 @Override 153 public Thread newThread(Runnable r) { 154 return new Thread(r, "EuiccService #" + mCount.getAndIncrement()); 155 } 156 } 157 ); 158 mExecutor.allowCoreThreadTimeOut(true); 159 } 160 161 @Override 162 @CallSuper 163 public void onDestroy() { 164 mExecutor.shutdownNow(); 165 super.onDestroy(); 166 } 167 168 /** 169 * If overriding this method, call through to the super method for any unknown actions. 170 * {@inheritDoc} 171 */ 172 @Override 173 @CallSuper 174 public IBinder onBind(Intent intent) { 175 return mStubWrapper; 176 } 177 178 /** 179 * Return the EID of the eUICC. 180 * 181 * @param slotId ID of the SIM slot being queried. This is currently not populated but is here 182 * to future-proof the APIs. 183 * @return the EID. 184 * @see android.telephony.euicc.EuiccManager#getEid 185 */ 186 // TODO(b/36260308): Update doc when we have multi-SIM support. 187 public abstract String onGetEid(int slotId); 188 189 /** 190 * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription. 191 * 192 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 193 * but is here to future-proof the APIs. 194 * @param subscription A subscription whose metadata needs to be populated. 195 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 196 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)} 197 * should be returned to allow the user to consent to this operation first. 198 * @return The result of the operation. 199 * @see android.telephony.euicc.EuiccManager#getDownloadableSubscriptionMetadata 200 */ 201 public abstract GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata( 202 int slotId, DownloadableSubscription subscription, boolean forceDeactivateSim); 203 204 /** 205 * Return metadata for subscriptions which are available for download for this device. 206 * 207 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 208 * but is here to future-proof the APIs. 209 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 210 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)} 211 * should be returned to allow the user to consent to this operation first. 212 * @return The result of the list operation. 213 * @see android.telephony.euicc.EuiccManager#getDefaultDownloadableSubscriptionList 214 */ 215 public abstract GetDefaultDownloadableSubscriptionListResult 216 onGetDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim); 217 218 /** 219 * Download the given subscription. 220 * 221 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 222 * but is here to future-proof the APIs. 223 * @param subscription The subscription to download. 224 * @param switchAfterDownload If true, the subscription should be enabled upon successful 225 * download. 226 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 227 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM} 228 * should be returned to allow the user to consent to this operation first. 229 * @return the result of the download operation. May be one of the predefined {@code RESULT_} 230 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 231 * @see android.telephony.euicc.EuiccManager#downloadSubscription 232 */ 233 public abstract int onDownloadSubscription(int slotId, 234 DownloadableSubscription subscription, boolean switchAfterDownload, 235 boolean forceDeactivateSim); 236 237 /** 238 * Return a list of all @link EuiccProfileInfo}s. 239 * 240 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 241 * but is here to future-proof the APIs. 242 * @return The result of the operation. 243 * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList 244 * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList 245 */ 246 public abstract GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId); 247 248 /** 249 * Return info about the eUICC chip/device. 250 * 251 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 252 * but is here to future-proof the APIs. 253 * @return the {@link EuiccInfo} for the eUICC chip/device. 254 * @see android.telephony.euicc.EuiccManager#getEuiccInfo 255 */ 256 public abstract EuiccInfo onGetEuiccInfo(int slotId); 257 258 /** 259 * Delete the given subscription. 260 * 261 * <p>If the subscription is currently active, it should be deactivated first (equivalent to a 262 * physical SIM being ejected). 263 * 264 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 265 * but is here to future-proof the APIs. 266 * @param iccid the ICCID of the subscription to delete. 267 * @return the result of the delete operation. May be one of the predefined {@code RESULT_} 268 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 269 * @see android.telephony.euicc.EuiccManager#deleteSubscription 270 */ 271 public abstract int onDeleteSubscription(int slotId, String iccid); 272 273 /** 274 * Switch to the given subscription. 275 * 276 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 277 * but is here to future-proof the APIs. 278 * @param iccid the ICCID of the subscription to enable. May be null, in which case the current 279 * profile should be deactivated and no profile should be activated to replace it - this is 280 * equivalent to a physical SIM being ejected. 281 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 282 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM} 283 * should be returned to allow the user to consent to this operation first. 284 * @return the result of the switch operation. May be one of the predefined {@code RESULT_} 285 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 286 * @see android.telephony.euicc.EuiccManager#switchToSubscription 287 */ 288 public abstract int onSwitchToSubscription(int slotId, @Nullable String iccid, 289 boolean forceDeactivateSim); 290 291 /** 292 * Update the nickname of the given subscription. 293 * 294 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 295 * but is here to future-proof the APIs. 296 * @param iccid the ICCID of the subscription to update. 297 * @param nickname the new nickname to apply. 298 * @return the result of the update operation. May be one of the predefined {@code RESULT_} 299 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 300 * @see android.telephony.euicc.EuiccManager#updateSubscriptionNickname 301 */ 302 public abstract int onUpdateSubscriptionNickname(int slotId, String iccid, 303 String nickname); 304 305 /** 306 * Erase all of the subscriptions on the device. 307 * 308 * <p>This is intended to be used for device resets. As such, the reset should be performed even 309 * if an active SIM must be deactivated in order to access the eUICC. 310 * 311 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 312 * but is here to future-proof the APIs. 313 * @return the result of the erase operation. May be one of the predefined {@code RESULT_} 314 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 315 * @see android.telephony.euicc.EuiccManager#eraseSubscriptions 316 */ 317 public abstract int onEraseSubscriptions(int slotId); 318 319 /** 320 * Ensure that subscriptions will be retained on the next factory reset. 321 * 322 * <p>Called directly before a factory reset. Assumes that a normal factory reset will lead to 323 * profiles being erased on first boot (to cover fastboot/recovery wipes), so the implementation 324 * should persist some bit that will remain accessible after the factory reset to bypass this 325 * flow when this method is called. 326 * 327 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 328 * but is here to future-proof the APIs. 329 * @return the result of the operation. May be one of the predefined {@code RESULT_} constants 330 * or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 331 */ 332 public abstract int onRetainSubscriptionsForFactoryReset(int slotId); 333 334 /** 335 * Wrapper around IEuiccService that forwards calls to implementations of {@link EuiccService}. 336 */ 337 private class IEuiccServiceWrapper extends IEuiccService.Stub { 338 @Override 339 public void downloadSubscription(int slotId, DownloadableSubscription subscription, 340 boolean switchAfterDownload, boolean forceDeactivateSim, 341 IDownloadSubscriptionCallback callback) { 342 mExecutor.execute(new Runnable() { 343 @Override 344 public void run() { 345 int result = EuiccService.this.onDownloadSubscription( 346 slotId, subscription, switchAfterDownload, forceDeactivateSim); 347 try { 348 callback.onComplete(result); 349 } catch (RemoteException e) { 350 // Can't communicate with the phone process; ignore. 351 } 352 } 353 }); 354 } 355 356 @Override 357 public void getEid(int slotId, IGetEidCallback callback) { 358 mExecutor.execute(new Runnable() { 359 @Override 360 public void run() { 361 String eid = EuiccService.this.onGetEid(slotId); 362 try { 363 callback.onSuccess(eid); 364 } catch (RemoteException e) { 365 // Can't communicate with the phone process; ignore. 366 } 367 } 368 }); 369 } 370 371 @Override 372 public void getDownloadableSubscriptionMetadata(int slotId, 373 DownloadableSubscription subscription, 374 boolean forceDeactivateSim, 375 IGetDownloadableSubscriptionMetadataCallback callback) { 376 mExecutor.execute(new Runnable() { 377 @Override 378 public void run() { 379 GetDownloadableSubscriptionMetadataResult result = 380 EuiccService.this.onGetDownloadableSubscriptionMetadata( 381 slotId, subscription, forceDeactivateSim); 382 try { 383 callback.onComplete(result); 384 } catch (RemoteException e) { 385 // Can't communicate with the phone process; ignore. 386 } 387 } 388 }); 389 } 390 391 @Override 392 public void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim, 393 IGetDefaultDownloadableSubscriptionListCallback callback) { 394 mExecutor.execute(new Runnable() { 395 @Override 396 public void run() { 397 GetDefaultDownloadableSubscriptionListResult result = 398 EuiccService.this.onGetDefaultDownloadableSubscriptionList( 399 slotId, forceDeactivateSim); 400 try { 401 callback.onComplete(result); 402 } catch (RemoteException e) { 403 // Can't communicate with the phone process; ignore. 404 } 405 } 406 }); 407 } 408 409 @Override 410 public void getEuiccProfileInfoList(int slotId, IGetEuiccProfileInfoListCallback callback) { 411 mExecutor.execute(new Runnable() { 412 @Override 413 public void run() { 414 GetEuiccProfileInfoListResult result = 415 EuiccService.this.onGetEuiccProfileInfoList(slotId); 416 try { 417 callback.onComplete(result); 418 } catch (RemoteException e) { 419 // Can't communicate with the phone process; ignore. 420 } 421 } 422 }); 423 } 424 425 @Override 426 public void getEuiccInfo(int slotId, IGetEuiccInfoCallback callback) { 427 mExecutor.execute(new Runnable() { 428 @Override 429 public void run() { 430 EuiccInfo euiccInfo = EuiccService.this.onGetEuiccInfo(slotId); 431 try { 432 callback.onSuccess(euiccInfo); 433 } catch (RemoteException e) { 434 // Can't communicate with the phone process; ignore. 435 } 436 } 437 }); 438 439 } 440 441 @Override 442 public void deleteSubscription(int slotId, String iccid, 443 IDeleteSubscriptionCallback callback) { 444 mExecutor.execute(new Runnable() { 445 @Override 446 public void run() { 447 int result = EuiccService.this.onDeleteSubscription(slotId, iccid); 448 try { 449 callback.onComplete(result); 450 } catch (RemoteException e) { 451 // Can't communicate with the phone process; ignore. 452 } 453 } 454 }); 455 } 456 457 @Override 458 public void switchToSubscription(int slotId, String iccid, boolean forceDeactivateSim, 459 ISwitchToSubscriptionCallback callback) { 460 mExecutor.execute(new Runnable() { 461 @Override 462 public void run() { 463 int result = 464 EuiccService.this.onSwitchToSubscription( 465 slotId, iccid, forceDeactivateSim); 466 try { 467 callback.onComplete(result); 468 } catch (RemoteException e) { 469 // Can't communicate with the phone process; ignore. 470 } 471 } 472 }); 473 } 474 475 @Override 476 public void updateSubscriptionNickname(int slotId, String iccid, String nickname, 477 IUpdateSubscriptionNicknameCallback callback) { 478 mExecutor.execute(new Runnable() { 479 @Override 480 public void run() { 481 int result = 482 EuiccService.this.onUpdateSubscriptionNickname(slotId, iccid, nickname); 483 try { 484 callback.onComplete(result); 485 } catch (RemoteException e) { 486 // Can't communicate with the phone process; ignore. 487 } 488 } 489 }); 490 } 491 492 @Override 493 public void eraseSubscriptions(int slotId, IEraseSubscriptionsCallback callback) { 494 mExecutor.execute(new Runnable() { 495 @Override 496 public void run() { 497 int result = EuiccService.this.onEraseSubscriptions(slotId); 498 try { 499 callback.onComplete(result); 500 } catch (RemoteException e) { 501 // Can't communicate with the phone process; ignore. 502 } 503 } 504 }); 505 } 506 507 @Override 508 public void retainSubscriptionsForFactoryReset(int slotId, 509 IRetainSubscriptionsForFactoryResetCallback callback) { 510 mExecutor.execute(new Runnable() { 511 @Override 512 public void run() { 513 int result = EuiccService.this.onRetainSubscriptionsForFactoryReset(slotId); 514 try { 515 callback.onComplete(result); 516 } catch (RemoteException e) { 517 // Can't communicate with the phone process; ignore. 518 } 519 } 520 }); 521 } 522 } 523 } 524