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