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