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