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