EuiccService.java revision 707c372f09bcf2d4b7ee662f5dfe354f8aca1054
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 android.service.euicc; 17 18import android.annotation.CallSuper; 19import android.annotation.Nullable; 20import android.app.Service; 21import android.content.Intent; 22import android.os.IBinder; 23import android.os.RemoteException; 24import android.telephony.euicc.DownloadableSubscription; 25import android.telephony.euicc.EuiccInfo; 26import android.util.ArraySet; 27 28import java.util.concurrent.LinkedBlockingQueue; 29import java.util.concurrent.ThreadFactory; 30import java.util.concurrent.ThreadPoolExecutor; 31import java.util.concurrent.TimeUnit; 32import 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 */ 70public 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 /** Result code for a successful operation. */ 101 public static final int RESULT_OK = 0; 102 /** Result code indicating that an active SIM must be deactivated to perform the operation. */ 103 public static final int RESULT_MUST_DEACTIVATE_SIM = -1; 104 // New predefined codes should have negative values. 105 106 /** Start of implementation-specific error results. */ 107 public static final int RESULT_FIRST_USER = 1; 108 109 /** 110 * List of all valid resolution actions for validation purposes. 111 * @hide 112 */ 113 public static final ArraySet<String> RESOLUTION_ACTIONS; 114 static { 115 RESOLUTION_ACTIONS = new ArraySet<>(); 116 RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM); 117 RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES); 118 } 119 120 /** Boolean extra for resolution actions indicating whether the user granted consent. */ 121 public static final String RESOLUTION_EXTRA_CONSENT = "consent"; 122 123 private final IEuiccService.Stub mStubWrapper; 124 125 private ThreadPoolExecutor mExecutor; 126 127 public EuiccService() { 128 mStubWrapper = new IEuiccServiceWrapper(); 129 } 130 131 @Override 132 @CallSuper 133 public void onCreate() { 134 super.onCreate(); 135 // We use a oneway AIDL interface to avoid blocking phone process binder threads on IPCs to 136 // an external process, but doing so means the requests are serialized by binder, which is 137 // not desired. Spin up a background thread pool to allow requests to be parallelized. 138 // TODO(b/38206971): Consider removing this if basic card-level functions like listing 139 // profiles are moved to the platform. 140 mExecutor = new ThreadPoolExecutor( 141 4 /* corePoolSize */, 142 4 /* maxPoolSize */, 143 30, TimeUnit.SECONDS, /* keepAliveTime */ 144 new LinkedBlockingQueue<>(), /* workQueue */ 145 new ThreadFactory() { 146 private final AtomicInteger mCount = new AtomicInteger(1); 147 148 @Override 149 public Thread newThread(Runnable r) { 150 return new Thread(r, "EuiccService #" + mCount.getAndIncrement()); 151 } 152 } 153 ); 154 mExecutor.allowCoreThreadTimeOut(true); 155 } 156 157 @Override 158 @CallSuper 159 public void onDestroy() { 160 mExecutor.shutdownNow(); 161 super.onDestroy(); 162 } 163 164 /** 165 * If overriding this method, call through to the super method for any unknown actions. 166 * {@inheritDoc} 167 */ 168 @Override 169 @CallSuper 170 public IBinder onBind(Intent intent) { 171 return mStubWrapper; 172 } 173 174 /** 175 * Return the EID of the eUICC. 176 * 177 * @param slotId ID of the SIM slot being queried. This is currently not populated but is here 178 * to future-proof the APIs. 179 * @return the EID. 180 * @see android.telephony.euicc.EuiccManager#getEid 181 */ 182 // TODO(b/36260308): Update doc when we have multi-SIM support. 183 public abstract String onGetEid(int slotId); 184 185 /** 186 * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription. 187 * 188 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 189 * but is here to future-proof the APIs. 190 * @param subscription A subscription whose metadata needs to be populated. 191 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 192 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)} 193 * should be returned to allow the user to consent to this operation first. 194 * @return The result of the operation. 195 * @see android.telephony.euicc.EuiccManager#getDownloadableSubscriptionMetadata 196 */ 197 public abstract GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata( 198 int slotId, DownloadableSubscription subscription, boolean forceDeactivateSim); 199 200 /** 201 * Return metadata for subscriptions which are available for download for this device. 202 * 203 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 204 * but is here to future-proof the APIs. 205 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 206 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)} 207 * should be returned to allow the user to consent to this operation first. 208 * @return The result of the list operation. 209 * @see android.telephony.euicc.EuiccManager#getDefaultDownloadableSubscriptionList 210 */ 211 public abstract GetDefaultDownloadableSubscriptionListResult 212 onGetDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim); 213 214 /** 215 * Download the given subscription. 216 * 217 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 218 * but is here to future-proof the APIs. 219 * @param subscription The subscription to download. 220 * @param switchAfterDownload If true, the subscription should be enabled upon successful 221 * download. 222 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 223 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM} 224 * should be returned to allow the user to consent to this operation first. 225 * @return the result of the download operation. May be one of the predefined {@code RESULT_} 226 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 227 * @see android.telephony.euicc.EuiccManager#downloadSubscription 228 */ 229 public abstract int onDownloadSubscription(int slotId, 230 DownloadableSubscription subscription, boolean switchAfterDownload, 231 boolean forceDeactivateSim); 232 233 /** 234 * Return a list of all @link EuiccProfileInfo}s. 235 * 236 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 237 * but is here to future-proof the APIs. 238 * @return The result of the operation. 239 * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList 240 * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList 241 */ 242 public abstract GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int slotId); 243 244 /** 245 * Return info about the eUICC chip/device. 246 * 247 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 248 * but is here to future-proof the APIs. 249 * @return the {@link EuiccInfo} for the eUICC chip/device. 250 * @see android.telephony.euicc.EuiccManager#getEuiccInfo 251 */ 252 public abstract EuiccInfo onGetEuiccInfo(int slotId); 253 254 /** 255 * Delete the given subscription. 256 * 257 * <p>If the subscription is currently active, it should be deactivated first (equivalent to a 258 * physical SIM being ejected). 259 * 260 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 261 * but is here to future-proof the APIs. 262 * @param iccid the ICCID of the subscription to delete. 263 * @return the result of the delete operation. May be one of the predefined {@code RESULT_} 264 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 265 * @see android.telephony.euicc.EuiccManager#deleteSubscription 266 */ 267 public abstract int onDeleteSubscription(int slotId, String iccid); 268 269 /** 270 * Switch to the given subscription. 271 * 272 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 273 * but is here to future-proof the APIs. 274 * @param iccid the ICCID of the subscription to enable. May be null, in which case the current 275 * profile should be deactivated and no profile should be activated to replace it - this is 276 * equivalent to a physical SIM being ejected. 277 * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the 278 * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM} 279 * should be returned to allow the user to consent to this operation first. 280 * @return the result of the switch operation. May be one of the predefined {@code RESULT_} 281 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 282 * @see android.telephony.euicc.EuiccManager#switchToSubscription 283 */ 284 public abstract int onSwitchToSubscription(int slotId, @Nullable String iccid, 285 boolean forceDeactivateSim); 286 287 /** 288 * Update the nickname of the given subscription. 289 * 290 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 291 * but is here to future-proof the APIs. 292 * @param iccid the ICCID of the subscription to update. 293 * @param nickname the new nickname to apply. 294 * @return the result of the update operation. May be one of the predefined {@code RESULT_} 295 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 296 * @see android.telephony.euicc.EuiccManager#updateSubscriptionNickname 297 */ 298 public abstract int onUpdateSubscriptionNickname(int slotId, String iccid, 299 String nickname); 300 301 /** 302 * Erase all of the subscriptions on the device. 303 * 304 * <p>This is intended to be used for device resets. As such, the reset should be performed even 305 * if an active SIM must be deactivated in order to access the eUICC. 306 * 307 * @param slotId ID of the SIM slot to use for the operation. This is currently not populated 308 * but is here to future-proof the APIs. 309 * @return the result of the erase operation. May be one of the predefined {@code RESULT_} 310 * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}. 311 * @see android.telephony.euicc.EuiccManager#eraseSubscriptions 312 */ 313 public abstract int onEraseSubscriptions(int slotId); 314 315 /** 316 * Wrapper around IEuiccService that forwards calls to implementations of {@link EuiccService}. 317 */ 318 private class IEuiccServiceWrapper extends IEuiccService.Stub { 319 @Override 320 public void downloadSubscription(int slotId, DownloadableSubscription subscription, 321 boolean switchAfterDownload, boolean forceDeactivateSim, 322 IDownloadSubscriptionCallback callback) { 323 mExecutor.execute(new Runnable() { 324 @Override 325 public void run() { 326 int result = EuiccService.this.onDownloadSubscription( 327 slotId, subscription, switchAfterDownload, forceDeactivateSim); 328 try { 329 callback.onComplete(result); 330 } catch (RemoteException e) { 331 // Can't communicate with the phone process; ignore. 332 } 333 } 334 }); 335 } 336 337 @Override 338 public void getEid(int slotId, IGetEidCallback callback) { 339 mExecutor.execute(new Runnable() { 340 @Override 341 public void run() { 342 String eid = EuiccService.this.onGetEid(slotId); 343 try { 344 callback.onSuccess(eid); 345 } catch (RemoteException e) { 346 // Can't communicate with the phone process; ignore. 347 } 348 } 349 }); 350 } 351 352 @Override 353 public void getDownloadableSubscriptionMetadata(int slotId, 354 DownloadableSubscription subscription, 355 boolean forceDeactivateSim, 356 IGetDownloadableSubscriptionMetadataCallback callback) { 357 mExecutor.execute(new Runnable() { 358 @Override 359 public void run() { 360 GetDownloadableSubscriptionMetadataResult result = 361 EuiccService.this.onGetDownloadableSubscriptionMetadata( 362 slotId, subscription, forceDeactivateSim); 363 try { 364 callback.onComplete(result); 365 } catch (RemoteException e) { 366 // Can't communicate with the phone process; ignore. 367 } 368 } 369 }); 370 } 371 372 @Override 373 public void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim, 374 IGetDefaultDownloadableSubscriptionListCallback callback) { 375 mExecutor.execute(new Runnable() { 376 @Override 377 public void run() { 378 GetDefaultDownloadableSubscriptionListResult result = 379 EuiccService.this.onGetDefaultDownloadableSubscriptionList( 380 slotId, forceDeactivateSim); 381 try { 382 callback.onComplete(result); 383 } catch (RemoteException e) { 384 // Can't communicate with the phone process; ignore. 385 } 386 } 387 }); 388 } 389 390 @Override 391 public void getEuiccProfileInfoList(int slotId, IGetEuiccProfileInfoListCallback callback) { 392 mExecutor.execute(new Runnable() { 393 @Override 394 public void run() { 395 GetEuiccProfileInfoListResult result = 396 EuiccService.this.onGetEuiccProfileInfoList(slotId); 397 try { 398 callback.onComplete(result); 399 } catch (RemoteException e) { 400 // Can't communicate with the phone process; ignore. 401 } 402 } 403 }); 404 } 405 406 @Override 407 public void getEuiccInfo(int slotId, IGetEuiccInfoCallback callback) { 408 mExecutor.execute(new Runnable() { 409 @Override 410 public void run() { 411 EuiccInfo euiccInfo = EuiccService.this.onGetEuiccInfo(slotId); 412 try { 413 callback.onSuccess(euiccInfo); 414 } catch (RemoteException e) { 415 // Can't communicate with the phone process; ignore. 416 } 417 } 418 }); 419 420 } 421 422 @Override 423 public void deleteSubscription(int slotId, String iccid, 424 IDeleteSubscriptionCallback callback) { 425 mExecutor.execute(new Runnable() { 426 @Override 427 public void run() { 428 int result = EuiccService.this.onDeleteSubscription(slotId, iccid); 429 try { 430 callback.onComplete(result); 431 } catch (RemoteException e) { 432 // Can't communicate with the phone process; ignore. 433 } 434 } 435 }); 436 } 437 438 @Override 439 public void switchToSubscription(int slotId, String iccid, boolean forceDeactivateSim, 440 ISwitchToSubscriptionCallback callback) { 441 mExecutor.execute(new Runnable() { 442 @Override 443 public void run() { 444 int result = 445 EuiccService.this.onSwitchToSubscription( 446 slotId, iccid, forceDeactivateSim); 447 try { 448 callback.onComplete(result); 449 } catch (RemoteException e) { 450 // Can't communicate with the phone process; ignore. 451 } 452 } 453 }); 454 } 455 456 @Override 457 public void updateSubscriptionNickname(int slotId, String iccid, String nickname, 458 IUpdateSubscriptionNicknameCallback callback) { 459 mExecutor.execute(new Runnable() { 460 @Override 461 public void run() { 462 int result = 463 EuiccService.this.onUpdateSubscriptionNickname(slotId, iccid, nickname); 464 try { 465 callback.onComplete(result); 466 } catch (RemoteException e) { 467 // Can't communicate with the phone process; ignore. 468 } 469 } 470 }); 471 } 472 473 @Override 474 public void eraseSubscriptions(int slotId, IEraseSubscriptionsCallback callback) { 475 mExecutor.execute(new Runnable() { 476 @Override 477 public void run() { 478 int result = EuiccService.this.onEraseSubscriptions(slotId); 479 try { 480 callback.onComplete(result); 481 } catch (RemoteException e) { 482 // Can't communicate with the phone process; ignore. 483 } 484 } 485 }); 486 } 487 } 488} 489