1 /* 2 * Copyright (C) 2018 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 17 package com.android.internal.telephony.uicc.euicc; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.os.AsyncResult; 23 import android.os.Handler; 24 import android.os.Registrant; 25 import android.os.RegistrantList; 26 import android.service.carrier.CarrierIdentifier; 27 import android.service.euicc.EuiccProfileInfo; 28 import android.telephony.Rlog; 29 import android.telephony.SubscriptionInfo; 30 import android.telephony.UiccAccessRule; 31 import android.telephony.euicc.EuiccCardManager; 32 import android.telephony.euicc.EuiccNotification; 33 import android.telephony.euicc.EuiccRulesAuthTable; 34 import android.text.TextUtils; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.telephony.CommandsInterface; 38 import com.android.internal.telephony.Phone; 39 import com.android.internal.telephony.PhoneFactory; 40 import com.android.internal.telephony.uicc.IccCardStatus; 41 import com.android.internal.telephony.uicc.IccIoResult; 42 import com.android.internal.telephony.uicc.IccUtils; 43 import com.android.internal.telephony.uicc.UiccCard; 44 import com.android.internal.telephony.uicc.asn1.Asn1Decoder; 45 import com.android.internal.telephony.uicc.asn1.Asn1Node; 46 import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException; 47 import com.android.internal.telephony.uicc.asn1.TagNotFoundException; 48 import com.android.internal.telephony.uicc.euicc.EuiccCardErrorException.OperationCode; 49 import com.android.internal.telephony.uicc.euicc.apdu.ApduException; 50 import com.android.internal.telephony.uicc.euicc.apdu.ApduSender; 51 import com.android.internal.telephony.uicc.euicc.apdu.ApduSenderResultCallback; 52 import com.android.internal.telephony.uicc.euicc.apdu.RequestBuilder; 53 import com.android.internal.telephony.uicc.euicc.apdu.RequestProvider; 54 import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; 55 import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper; 56 57 import java.io.FileDescriptor; 58 import java.io.PrintWriter; 59 import java.util.Arrays; 60 import java.util.List; 61 62 /** 63 * This represents an eUICC card to perform profile management operations asynchronously. This class 64 * includes methods defined by different versions of GSMA Spec (SGP.22). 65 */ 66 public class EuiccCard extends UiccCard { 67 private static final String LOG_TAG = "EuiccCard"; 68 private static final boolean DBG = true; 69 70 private static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100"; 71 private static final int ICCID_LENGTH = 20; 72 73 // APDU status for SIM refresh 74 private static final int APDU_ERROR_SIM_REFRESH = 0x6F00; 75 76 // These error codes are defined in GSMA SGP.22. 0 is the code for success. 77 private static final int CODE_OK = 0; 78 79 // Error code for profile not in expected state for the operation. This error includes the case 80 // that profile is not in disabled state when being enabled or deleted, and that profile is not 81 // in enabled state when being disabled. 82 private static final int CODE_PROFILE_NOT_IN_EXPECTED_STATE = 2; 83 84 // Error code for nothing to delete when resetting eUICC memory or removing notifications. 85 private static final int CODE_NOTHING_TO_DELETE = 1; 86 87 // Error code for no result available when retrieving notifications. 88 private static final int CODE_NO_RESULT_AVAILABLE = 1; 89 90 private static final EuiccSpecVersion SGP22_V_2_0 = new EuiccSpecVersion(2, 0, 0); 91 private static final EuiccSpecVersion SGP22_V_2_1 = new EuiccSpecVersion(2, 1, 0); 92 93 // Device capabilities. 94 private static final String DEV_CAP_GSM = "gsm"; 95 private static final String DEV_CAP_UTRAN = "utran"; 96 private static final String DEV_CAP_CDMA_1X = "cdma1x"; 97 private static final String DEV_CAP_HRPD = "hrpd"; 98 private static final String DEV_CAP_EHRPD = "ehrpd"; 99 private static final String DEV_CAP_EUTRAN = "eutran"; 100 private static final String DEV_CAP_NFC = "nfc"; 101 private static final String DEV_CAP_CRL = "crl"; 102 103 // These interfaces are used for simplifying the code by leveraging lambdas. 104 private interface ApduRequestBuilder { build(RequestBuilder requestBuilder)105 void build(RequestBuilder requestBuilder) 106 throws EuiccCardException, TagNotFoundException, InvalidAsn1DataException; 107 } 108 109 private interface ApduResponseHandler<T> { handleResult(byte[] response)110 T handleResult(byte[] response) 111 throws EuiccCardException, TagNotFoundException, InvalidAsn1DataException; 112 } 113 114 private interface ApduIntermediateResultHandler { shouldContinue(IccIoResult intermediateResult)115 boolean shouldContinue(IccIoResult intermediateResult); 116 } 117 118 private interface ApduExceptionHandler { handleException(Throwable e)119 void handleException(Throwable e); 120 } 121 122 private final ApduSender mApduSender; 123 private RegistrantList mEidReadyRegistrants; 124 private EuiccSpecVersion mSpecVersion; 125 private volatile String mEid; 126 EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock)127 public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) { 128 super(c, ci, ics, phoneId, lock); 129 // TODO: Set supportExtendedApdu based on ATR. 130 mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */); 131 132 if (TextUtils.isEmpty(ics.eid)) { 133 loge("no eid given in constructor for phone " + phoneId); 134 loadEidAndNotifyRegistrants(); 135 } else { 136 mEid = ics.eid; 137 mCardId = ics.eid; 138 } 139 } 140 141 /** 142 * Registers to be notified when EID is ready. If the EID is ready when this method is called, 143 * the registrant will be notified immediately. 144 */ registerForEidReady(Handler h, int what, Object obj)145 public void registerForEidReady(Handler h, int what, Object obj) { 146 Registrant r = new Registrant(h, what, obj); 147 if (mEid != null) { 148 r.notifyRegistrant(new AsyncResult(null, null, null)); 149 } else { 150 if (mEidReadyRegistrants == null) { 151 mEidReadyRegistrants = new RegistrantList(); 152 } 153 mEidReadyRegistrants.add(r); 154 } 155 } 156 157 /** 158 * Unregisters to be notified when EID is ready. 159 */ unregisterForEidReady(Handler h)160 public void unregisterForEidReady(Handler h) { 161 if (mEidReadyRegistrants != null) { 162 mEidReadyRegistrants.remove(h); 163 } 164 } 165 166 // For RadioConfig<1.2 we don't know the EID when constructing the EuiccCard, so callers may 167 // need to register to be notified when we have the EID 168 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) loadEidAndNotifyRegistrants()169 protected void loadEidAndNotifyRegistrants() { 170 Handler euiccMainThreadHandler = new Handler(); 171 AsyncResultCallback<String> cardCb = new AsyncResultCallback<String>() { 172 @Override 173 public void onResult(String result) { 174 if (mEidReadyRegistrants != null) { 175 mEidReadyRegistrants.notifyRegistrants(new AsyncResult(null, null, null)); 176 } 177 } 178 179 @Override 180 public void onException(Throwable e) { 181 // Still notifying registrants even getting eid fails. 182 if (mEidReadyRegistrants != null) { 183 mEidReadyRegistrants.notifyRegistrants(new AsyncResult(null, null, null)); 184 } 185 mEid = ""; 186 mCardId = ""; 187 Rlog.e(LOG_TAG, "Failed loading eid", e); 188 } 189 }; 190 getEid(cardCb, euiccMainThreadHandler); 191 } 192 193 /** 194 * Gets the GSMA RSP specification version supported by this eUICC. This may return null if the 195 * version cannot be read. 196 */ getSpecVersion(AsyncResultCallback<EuiccSpecVersion> callback, Handler handler)197 public void getSpecVersion(AsyncResultCallback<EuiccSpecVersion> callback, Handler handler) { 198 if (mSpecVersion != null) { 199 AsyncResultHelper.returnResult(mSpecVersion, callback, handler); 200 return; 201 } 202 203 sendApdu(newRequestProvider((RequestBuilder requestBuilder) -> { /* Do nothing */ }), 204 (byte[] response) -> mSpecVersion, callback, handler); 205 } 206 207 @Override update(Context c, CommandsInterface ci, IccCardStatus ics)208 public void update(Context c, CommandsInterface ci, IccCardStatus ics) { 209 synchronized (mLock) { 210 if (!TextUtils.isEmpty(ics.eid)) { 211 mEid = ics.eid; 212 } 213 super.update(c, ci, ics); 214 } 215 } 216 217 @Override updateCardId()218 protected void updateCardId() { 219 if (TextUtils.isEmpty(mEid)) { 220 super.updateCardId(); 221 } else { 222 mCardId = mEid; 223 } 224 } 225 226 /** 227 * Gets a list of user-visible profiles. 228 * 229 * @param callback The callback to get the result. 230 * @param handler The handler to run the callback. 231 * @since 1.1.0 [GSMA SGP.22] 232 */ getAllProfiles(AsyncResultCallback<EuiccProfileInfo[]> callback, Handler handler)233 public void getAllProfiles(AsyncResultCallback<EuiccProfileInfo[]> callback, Handler handler) { 234 sendApdu( 235 newRequestProvider((RequestBuilder requestBuilder) -> 236 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES) 237 .addChildAsBytes(Tags.TAG_TAG_LIST, Tags.EUICC_PROFILE_TAGS) 238 .build().toHex())), 239 response -> { 240 List<Asn1Node> profileNodes = new Asn1Decoder(response).nextNode() 241 .getChild(Tags.TAG_CTX_COMP_0).getChildren(Tags.TAG_PROFILE_INFO); 242 int size = profileNodes.size(); 243 EuiccProfileInfo[] profiles = new EuiccProfileInfo[size]; 244 int profileCount = 0; 245 for (int i = 0; i < size; i++) { 246 Asn1Node profileNode = profileNodes.get(i); 247 if (!profileNode.hasChild(Tags.TAG_ICCID)) { 248 loge("Profile must have an ICCID."); 249 continue; 250 } 251 String strippedIccIdString = 252 stripTrailingFs(profileNode.getChild(Tags.TAG_ICCID).asBytes()); 253 EuiccProfileInfo.Builder profileBuilder = 254 new EuiccProfileInfo.Builder(strippedIccIdString); 255 buildProfile(profileNode, profileBuilder); 256 257 EuiccProfileInfo profile = profileBuilder.build(); 258 profiles[profileCount++] = profile; 259 } 260 return profiles; 261 }, 262 callback, handler); 263 } 264 265 /** 266 * Gets a profile. 267 * 268 * @param callback The callback to get the result. 269 * @param handler The handler to run the callback. 270 * @since 1.1.0 [GSMA SGP.22] 271 */ getProfile(String iccid, AsyncResultCallback<EuiccProfileInfo> callback, Handler handler)272 public final void getProfile(String iccid, AsyncResultCallback<EuiccProfileInfo> callback, 273 Handler handler) { 274 sendApdu( 275 newRequestProvider((RequestBuilder requestBuilder) -> 276 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES) 277 .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0) 278 .addChildAsBytes( 279 Tags.TAG_ICCID, IccUtils.bcdToBytes(padTrailingFs(iccid))) 280 .build()) 281 .addChildAsBytes(Tags.TAG_TAG_LIST, Tags.EUICC_PROFILE_TAGS) 282 .build().toHex())), 283 response -> { 284 List<Asn1Node> profileNodes = new Asn1Decoder(response).nextNode() 285 .getChild(Tags.TAG_CTX_COMP_0).getChildren(Tags.TAG_PROFILE_INFO); 286 if (profileNodes.isEmpty()) { 287 return null; 288 } 289 Asn1Node profileNode = profileNodes.get(0); 290 String strippedIccIdString = 291 stripTrailingFs(profileNode.getChild(Tags.TAG_ICCID).asBytes()); 292 EuiccProfileInfo.Builder profileBuilder = 293 new EuiccProfileInfo.Builder(strippedIccIdString); 294 buildProfile(profileNode, profileBuilder); 295 return profileBuilder.build(); 296 }, 297 callback, handler); 298 } 299 300 /** 301 * Disables a profile of the given {@code iccid}. 302 * 303 * @param refresh Whether sending the REFRESH command to modem. 304 * @param callback The callback to get the result. 305 * @param handler The handler to run the callback. 306 * @since 1.1.0 [GSMA SGP.22] 307 */ disableProfile(String iccid, boolean refresh, AsyncResultCallback<Void> callback, Handler handler)308 public void disableProfile(String iccid, boolean refresh, AsyncResultCallback<Void> callback, 309 Handler handler) { 310 sendApduWithSimResetErrorWorkaround( 311 newRequestProvider((RequestBuilder requestBuilder) -> { 312 byte[] iccidBytes = IccUtils.bcdToBytes(padTrailingFs(iccid)); 313 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_DISABLE_PROFILE) 314 .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0) 315 .addChildAsBytes(Tags.TAG_ICCID, iccidBytes)) 316 .addChildAsBoolean(Tags.TAG_CTX_1, refresh) 317 .build().toHex()); 318 }), 319 response -> { 320 int result; 321 // SGP.22 v2.0 DisableProfileResponse 322 result = parseSimpleResult(response); 323 switch (result) { 324 case CODE_OK: 325 return null; 326 case CODE_PROFILE_NOT_IN_EXPECTED_STATE: 327 logd("Profile is already disabled, iccid: " 328 + SubscriptionInfo.givePrintableIccid(iccid)); 329 return null; 330 default: 331 throw new EuiccCardErrorException( 332 EuiccCardErrorException.OPERATION_DISABLE_PROFILE, result); 333 } 334 }, 335 callback, handler); 336 } 337 338 /** 339 * Switches from the current profile to another profile. The current profile will be disabled 340 * and the specified profile will be enabled. 341 * 342 * @param refresh Whether sending the REFRESH command to modem. 343 * @param callback The callback to get the EuiccProfile enabled. 344 * @param handler The handler to run the callback. 345 * @since 1.1.0 [GSMA SGP.22] 346 */ switchToProfile(String iccid, boolean refresh, AsyncResultCallback<Void> callback, Handler handler)347 public void switchToProfile(String iccid, boolean refresh, AsyncResultCallback<Void> callback, 348 Handler handler) { 349 sendApduWithSimResetErrorWorkaround( 350 newRequestProvider((RequestBuilder requestBuilder) -> { 351 byte[] iccidBytes = IccUtils.bcdToBytes(padTrailingFs(iccid)); 352 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_ENABLE_PROFILE) 353 .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0) 354 .addChildAsBytes(Tags.TAG_ICCID, iccidBytes)) 355 .addChildAsBoolean(Tags.TAG_CTX_1, refresh) 356 .build().toHex()); 357 }), 358 response -> { 359 int result; 360 // SGP.22 v2.0 EnableProfileResponse 361 result = parseSimpleResult(response); 362 switch (result) { 363 case CODE_OK: 364 return null; 365 case CODE_PROFILE_NOT_IN_EXPECTED_STATE: 366 logd("Profile is already enabled, iccid: " 367 + SubscriptionInfo.givePrintableIccid(iccid)); 368 return null; 369 default: 370 throw new EuiccCardErrorException( 371 EuiccCardErrorException.OPERATION_SWITCH_TO_PROFILE, result); 372 } 373 }, 374 callback, handler); 375 } 376 377 /** 378 * Gets the EID synchronously. 379 * @return The EID string. Returns null if it is not ready yet. 380 */ getEid()381 public String getEid() { 382 return mEid; 383 } 384 385 /** 386 * Gets the EID of the eUICC and overwrites mCardId in UiccCard. 387 * 388 * @param callback The callback to get the result. 389 * @param handler The handler to run the callback. 390 * @since 1.1.0 [GSMA SGP.22] 391 */ getEid(AsyncResultCallback<String> callback, Handler handler)392 public void getEid(AsyncResultCallback<String> callback, Handler handler) { 393 if (mEid != null) { 394 AsyncResultHelper.returnResult(mEid, callback, handler); 395 return; 396 } 397 sendApdu( 398 newRequestProvider((RequestBuilder requestBuilder) -> 399 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_EID) 400 .addChildAsBytes(Tags.TAG_TAG_LIST, new byte[] {Tags.TAG_EID}) 401 .build().toHex())), 402 response -> { 403 String eid = IccUtils.bytesToHexString(parseResponse(response) 404 .getChild(Tags.TAG_EID).asBytes()); 405 synchronized (mLock) { 406 mEid = eid; 407 mCardId = eid; 408 } 409 return eid; 410 }, 411 callback, handler); 412 } 413 414 /** 415 * Sets the nickname of a profile. 416 * 417 * @param callback The callback to get the result. 418 * @param handler The handler to run the callback. 419 * @since 1.1.0 [GSMA SGP.22] 420 */ setNickname(String iccid, String nickname, AsyncResultCallback<Void> callback, Handler handler)421 public void setNickname(String iccid, String nickname, AsyncResultCallback<Void> callback, 422 Handler handler) { 423 sendApdu( 424 newRequestProvider((RequestBuilder requestBuilder) -> 425 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_SET_NICKNAME) 426 .addChildAsBytes(Tags.TAG_ICCID, 427 IccUtils.bcdToBytes(padTrailingFs(iccid))) 428 .addChildAsString(Tags.TAG_NICKNAME, nickname) 429 .build().toHex())), 430 response -> { 431 // SGP.22 v2.0 SetNicknameResponse 432 int result = parseSimpleResult(response); 433 if (result != CODE_OK) { 434 throw new EuiccCardErrorException( 435 EuiccCardErrorException.OPERATION_SET_NICKNAME, result); 436 } 437 return null; 438 }, 439 callback, handler); 440 } 441 442 /** 443 * Deletes a profile from eUICC. 444 * 445 * @param callback The callback to get the result. 446 * @param handler The handler to run the callback. 447 * @since 1.1.0 [GSMA SGP.22] 448 */ deleteProfile(String iccid, AsyncResultCallback<Void> callback, Handler handler)449 public void deleteProfile(String iccid, AsyncResultCallback<Void> callback, Handler handler) { 450 sendApdu( 451 newRequestProvider((RequestBuilder requestBuilder) -> { 452 byte[] iccidBytes = IccUtils.bcdToBytes(padTrailingFs(iccid)); 453 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_DELETE_PROFILE) 454 .addChildAsBytes(Tags.TAG_ICCID, iccidBytes) 455 .build().toHex()); 456 }), 457 response -> { 458 // SGP.22 v2.0 DeleteProfileRequest 459 int result = parseSimpleResult(response); 460 if (result != CODE_OK) { 461 throw new EuiccCardErrorException( 462 EuiccCardErrorException.OPERATION_DELETE_PROFILE, result); 463 } 464 return null; 465 }, 466 callback, handler); 467 } 468 469 /** 470 * Resets the eUICC memory (e.g., remove all profiles). 471 * 472 * @param options Bits of the options of resetting which parts of the eUICC memory. 473 * @param callback The callback to get the result. 474 * @param handler The handler to run the callback. 475 * @since 1.1.0 [GSMA SGP.22] 476 */ resetMemory(@uiccCardManager.ResetOption int options, AsyncResultCallback<Void> callback, Handler handler)477 public void resetMemory(@EuiccCardManager.ResetOption int options, 478 AsyncResultCallback<Void> callback, Handler handler) { 479 sendApduWithSimResetErrorWorkaround( 480 newRequestProvider((RequestBuilder requestBuilder) -> 481 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_EUICC_MEMORY_RESET) 482 .addChildAsBits(Tags.TAG_CTX_2, options) 483 .build().toHex())), 484 response -> { 485 int result = parseSimpleResult(response); 486 if (result != CODE_OK && result != CODE_NOTHING_TO_DELETE) { 487 throw new EuiccCardErrorException( 488 EuiccCardErrorException.OPERATION_RESET_MEMORY, result); 489 } 490 return null; 491 }, 492 callback, handler); 493 } 494 495 /** 496 * Gets the default SM-DP+ address from eUICC. 497 * 498 * @param callback The callback to get the result. 499 * @param handler The handler to run the callback. 500 * @since 2.0.0 [GSMA SGP.22] 501 */ getDefaultSmdpAddress(AsyncResultCallback<String> callback, Handler handler)502 public void getDefaultSmdpAddress(AsyncResultCallback<String> callback, Handler handler) { 503 sendApdu( 504 newRequestProvider((RequestBuilder requestBuilder) -> 505 requestBuilder.addStoreData( 506 Asn1Node.newBuilder(Tags.TAG_GET_CONFIGURED_ADDRESSES) 507 .build().toHex())), 508 (byte[] response) -> parseResponse(response).getChild(Tags.TAG_CTX_0).asString(), 509 callback, handler); 510 } 511 512 /** 513 * Gets the SM-DS address from eUICC. 514 * 515 * @param callback The callback to get the result. 516 * @param handler The handler to run the callback. 517 * @since 2.0.0 [GSMA SGP.22] 518 */ getSmdsAddress(AsyncResultCallback<String> callback, Handler handler)519 public void getSmdsAddress(AsyncResultCallback<String> callback, Handler handler) { 520 sendApdu( 521 newRequestProvider((RequestBuilder requestBuilder) -> 522 requestBuilder.addStoreData( 523 Asn1Node.newBuilder(Tags.TAG_GET_CONFIGURED_ADDRESSES) 524 .build().toHex())), 525 (byte[] response) -> parseResponse(response).getChild(Tags.TAG_CTX_1).asString(), 526 callback, handler); 527 } 528 529 /** 530 * Sets the default SM-DP+ address of eUICC. 531 * 532 * @param callback The callback to get the result. 533 * @param handler The handler to run the callback. 534 * @since 2.0.0 [GSMA SGP.22] 535 */ setDefaultSmdpAddress(String defaultSmdpAddress, AsyncResultCallback<Void> callback, Handler handler)536 public void setDefaultSmdpAddress(String defaultSmdpAddress, AsyncResultCallback<Void> callback, 537 Handler handler) { 538 sendApdu( 539 newRequestProvider((RequestBuilder requestBuilder) -> 540 requestBuilder.addStoreData( 541 Asn1Node.newBuilder(Tags.TAG_SET_DEFAULT_SMDP_ADDRESS) 542 .addChildAsString(Tags.TAG_CTX_0, defaultSmdpAddress) 543 .build().toHex())), 544 response -> { 545 // SGP.22 v2.0 SetDefaultDpAddressResponse 546 int result = parseSimpleResult(response); 547 if (result != CODE_OK) { 548 throw new EuiccCardErrorException( 549 EuiccCardErrorException.OPERATION_SET_DEFAULT_SMDP_ADDRESS, result); 550 } 551 return null; 552 }, 553 callback, handler); 554 } 555 556 /** 557 * Gets Rules Authorisation Table. 558 * 559 * @param callback The callback to get the result. 560 * @param handler The handler to run the callback. 561 * @since 2.0.0 [GSMA SGP.22] 562 */ getRulesAuthTable(AsyncResultCallback<EuiccRulesAuthTable> callback, Handler handler)563 public void getRulesAuthTable(AsyncResultCallback<EuiccRulesAuthTable> callback, 564 Handler handler) { 565 sendApdu( 566 newRequestProvider((RequestBuilder requestBuilder) -> 567 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_RAT) 568 .build().toHex())), 569 response -> { 570 Asn1Node root = parseResponse(response); 571 List<Asn1Node> nodes = root.getChildren(Tags.TAG_CTX_COMP_0); 572 EuiccRulesAuthTable.Builder builder = 573 new EuiccRulesAuthTable.Builder(nodes.size()); 574 int size = nodes.size(); 575 for (int i = 0; i < size; i++) { 576 Asn1Node node = nodes.get(i); 577 List<Asn1Node> opIdNodes = 578 node.getChild(Tags.TAG_SEQUENCE, Tags.TAG_CTX_COMP_1).getChildren(); 579 int opIdSize = opIdNodes.size(); 580 CarrierIdentifier[] opIds = new CarrierIdentifier[opIdSize]; 581 for (int j = 0; j < opIdSize; j++) { 582 opIds[j] = buildCarrierIdentifier(opIdNodes.get(j)); 583 } 584 builder.add(node.getChild(Tags.TAG_SEQUENCE, Tags.TAG_CTX_0).asBits(), 585 Arrays.asList(opIds), node.getChild(Tags.TAG_SEQUENCE, 586 Tags.TAG_CTX_2).asBits()); 587 } 588 return builder.build(); 589 }, 590 callback, handler); 591 } 592 593 /** 594 * Gets the eUICC challenge for new profile downloading. 595 * 596 * @param callback The callback to get the result. 597 * @param handler The handler to run the callback. 598 * @since 2.0.0 [GSMA SGP.22] 599 */ getEuiccChallenge(AsyncResultCallback<byte[]> callback, Handler handler)600 public void getEuiccChallenge(AsyncResultCallback<byte[]> callback, Handler handler) { 601 sendApdu( 602 newRequestProvider((RequestBuilder requestBuilder) -> 603 requestBuilder.addStoreData( 604 Asn1Node.newBuilder(Tags.TAG_GET_EUICC_CHALLENGE) 605 .build().toHex())), 606 (byte[] response) -> parseResponse(response).getChild(Tags.TAG_CTX_0).asBytes(), 607 callback, handler); 608 } 609 610 /** 611 * Gets the eUICC info1 for new profile downloading. 612 * 613 * @param callback The callback to get the result, which represents an {@code EUICCInfo1} 614 * defined in GSMA RSP v2.0+. 615 * @param handler The handler to run the callback. 616 * @since 2.0.0 [GSMA SGP.22] 617 */ getEuiccInfo1(AsyncResultCallback<byte[]> callback, Handler handler)618 public void getEuiccInfo1(AsyncResultCallback<byte[]> callback, Handler handler) { 619 sendApdu( 620 newRequestProvider((RequestBuilder requestBuilder) -> 621 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_EUICC_INFO_1) 622 .build().toHex())), 623 (response) -> response, 624 callback, handler); 625 } 626 627 /** 628 * Gets the eUICC info2 for new profile downloading. 629 * 630 * @param callback The callback to get the result, which represents an {@code EUICCInfo2} 631 * defined in GSMA RSP v2.0+. 632 * @param handler The handler to run the callback. 633 * @since 2.0.0 [GSMA SGP.22] 634 */ getEuiccInfo2(AsyncResultCallback<byte[]> callback, Handler handler)635 public void getEuiccInfo2(AsyncResultCallback<byte[]> callback, Handler handler) { 636 sendApdu( 637 newRequestProvider((RequestBuilder requestBuilder) -> 638 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_EUICC_INFO_2) 639 .build().toHex())), 640 (response) -> response, 641 callback, handler); 642 } 643 644 /** 645 * Authenticates the SM-DP+ server by the eUICC. The parameters {@code serverSigned1}, {@code 646 * serverSignature1}, {@code euiccCiPkIdToBeUsed}, and {@code serverCertificate} are the ASN.1 647 * data returned by SM-DP+ server. 648 * 649 * @param matchingId The activation code or an empty string. 650 * @param callback The callback to get the result, which represents an {@code 651 * AuthenticateServerResponse} defined in GSMA RSP v2.0+. 652 * @param handler The handler to run the callback. 653 * @since 2.0.0 [GSMA SGP.22] 654 */ authenticateServer(String matchingId, byte[] serverSigned1, byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate, AsyncResultCallback<byte[]> callback, Handler handler)655 public void authenticateServer(String matchingId, byte[] serverSigned1, byte[] serverSignature1, 656 byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate, 657 AsyncResultCallback<byte[]> callback, Handler handler) { 658 sendApdu( 659 newRequestProvider((RequestBuilder requestBuilder) -> { 660 byte[] imeiBytes = getDeviceId(); 661 // TAC is the first 8 digits (4 bytes) of IMEI. 662 byte[] tacBytes = new byte[4]; 663 System.arraycopy(imeiBytes, 0, tacBytes, 0, 4); 664 665 Asn1Node.Builder devCapsBuilder = Asn1Node.newBuilder(Tags.TAG_CTX_COMP_1); 666 String[] devCapsStrings = getResources().getStringArray( 667 com.android.internal.R.array.config_telephonyEuiccDeviceCapabilities); 668 if (devCapsStrings != null) { 669 for (String devCapItem : devCapsStrings) { 670 addDeviceCapability(devCapsBuilder, devCapItem); 671 } 672 } else { 673 if (DBG) logd("No device capabilities set."); 674 } 675 676 Asn1Node.Builder ctxParams1Builder = Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0) 677 .addChildAsString(Tags.TAG_CTX_0, matchingId) 678 .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_1) 679 .addChildAsBytes(Tags.TAG_CTX_0, tacBytes) 680 .addChild(devCapsBuilder) 681 .addChildAsBytes(Tags.TAG_CTX_2, imeiBytes)); 682 683 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_AUTHENTICATE_SERVER) 684 .addChild(new Asn1Decoder(serverSigned1).nextNode()) 685 .addChild(new Asn1Decoder(serverSignature1).nextNode()) 686 .addChild(new Asn1Decoder(euiccCiPkIdToBeUsed).nextNode()) 687 .addChild(new Asn1Decoder(serverCertificate).nextNode()) 688 .addChild(ctxParams1Builder) 689 .build().toHex()); 690 }), 691 response -> { 692 Asn1Node root = parseResponse(response); 693 if (root.hasChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2)) { 694 throw new EuiccCardErrorException( 695 EuiccCardErrorException.OPERATION_AUTHENTICATE_SERVER, 696 root.getChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2).asInteger()); 697 } 698 return root.toBytes(); 699 }, 700 callback, handler); 701 } 702 703 /** 704 * Prepares the profile download request sent to SM-DP+. The parameters {@code smdpSigned2}, 705 * {@code smdpSignature2}, and {@code smdpCertificate} are the ASN.1 data returned by SM-DP+ 706 * server. 707 * 708 * @param hashCc The hash of confirmation code. It can be null if there is no confirmation code 709 * required. 710 * @param callback The callback to get the result, which represents an {@code 711 * PrepareDownloadResponse} defined in GSMA RSP v2.0+. 712 * @param handler The handler to run the callback. 713 * @since 2.0.0 [GSMA SGP.22] 714 */ prepareDownload(@ullable byte[] hashCc, byte[] smdpSigned2, byte[] smdpSignature2, byte[] smdpCertificate, AsyncResultCallback<byte[]> callback, Handler handler)715 public void prepareDownload(@Nullable byte[] hashCc, byte[] smdpSigned2, byte[] smdpSignature2, 716 byte[] smdpCertificate, AsyncResultCallback<byte[]> callback, Handler handler) { 717 sendApdu( 718 newRequestProvider((RequestBuilder requestBuilder) -> { 719 Asn1Node.Builder builder = Asn1Node.newBuilder(Tags.TAG_PREPARE_DOWNLOAD) 720 .addChild(new Asn1Decoder(smdpSigned2).nextNode()) 721 .addChild(new Asn1Decoder(smdpSignature2).nextNode()); 722 if (hashCc != null) { 723 builder.addChildAsBytes(Tags.TAG_UNI_4, hashCc); 724 } 725 requestBuilder.addStoreData( 726 builder.addChild(new Asn1Decoder(smdpCertificate).nextNode()) 727 .build().toHex()); 728 }), 729 response -> { 730 Asn1Node root = parseResponse(response); 731 if (root.hasChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2)) { 732 throw new EuiccCardErrorException( 733 EuiccCardErrorException.OPERATION_PREPARE_DOWNLOAD, 734 root.getChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2).asInteger()); 735 } 736 return root.toBytes(); 737 }, 738 callback, handler); 739 } 740 741 /** 742 * Loads a downloaded bound profile package onto the eUICC. 743 * 744 * @param boundProfilePackage The Bound Profile Package data returned by SM-DP+ server. 745 * @param callback The callback to get the result, which represents an {@code 746 * LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+. 747 * @param handler The handler to run the callback. 748 * @since 2.0.0 [GSMA SGP.22] 749 */ loadBoundProfilePackage(byte[] boundProfilePackage, AsyncResultCallback<byte[]> callback, Handler handler)750 public void loadBoundProfilePackage(byte[] boundProfilePackage, 751 AsyncResultCallback<byte[]> callback, Handler handler) { 752 sendApdu( 753 newRequestProvider((RequestBuilder requestBuilder) -> { 754 Asn1Node bppNode = new Asn1Decoder(boundProfilePackage).nextNode(); 755 // initialiseSecureChannelRequest (ES8+.InitialiseSecureChannel) 756 Asn1Node initialiseSecureChannelRequest = bppNode.getChild( 757 Tags.TAG_INITIALISE_SECURE_CHANNEL); 758 // firstSequenceOf87 (ES8+.ConfigureISDP) 759 Asn1Node firstSequenceOf87 = bppNode.getChild(Tags.TAG_CTX_COMP_0); 760 // sequenceOf88 (ES8+.StoreMetadata) 761 Asn1Node sequenceOf88 = bppNode.getChild(Tags.TAG_CTX_COMP_1); 762 List<Asn1Node> metaDataSeqs = sequenceOf88.getChildren(Tags.TAG_CTX_8); 763 // sequenceOf86 (ES8+.LoadProfileElements #1) 764 Asn1Node sequenceOf86 = bppNode.getChild(Tags.TAG_CTX_COMP_3); 765 List<Asn1Node> elementSeqs = sequenceOf86.getChildren(Tags.TAG_CTX_6); 766 767 requestBuilder.addStoreData(bppNode.getHeadAsHex() 768 + initialiseSecureChannelRequest.toHex()); 769 770 requestBuilder.addStoreData(firstSequenceOf87.toHex()); 771 772 requestBuilder.addStoreData(sequenceOf88.getHeadAsHex()); 773 int size = metaDataSeqs.size(); 774 for (int i = 0; i < size; i++) { 775 requestBuilder.addStoreData(metaDataSeqs.get(i).toHex()); 776 } 777 778 if (bppNode.hasChild(Tags.TAG_CTX_COMP_2)) { 779 requestBuilder.addStoreData(bppNode.getChild(Tags.TAG_CTX_COMP_2).toHex()); 780 } 781 782 requestBuilder.addStoreData(sequenceOf86.getHeadAsHex()); 783 size = elementSeqs.size(); 784 for (int i = 0; i < size; i++) { 785 requestBuilder.addStoreData(elementSeqs.get(i).toHex()); 786 } 787 }), 788 response -> { 789 // SGP.22 v2.0 ErrorResult 790 Asn1Node root = parseResponse(response); 791 if (root.hasChild(Tags.TAG_PROFILE_INSTALLATION_RESULT_DATA, 792 Tags.TAG_CTX_COMP_2, Tags.TAG_CTX_COMP_1, Tags.TAG_CTX_1)) { 793 Asn1Node errorNode = root.getChild( 794 Tags.TAG_PROFILE_INSTALLATION_RESULT_DATA, Tags.TAG_CTX_COMP_2, 795 Tags.TAG_CTX_COMP_1, Tags.TAG_CTX_1); 796 throw new EuiccCardErrorException( 797 EuiccCardErrorException.OPERATION_LOAD_BOUND_PROFILE_PACKAGE, 798 errorNode.asInteger(), errorNode); 799 } 800 return root.toBytes(); 801 }, 802 intermediateResult -> { 803 byte[] payload = intermediateResult.payload; 804 if (payload != null && payload.length > 2) { 805 int tag = (payload[0] & 0xFF) << 8 | (payload[1] & 0xFF); 806 // Stops if the installation result has been returned 807 if (tag == Tags.TAG_PROFILE_INSTALLATION_RESULT) { 808 logd("loadBoundProfilePackage failed due to an early error."); 809 return false; 810 } 811 } 812 return true; 813 }, 814 callback, handler); 815 } 816 817 /** 818 * Cancels the current profile download session. 819 * 820 * @param transactionId The transaction ID returned by SM-DP+ server. 821 * @param callback The callback to get the result, which represents an {@code 822 * CancelSessionResponse} defined in GSMA RSP v2.0+. 823 * @param handler The handler to run the callback. 824 * @since 2.0.0 [GSMA SGP.22] 825 */ cancelSession(byte[] transactionId, @EuiccCardManager.CancelReason int reason, AsyncResultCallback<byte[]> callback, Handler handler)826 public void cancelSession(byte[] transactionId, @EuiccCardManager.CancelReason int reason, 827 AsyncResultCallback<byte[]> callback, Handler handler) { 828 sendApdu( 829 newRequestProvider((RequestBuilder requestBuilder) -> 830 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_CANCEL_SESSION) 831 .addChildAsBytes(Tags.TAG_CTX_0, transactionId) 832 .addChildAsInteger(Tags.TAG_CTX_1, reason) 833 .build().toHex())), 834 (byte[] response) -> 835 parseResponseAndCheckSimpleError(response, 836 EuiccCardErrorException.OPERATION_CANCEL_SESSION).toBytes(), 837 callback, handler); 838 } 839 840 /** 841 * Lists all notifications of the given {@code notificationEvents}. 842 * 843 * @param events Bits of the event types ({@link EuiccNotification.Event}) to list. 844 * @param callback The callback to get the result. 845 * @param handler The handler to run the callback. 846 * @since 2.0.0 [GSMA SGP.22] 847 */ listNotifications(@uiccNotification.Event int events, AsyncResultCallback<EuiccNotification[]> callback, Handler handler)848 public void listNotifications(@EuiccNotification.Event int events, 849 AsyncResultCallback<EuiccNotification[]> callback, Handler handler) { 850 sendApdu( 851 newRequestProvider((RequestBuilder requestBuilder) -> 852 requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_LIST_NOTIFICATION) 853 .addChildAsBits(Tags.TAG_CTX_1, events) 854 .build().toHex())), 855 response -> { 856 Asn1Node root = parseResponseAndCheckSimpleError(response, 857 EuiccCardErrorException.OPERATION_LIST_NOTIFICATIONS); 858 List<Asn1Node> nodes = root.getChild(Tags.TAG_CTX_COMP_0).getChildren(); 859 EuiccNotification[] notifications = new EuiccNotification[nodes.size()]; 860 for (int i = 0; i < notifications.length; ++i) { 861 notifications[i] = createNotification(nodes.get(i)); 862 } 863 return notifications; 864 }, 865 callback, handler); 866 } 867 868 /** 869 * Retrieves contents of all notification of the given {@code events}. 870 * 871 * @param events Bits of the event types ({@link EuiccNotification.Event}) to list. 872 * @param callback The callback to get the result. 873 * @param handler The handler to run the callback. 874 * @since 2.0.0 [GSMA SGP.22] 875 */ retrieveNotificationList(@uiccNotification.Event int events, AsyncResultCallback<EuiccNotification[]> callback, Handler handler)876 public void retrieveNotificationList(@EuiccNotification.Event int events, 877 AsyncResultCallback<EuiccNotification[]> callback, Handler handler) { 878 sendApdu( 879 newRequestProvider((RequestBuilder requestBuilder) -> 880 requestBuilder.addStoreData( 881 Asn1Node.newBuilder(Tags.TAG_RETRIEVE_NOTIFICATIONS_LIST) 882 .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0) 883 .addChildAsBits(Tags.TAG_CTX_1, events)) 884 .build().toHex())), 885 response -> { 886 Asn1Node root = parseResponse(response); 887 if (root.hasChild(Tags.TAG_CTX_1)) { 888 // SGP.22 v2.0 RetrieveNotificationsListResponse 889 int error = root.getChild(Tags.TAG_CTX_1).asInteger(); 890 switch (error) { 891 case CODE_NO_RESULT_AVAILABLE: 892 return new EuiccNotification[0]; 893 default: 894 throw new EuiccCardErrorException( 895 EuiccCardErrorException.OPERATION_RETRIEVE_NOTIFICATION, 896 error); 897 } 898 } 899 List<Asn1Node> nodes = root.getChild(Tags.TAG_CTX_COMP_0).getChildren(); 900 EuiccNotification[] notifications = new EuiccNotification[nodes.size()]; 901 for (int i = 0; i < notifications.length; ++i) { 902 notifications[i] = createNotification(nodes.get(i)); 903 } 904 return notifications; 905 }, 906 callback, handler); 907 } 908 909 /** 910 * Retrieves the content of a notification of the given {@code seqNumber}. 911 * 912 * @param seqNumber The sequence number of the notification. 913 * @param callback The callback to get the result. 914 * @param handler The handler to run the callback. 915 * @since 2.0.0 [GSMA SGP.22] 916 */ retrieveNotification(int seqNumber, AsyncResultCallback<EuiccNotification> callback, Handler handler)917 public void retrieveNotification(int seqNumber, AsyncResultCallback<EuiccNotification> callback, 918 Handler handler) { 919 sendApdu( 920 newRequestProvider((RequestBuilder requestBuilder) -> 921 requestBuilder.addStoreData( 922 Asn1Node.newBuilder(Tags.TAG_RETRIEVE_NOTIFICATIONS_LIST) 923 .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0) 924 .addChildAsInteger(Tags.TAG_CTX_0, seqNumber)) 925 .build().toHex())), 926 response -> { 927 Asn1Node root = parseResponseAndCheckSimpleError(response, 928 EuiccCardErrorException.OPERATION_RETRIEVE_NOTIFICATION); 929 List<Asn1Node> nodes = root.getChild(Tags.TAG_CTX_COMP_0).getChildren(); 930 if (nodes.size() > 0) { 931 return createNotification(nodes.get(0)); 932 } 933 return null; 934 }, 935 callback, handler); 936 } 937 938 /** 939 * Removes a notification from eUICC. 940 * 941 * @param seqNumber The sequence number of the notification. 942 * @param callback The callback to get the result. 943 * @param handler The handler to run the callback. 944 * @since 2.0.0 [GSMA SGP.22] 945 */ removeNotificationFromList(int seqNumber, AsyncResultCallback<Void> callback, Handler handler)946 public void removeNotificationFromList(int seqNumber, AsyncResultCallback<Void> callback, 947 Handler handler) { 948 sendApdu( 949 newRequestProvider((RequestBuilder requestBuilder) -> 950 requestBuilder.addStoreData( 951 Asn1Node.newBuilder(Tags.TAG_REMOVE_NOTIFICATION_FROM_LIST) 952 .addChildAsInteger(Tags.TAG_CTX_0, seqNumber) 953 .build().toHex())), 954 response -> { 955 // SGP.22 v2.0 NotificationSentResponse 956 int result = parseSimpleResult(response); 957 if (result != CODE_OK && result != CODE_NOTHING_TO_DELETE) { 958 throw new EuiccCardErrorException( 959 EuiccCardErrorException.OPERATION_REMOVE_NOTIFICATION_FROM_LIST, 960 result); 961 } 962 return null; 963 }, 964 callback, handler); 965 } 966 967 /** 968 * Sets a device capability version as the child of the given device capability ASN1 node 969 * builder. 970 * 971 * @param devCapBuilder The ASN1 node builder to modify. 972 * @param devCapItem The device capability and its supported version in pair. 973 */ 974 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) addDeviceCapability(Asn1Node.Builder devCapBuilder, String devCapItem)975 public void addDeviceCapability(Asn1Node.Builder devCapBuilder, String devCapItem) { 976 String[] split = devCapItem.split(","); 977 if (split.length != 2) { 978 loge("Invalid device capability item: " + Arrays.toString(split)); 979 return; 980 } 981 982 String devCap = split[0].trim(); 983 Integer version; 984 try { 985 version = Integer.parseInt(split[1].trim()); 986 } catch (NumberFormatException e) { 987 loge("Invalid device capability version number.", e); 988 return; 989 } 990 991 byte[] versionBytes = new byte[] { version.byteValue(), 0, 0 }; 992 switch (devCap) { 993 case DEV_CAP_GSM: 994 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_0, versionBytes); 995 break; 996 case DEV_CAP_UTRAN: 997 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_1, versionBytes); 998 break; 999 case DEV_CAP_CDMA_1X: 1000 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_2, versionBytes); 1001 break; 1002 case DEV_CAP_HRPD: 1003 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_3, versionBytes); 1004 break; 1005 case DEV_CAP_EHRPD: 1006 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_4, versionBytes); 1007 break; 1008 case DEV_CAP_EUTRAN: 1009 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_5, versionBytes); 1010 break; 1011 case DEV_CAP_NFC: 1012 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_6, versionBytes); 1013 break; 1014 case DEV_CAP_CRL: 1015 devCapBuilder.addChildAsBytes(Tags.TAG_CTX_7, versionBytes); 1016 break; 1017 default: 1018 loge("Invalid device capability name: " + devCap); 1019 break; 1020 } 1021 } 1022 1023 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) getDeviceId()1024 protected byte[] getDeviceId() { 1025 Phone phone = PhoneFactory.getPhone(getPhoneId()); 1026 if (phone == null) { 1027 return new byte[8]; 1028 } 1029 return getDeviceId(phone.getDeviceId(), mSpecVersion); 1030 } 1031 1032 /** 1033 * Different versions of SGP.22 specify different encodings of the device's IMEI, so we handle 1034 * those differences here. 1035 * 1036 * @param imei The IMEI of the device. Assumed to be 15 decimal digits. 1037 * @param specVersion The SGP.22 version which we're encoding the IMEI for. 1038 * @return A byte string representing the given IMEI according to the specified SGP.22 version. 1039 */ 1040 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) getDeviceId(String imei, EuiccSpecVersion specVersion)1041 public static byte[] getDeviceId(String imei, EuiccSpecVersion specVersion) { 1042 byte[] imeiBytes = new byte[8]; 1043 // The IMEI's encoding is version-dependent. 1044 if (specVersion.compareTo(SGP22_V_2_1) >= 0) { 1045 /* 1046 * In SGP.22 v2.1, a clarification was added to clause 4.2 that requires the nibbles of 1047 * the last byte to be swapped from normal TBCD encoding (so put back in normal order): 1048 * 1049 * The IMEI (including the check digit) SHALL be represented as a string of 8 octets 1050 * that is coded as a Telephony Binary Coded Decimal String as defined in 3GPP TS 29.002 1051 * [63], except that the last octet contains the check digit (in high nibble) and an 'F' 1052 * filler (in low nibble). It SHOULD be present if the Device contains a non-removable 1053 * eUICC. 1054 * 1055 * 3GPP TS 29.002 clause 17.7.8 in turn says this: 1056 * 1057 * TBCD-STRING ::= OCTET STRING 1058 * This type (Telephony Binary Coded Decimal String) is used to represent several digits 1059 * from 0 through 9, *, #, a, b, c, two digits per octet, each digit encoded 0000 to 1060 * 1001 (0 to 9), 1010 (*), 1011 (#), 1100 (a), 1101 (b) or 1110 (c); 1111 used as 1061 * filler when there is an odd number of digits. 1062 * Bits 8765 of octet n encoding digit 2n 1063 * Bits 4321 of octet n encoding digit 2(n-1) + 1 1064 */ 1065 // Since the IMEI is always just decimal digits, we can still use BCD encoding (which 1066 // correctly swaps digit ordering within bytes), but we have to manually pad a 0xF value 1067 // instead of 0. 1068 imei += 'F'; 1069 IccUtils.bcdToBytes(imei, imeiBytes); 1070 // And now the funky last byte flip (this is not normal TBCD, the GSMA added it on top 1071 // just for the IMEI for some reason). Bitwise operations promote to int first, so we 1072 // have to do some extra masking. 1073 byte last = imeiBytes[7]; 1074 imeiBytes[7] = (byte) ((last & 0xFF) << 4 | ((last & 0xFF) >>> 4)); 1075 } else { 1076 /* 1077 * Prior to SGP.22 v2.1, clause 4.2 reads as follows: 1078 * 1079 * The IMEI (including the check digit) SHALL be represented as a string of 8 octets 1080 * that is BCD coded as defined in 3GPP TS 23.003 [35]. It SHOULD be present if the 1081 * Device contains a non-removable eUICC. 1082 * 1083 * It appears that 3GPP TS 23.003 doesn't define anything about BCD encoding, it just 1084 * defines what IMEI and a few other telephony identifiers are. We default to normal BCD 1085 * encoding since the spec is unclear here. 1086 */ 1087 IccUtils.bcdToBytes(imei, imeiBytes); 1088 } 1089 return imeiBytes; 1090 } 1091 1092 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) getResources()1093 protected Resources getResources() { 1094 return Resources.getSystem(); 1095 } 1096 newRequestProvider(ApduRequestBuilder builder)1097 private RequestProvider newRequestProvider(ApduRequestBuilder builder) { 1098 return (selectResponse, requestBuilder) -> { 1099 EuiccSpecVersion ver = getOrExtractSpecVersion(selectResponse); 1100 if (ver == null) { 1101 throw new EuiccCardException("Cannot get eUICC spec version."); 1102 } 1103 try { 1104 if (ver.compareTo(SGP22_V_2_0) < 0) { 1105 throw new EuiccCardException("eUICC spec version is unsupported: " + ver); 1106 } 1107 builder.build(requestBuilder); 1108 } catch (InvalidAsn1DataException | TagNotFoundException e) { 1109 throw new EuiccCardException("Cannot parse ASN1 to build request.", e); 1110 } 1111 }; 1112 } 1113 getOrExtractSpecVersion(byte[] selectResponse)1114 private EuiccSpecVersion getOrExtractSpecVersion(byte[] selectResponse) { 1115 // Uses the cached version. 1116 if (mSpecVersion != null) { 1117 return mSpecVersion; 1118 } 1119 // Parses and caches the version. 1120 EuiccSpecVersion ver = EuiccSpecVersion.fromOpenChannelResponse(selectResponse); 1121 if (ver != null) { 1122 synchronized (mLock) { 1123 if (mSpecVersion == null) { 1124 mSpecVersion = ver; 1125 } 1126 } 1127 } 1128 return ver; 1129 } 1130 1131 /** 1132 * A wrapper on {@link ApduSender#send(RequestProvider, ApduSenderResultCallback, Handler)} to 1133 * leverage lambda to simplify the sending APDU code.EuiccCardErrorException. 1134 * 1135 * @param requestBuilder Builds the request of APDU commands. 1136 * @param responseHandler Converts the APDU response from bytes to expected result. 1137 * @param <T> Type of the originally expected result. 1138 */ sendApdu(RequestProvider requestBuilder, ApduResponseHandler<T> responseHandler, AsyncResultCallback<T> callback, Handler handler)1139 private <T> void sendApdu(RequestProvider requestBuilder, 1140 ApduResponseHandler<T> responseHandler, AsyncResultCallback<T> callback, 1141 Handler handler) { 1142 sendApdu(requestBuilder, responseHandler, 1143 (e) -> callback.onException(new EuiccCardException("Cannot send APDU.", e)), 1144 null, callback, handler); 1145 } 1146 sendApdu(RequestProvider requestBuilder, ApduResponseHandler<T> responseHandler, ApduIntermediateResultHandler intermediateResultHandler, AsyncResultCallback<T> callback, Handler handler)1147 private <T> void sendApdu(RequestProvider requestBuilder, 1148 ApduResponseHandler<T> responseHandler, 1149 ApduIntermediateResultHandler intermediateResultHandler, 1150 AsyncResultCallback<T> callback, Handler handler) { 1151 sendApdu(requestBuilder, responseHandler, 1152 (e) -> callback.onException(new EuiccCardException("Cannot send APDU.", e)), 1153 intermediateResultHandler, callback, handler); 1154 } 1155 1156 /** 1157 * This is a workaround solution to the bug that a SIM refresh may interrupt the modem to return 1158 * the reset of responses of the original APDU command. This applies to disable profile, switch 1159 * profile, and reset eUICC memory. 1160 * 1161 * <p>TODO: Use 1162 * {@link #sendApdu(RequestProvider, ApduResponseHandler, AsyncResultCallback, Handler)} when 1163 * this workaround is not needed. 1164 */ sendApduWithSimResetErrorWorkaround( RequestProvider requestBuilder, ApduResponseHandler<Void> responseHandler, AsyncResultCallback<Void> callback, Handler handler)1165 private void sendApduWithSimResetErrorWorkaround( 1166 RequestProvider requestBuilder, ApduResponseHandler<Void> responseHandler, 1167 AsyncResultCallback<Void> callback, Handler handler) { 1168 sendApdu(requestBuilder, responseHandler, (e) -> { 1169 if (e instanceof ApduException 1170 && ((ApduException) e).getApduStatus() == APDU_ERROR_SIM_REFRESH) { 1171 logi("Sim is refreshed after disabling profile, no response got."); 1172 callback.onResult(null); 1173 } else { 1174 callback.onException(new EuiccCardException("Cannot send APDU.", e)); 1175 } 1176 }, null, callback, handler); 1177 } 1178 sendApdu(RequestProvider requestBuilder, ApduResponseHandler<T> responseHandler, ApduExceptionHandler exceptionHandler, @Nullable ApduIntermediateResultHandler intermediateResultHandler, AsyncResultCallback<T> callback, Handler handler)1179 private <T> void sendApdu(RequestProvider requestBuilder, 1180 ApduResponseHandler<T> responseHandler, 1181 ApduExceptionHandler exceptionHandler, 1182 @Nullable ApduIntermediateResultHandler intermediateResultHandler, 1183 AsyncResultCallback<T> callback, 1184 Handler handler) { 1185 mApduSender.send(requestBuilder, new ApduSenderResultCallback() { 1186 @Override 1187 public void onResult(byte[] response) { 1188 try { 1189 callback.onResult(responseHandler.handleResult(response)); 1190 } catch (EuiccCardException e) { 1191 callback.onException(e); 1192 } catch (InvalidAsn1DataException | TagNotFoundException e) { 1193 callback.onException(new EuiccCardException( 1194 "Cannot parse response: " + IccUtils.bytesToHexString(response), e)); 1195 } 1196 } 1197 1198 @Override 1199 public boolean shouldContinueOnIntermediateResult(IccIoResult result) { 1200 if (intermediateResultHandler == null) { 1201 return true; 1202 } 1203 return intermediateResultHandler.shouldContinue(result); 1204 } 1205 1206 @Override 1207 public void onException(Throwable e) { 1208 exceptionHandler.handleException(e); 1209 } 1210 }, handler); 1211 } 1212 buildProfile(Asn1Node profileNode, EuiccProfileInfo.Builder profileBuilder)1213 private static void buildProfile(Asn1Node profileNode, EuiccProfileInfo.Builder profileBuilder) 1214 throws TagNotFoundException, InvalidAsn1DataException { 1215 if (profileNode.hasChild(Tags.TAG_NICKNAME)) { 1216 profileBuilder.setNickname(profileNode.getChild(Tags.TAG_NICKNAME).asString()); 1217 } 1218 1219 if (profileNode.hasChild(Tags.TAG_SERVICE_PROVIDER_NAME)) { 1220 profileBuilder.setServiceProviderName( 1221 profileNode.getChild(Tags.TAG_SERVICE_PROVIDER_NAME).asString()); 1222 } 1223 1224 if (profileNode.hasChild(Tags.TAG_PROFILE_NAME)) { 1225 profileBuilder.setProfileName( 1226 profileNode.getChild(Tags.TAG_PROFILE_NAME).asString()); 1227 } 1228 1229 if (profileNode.hasChild(Tags.TAG_OPERATOR_ID)) { 1230 profileBuilder.setCarrierIdentifier( 1231 buildCarrierIdentifier(profileNode.getChild(Tags.TAG_OPERATOR_ID))); 1232 } 1233 1234 if (profileNode.hasChild(Tags.TAG_PROFILE_STATE)) { 1235 // noinspection WrongConstant 1236 profileBuilder.setState(profileNode.getChild(Tags.TAG_PROFILE_STATE).asInteger()); 1237 } else { 1238 profileBuilder.setState(EuiccProfileInfo.PROFILE_STATE_DISABLED); 1239 } 1240 1241 if (profileNode.hasChild(Tags.TAG_PROFILE_CLASS)) { 1242 // noinspection WrongConstant 1243 profileBuilder.setProfileClass( 1244 profileNode.getChild(Tags.TAG_PROFILE_CLASS).asInteger()); 1245 } else { 1246 profileBuilder.setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL); 1247 } 1248 1249 if (profileNode.hasChild(Tags.TAG_PROFILE_POLICY_RULE)) { 1250 // noinspection WrongConstant 1251 profileBuilder.setPolicyRules( 1252 profileNode.getChild(Tags.TAG_PROFILE_POLICY_RULE).asBits()); 1253 } 1254 1255 if (profileNode.hasChild(Tags.TAG_CARRIER_PRIVILEGE_RULES)) { 1256 List<Asn1Node> refArDoNodes = profileNode.getChild(Tags.TAG_CARRIER_PRIVILEGE_RULES) 1257 .getChildren(Tags.TAG_REF_AR_DO); 1258 UiccAccessRule[] rules = buildUiccAccessRule(refArDoNodes); 1259 List<UiccAccessRule> rulesList = null; 1260 if (rules != null) { 1261 rulesList = Arrays.asList(rules); 1262 } 1263 profileBuilder.setUiccAccessRule(rulesList); 1264 } 1265 } 1266 buildCarrierIdentifier(Asn1Node node)1267 private static CarrierIdentifier buildCarrierIdentifier(Asn1Node node) 1268 throws InvalidAsn1DataException, TagNotFoundException { 1269 String gid1 = null; 1270 if (node.hasChild(Tags.TAG_CTX_1)) { 1271 gid1 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_1).asBytes()); 1272 } 1273 String gid2 = null; 1274 if (node.hasChild(Tags.TAG_CTX_2)) { 1275 gid2 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_2).asBytes()); 1276 } 1277 return new CarrierIdentifier(node.getChild(Tags.TAG_CTX_0).asBytes(), gid1, gid2); 1278 } 1279 1280 @Nullable buildUiccAccessRule(List<Asn1Node> nodes)1281 private static UiccAccessRule[] buildUiccAccessRule(List<Asn1Node> nodes) 1282 throws InvalidAsn1DataException, TagNotFoundException { 1283 if (nodes.isEmpty()) { 1284 return null; 1285 } 1286 int count = nodes.size(); 1287 UiccAccessRule[] rules = new UiccAccessRule[count]; 1288 for (int i = 0; i < count; i++) { 1289 Asn1Node node = nodes.get(i); 1290 Asn1Node refDoNode = node.getChild(Tags.TAG_REF_DO); 1291 byte[] signature = refDoNode.getChild(Tags.TAG_DEVICE_APP_ID_REF_DO).asBytes(); 1292 1293 String packageName = null; 1294 if (refDoNode.hasChild(Tags.TAG_PKG_REF_DO)) { 1295 packageName = refDoNode.getChild(Tags.TAG_PKG_REF_DO).asString(); 1296 } 1297 long accessType = 0; 1298 if (node.hasChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO)) { 1299 Asn1Node permArDoNode = node.getChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO); 1300 accessType = permArDoNode.asRawLong(); 1301 } 1302 rules[i] = new UiccAccessRule(signature, packageName, accessType); 1303 } 1304 return rules; 1305 } 1306 1307 /** 1308 * Creates an instance from the ASN.1 data. 1309 * 1310 * @param node This should be either {@code NotificationMetadata} or {@code PendingNotification} 1311 * defined by SGP.22 v2.0. 1312 * @throws TagNotFoundException If no notification tag is found in the bytes. 1313 * @throws InvalidAsn1DataException If no valid data is found in the bytes. 1314 */ createNotification(Asn1Node node)1315 private static EuiccNotification createNotification(Asn1Node node) 1316 throws TagNotFoundException, InvalidAsn1DataException { 1317 Asn1Node metadataNode; 1318 if (node.getTag() == Tags.TAG_NOTIFICATION_METADATA) { 1319 metadataNode = node; 1320 } else if (node.getTag() == Tags.TAG_PROFILE_INSTALLATION_RESULT) { 1321 metadataNode = node.getChild(Tags.TAG_PROFILE_INSTALLATION_RESULT_DATA, 1322 Tags.TAG_NOTIFICATION_METADATA); 1323 } else { 1324 // Other signed notification 1325 metadataNode = node.getChild(Tags.TAG_NOTIFICATION_METADATA); 1326 } 1327 // noinspection WrongConstant 1328 return new EuiccNotification(metadataNode.getChild(Tags.TAG_SEQ).asInteger(), 1329 metadataNode.getChild(Tags.TAG_TARGET_ADDR).asString(), 1330 metadataNode.getChild(Tags.TAG_EVENT).asBits(), 1331 node.getTag() == Tags.TAG_NOTIFICATION_METADATA ? null : node.toBytes()); 1332 } 1333 1334 /** Returns the first CONTEXT [0] as an integer. */ parseSimpleResult(byte[] response)1335 private static int parseSimpleResult(byte[] response) 1336 throws EuiccCardException, TagNotFoundException, InvalidAsn1DataException { 1337 return parseResponse(response).getChild(Tags.TAG_CTX_0).asInteger(); 1338 } 1339 parseResponse(byte[] response)1340 private static Asn1Node parseResponse(byte[] response) 1341 throws EuiccCardException, InvalidAsn1DataException { 1342 Asn1Decoder decoder = new Asn1Decoder(response); 1343 if (!decoder.hasNextNode()) { 1344 throw new EuiccCardException("Empty response", null); 1345 } 1346 return decoder.nextNode(); 1347 } 1348 1349 /** 1350 * Parses the bytes into an ASN1 node and check if there is an error code represented at the 1351 * context 1 tag. If there is an error code, an {@link EuiccCardErrorException} will be thrown 1352 * with the given operation code. 1353 */ parseResponseAndCheckSimpleError(byte[] response, @OperationCode int opCode)1354 private static Asn1Node parseResponseAndCheckSimpleError(byte[] response, 1355 @OperationCode int opCode) 1356 throws EuiccCardException, InvalidAsn1DataException, TagNotFoundException { 1357 Asn1Node root = parseResponse(response); 1358 if (root.hasChild(Tags.TAG_CTX_1)) { 1359 throw new EuiccCardErrorException(opCode, root.getChild(Tags.TAG_CTX_1).asInteger()); 1360 } 1361 return root; 1362 } 1363 1364 /** Strip all the trailing 'F' characters of an iccId. */ stripTrailingFs(byte[] iccId)1365 private static String stripTrailingFs(byte[] iccId) { 1366 return IccUtils.stripTrailingFs(IccUtils.bchToString(iccId, 0, iccId.length)); 1367 } 1368 1369 /** Pad an iccId with trailing 'F' characters until the length is 20. */ padTrailingFs(String iccId)1370 private static String padTrailingFs(String iccId) { 1371 if (!TextUtils.isEmpty(iccId) && iccId.length() < ICCID_LENGTH) { 1372 iccId += new String(new char[20 - iccId.length()]).replace('\0', 'F'); 1373 } 1374 return iccId; 1375 } 1376 loge(String message)1377 private static void loge(String message) { 1378 Rlog.e(LOG_TAG, message); 1379 } 1380 loge(String message, Throwable tr)1381 private static void loge(String message, Throwable tr) { 1382 Rlog.e(LOG_TAG, message, tr); 1383 } 1384 logi(String message)1385 private static void logi(String message) { 1386 Rlog.i(LOG_TAG, message); 1387 } 1388 logd(String message)1389 private static void logd(String message) { 1390 if (DBG) { 1391 Rlog.d(LOG_TAG, message); 1392 } 1393 } 1394 1395 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1396 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1397 super.dump(fd, pw, args); 1398 pw.println("EuiccCard:"); 1399 pw.println(" mEid=" + mEid); 1400 } 1401 } 1402