• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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