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