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