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