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