Loading src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java +4 −13 Original line number Diff line number Diff line Loading @@ -343,11 +343,11 @@ public class SubscriptionInfoUpdater extends Handler { String iccId = mIccId[slotId]; if (iccId == null) { IccRecords records = mPhone[slotId].getIccCard().getIccRecords(); if (stripIccIdSuffix(records.getFullIccId()) == null) { if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) { logd("handleSimLocked: IccID null"); return; } mIccId[slotId] = stripIccIdSuffix(records.getFullIccId()); mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId()); } else { logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId); } Loading Loading @@ -390,11 +390,11 @@ public class SubscriptionInfoUpdater extends Handler { logd("handleSimLoaded: IccRecords null"); return; } if (stripIccIdSuffix(records.getFullIccId()) == null) { if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) { logd("handleSimLoaded: IccID null"); return; } mIccId[slotId] = stripIccIdSuffix(records.getFullIccId()); mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId()); if (isAllIccIdQueryDone()) { updateSubscriptionInfoByIccId(); Loading Loading @@ -896,15 +896,6 @@ public class SubscriptionInfoUpdater extends Handler { } } // Remove trailing F's from full hexadecimal IccId, as they should be considered padding private String stripIccIdSuffix(String hexIccId) { if (hexIccId == null) { return null; } else { return hexIccId.replaceAll("(?i)f*$", ""); } } public void dispose() { logd("[dispose]"); mContext.unregisterReceiver(sReceiver); Loading src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java +137 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,10 @@ package com.android.internal.telephony.uicc.euicc; import android.annotation.Nullable; import android.content.Context; import android.os.Handler; import android.service.carrier.CarrierIdentifier; import android.service.euicc.EuiccProfileInfo; import android.telephony.Rlog; import android.telephony.UiccAccessRule; import android.telephony.euicc.EuiccCardManager; import android.telephony.euicc.EuiccNotification; import android.telephony.euicc.EuiccRulesAuthTable; Loading @@ -28,6 +31,8 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.uicc.IccCardStatus; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccCard; import com.android.internal.telephony.uicc.asn1.Asn1Decoder; import com.android.internal.telephony.uicc.asn1.Asn1Node; import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException; import com.android.internal.telephony.uicc.asn1.TagNotFoundException; import com.android.internal.telephony.uicc.euicc.apdu.ApduSender; Loading @@ -36,11 +41,15 @@ import com.android.internal.telephony.uicc.euicc.apdu.RequestProvider; import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper; import java.util.List; /** * This represents an eUICC card to perform profile management operations asynchronously. This class * includes methods defined by different versions of GSMA Spec (SGP.22). */ public class EuiccCard extends UiccCard { private static final String LOG_TAG = "EuiccCard"; private static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100"; private static final EuiccSpecVersion SGP_2_0 = new EuiccSpecVersion(2, 0, 0); Loading Loading @@ -88,7 +97,32 @@ public class EuiccCard extends UiccCard { * @since 1.1.0 [GSMA SGP.22] */ public void getAllProfiles(AsyncResultCallback<EuiccProfileInfo[]> callback, Handler handler) { // TODO: to be implemented. sendApdu( newRequestProvider((RequestBuilder requestBuilder) -> requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES) .addChildAsBytes(Tags.TAG_TAG_LIST, Tags.EUICC_PROFILE_TAGS) .build().toHex())), (byte[] response) -> { List<Asn1Node> profileNodes = new Asn1Decoder(response).nextNode() .getChild(Tags.TAG_CTX_COMP_0).getChildren(Tags.TAG_PROFILE_INFO); int size = profileNodes.size(); EuiccProfileInfo[] profiles = new EuiccProfileInfo[size]; int profileCount = 0; for (int i = 0; i < size; i++) { Asn1Node profileNode = profileNodes.get(i); if (!profileNode.hasChild(Tags.TAG_ICCID)) { loge("Profile must have an ICCID."); continue; } EuiccProfileInfo.Builder profileBuilder = new EuiccProfileInfo.Builder(); buildProfile(profileNode, profileBuilder); EuiccProfileInfo profile = profileBuilder.build(); profiles[profileCount++] = profile; } return profiles; }, callback, handler); } /** Loading Loading @@ -436,4 +470,106 @@ public class EuiccCard extends UiccCard { } }, handler); } private static void buildProfile(Asn1Node profileNode, EuiccProfileInfo.Builder profileBuilder) throws TagNotFoundException, InvalidAsn1DataException { String strippedIccIdString = stripTrailingFs(profileNode.getChild(Tags.TAG_ICCID).asBytes()); profileBuilder.setIccid(strippedIccIdString); if (profileNode.hasChild(Tags.TAG_NICKNAME)) { profileBuilder.setNickname(profileNode.getChild(Tags.TAG_NICKNAME).asString()); } if (profileNode.hasChild(Tags.TAG_SERVICE_PROVIDER_NAME)) { profileBuilder.setServiceProviderName( profileNode.getChild(Tags.TAG_SERVICE_PROVIDER_NAME).asString()); } if (profileNode.hasChild(Tags.TAG_PROFILE_NAME)) { profileBuilder.setProfileName( profileNode.getChild(Tags.TAG_PROFILE_NAME).asString()); } if (profileNode.hasChild(Tags.TAG_OPERATOR_ID)) { profileBuilder.setCarrierIdentifier( buildCarrierIdentifier(profileNode.getChild(Tags.TAG_OPERATOR_ID))); } if (profileNode.hasChild(Tags.TAG_PROFILE_STATE)) { // noinspection WrongConstant profileBuilder.setState(profileNode.getChild(Tags.TAG_PROFILE_STATE).asInteger()); } else { profileBuilder.setState(EuiccProfileInfo.PROFILE_STATE_DISABLED); } if (profileNode.hasChild(Tags.TAG_PROFILE_CLASS)) { // noinspection WrongConstant profileBuilder.setProfileClass( profileNode.getChild(Tags.TAG_PROFILE_CLASS).asInteger()); } else { profileBuilder.setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL); } if (profileNode.hasChild(Tags.TAG_PROFILE_POLICY_RULE)) { // noinspection WrongConstant profileBuilder.setPolicyRules( profileNode.getChild(Tags.TAG_PROFILE_POLICY_RULE).asBits()); } if (profileNode.hasChild(Tags.TAG_CARRIER_PRIVILEGE_RULES)) { List<Asn1Node> refArDoNodes = profileNode.getChild(Tags.TAG_CARRIER_PRIVILEGE_RULES) .getChildren(Tags.TAG_REF_AR_DO); profileBuilder.setUiccAccessRule(buildUiccAccessRule(refArDoNodes)); } } private static CarrierIdentifier buildCarrierIdentifier(Asn1Node node) throws InvalidAsn1DataException, TagNotFoundException { String gid1 = null; if (node.hasChild(Tags.TAG_CTX_1)) { gid1 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_1).asBytes()); } String gid2 = null; if (node.hasChild(Tags.TAG_CTX_2)) { gid2 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_2).asBytes()); } return new CarrierIdentifier(node.getChild(Tags.TAG_CTX_0).asBytes(), gid1, gid2); } @Nullable private static UiccAccessRule[] buildUiccAccessRule(List<Asn1Node> nodes) throws InvalidAsn1DataException, TagNotFoundException { if (nodes.isEmpty()) { return null; } int count = nodes.size(); UiccAccessRule[] rules = new UiccAccessRule[count]; for (int i = 0; i < count; i++) { Asn1Node node = nodes.get(i); Asn1Node refDoNode = node.getChild(Tags.TAG_REF_DO); byte[] signature = refDoNode.getChild(Tags.TAG_DEVICE_APP_ID_REF_DO).asBytes(); String packageName = null; if (refDoNode.hasChild(Tags.TAG_PKG_REF_DO)) { packageName = refDoNode.getChild(Tags.TAG_PKG_REF_DO).asString(); } long accessType = 0; if (node.hasChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO)) { Asn1Node permArDoNode = node.getChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO); accessType = permArDoNode.asRawLong(); } rules[i] = new UiccAccessRule(signature, packageName, accessType); } return rules; } /** Strip all the trailing 'F' characters of an iccId. */ private static String stripTrailingFs(byte[] iccId) { return IccUtils.stripTrailingFs(IccUtils.bchToString(iccId, 0, iccId.length)); } private static void loge(String message) { Rlog.e(LOG_TAG, message); } } tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java 0 → 100644 +182 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.uicc.euicc; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.HandlerThread; import android.service.euicc.EuiccProfileInfo; import android.util.ExceptionUtils; import android.util.Log; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccCardApplicationStatus; import com.android.internal.telephony.uicc.IccCardStatus; import com.android.internal.telephony.uicc.euicc.apdu.LogicalChannelMocker; import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class EuiccCardTest extends TelephonyTest { private static final long WAIT_TIMEOUT_MLLIS = 5000; private static class ResultCaptor<T> extends AsyncResultCallback<T> { public T result; public Throwable exception; private CountDownLatch mLatch; private ResultCaptor() { mLatch = new CountDownLatch(1); } public void await() { try { mLatch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail("Execution is interrupted: " + e); } } @Override public void onResult(T r) { result = r; mLatch.countDown(); } @Override public void onException(Throwable e) { exception = e; mLatch.countDown(); } } private class UiccCardHandlerThread extends HandlerThread { private UiccCardHandlerThread(String name) { super(name); } @Override public void onLooperPrepared() { mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus, 0 /* phoneId */); mHandler = new Handler(mTestHandlerThread.getLooper()); setReady(true); } } @Mock private CommandsInterface mMockCi; @Mock private IccCardStatus mMockIccCardStatus; private UiccCardHandlerThread mTestHandlerThread; private Handler mHandler; private EuiccCard mEuiccCard; @Before public void setUp() throws Exception { super.setUp(getClass().getSimpleName()); mMockIccCardStatus.mApplications = new IccCardApplicationStatus[]{}; mMockIccCardStatus.mCdmaSubscriptionAppIndex = mMockIccCardStatus.mImsSubscriptionAppIndex = mMockIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1; mMockIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT; mTestHandlerThread = new UiccCardHandlerThread(getClass().getSimpleName()); mTestHandlerThread.start(); waitUntilReady(); } @After public void tearDown() throws Exception { mTestHandlerThread.quit(); super.tearDown(); } private void assertUnexpectedException(Throwable e) { if (e != null) { fail("Unexpected exception: " + ExceptionUtils.getCompleteMessage(e) + "\n-----\n" + Log.getStackTraceString(e.getCause()) + "-----"); } } @Test public void testGetAllProfiles() { int channel = mockLogicalChannelResponses( "BF2D14A012E3105A0A896700000000004523019F7001019000"); ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>(); mEuiccCard.getAllProfiles(resultCaptor, mHandler); resultCaptor.await(); assertUnexpectedException(resultCaptor.exception); EuiccProfileInfo[] profiles = resultCaptor.result; assertEquals(1, profiles.length); assertEquals("98760000000000543210", profiles[0].getIccid()); assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState()); verifyStoreData(channel, "BF2D0D5C0B5A909192B79F709599BF76"); } @Test public void testFSuffix() { // iccID is 987600000000005432FF. int channel = mockLogicalChannelResponses( "BF2D14A012E3105A0A896700000000004523FF9F7001019000"); ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>(); mEuiccCard.getAllProfiles(resultCaptor, mHandler); resultCaptor.await(); assertUnexpectedException(resultCaptor.exception); EuiccProfileInfo[] profiles = resultCaptor.result; assertEquals(1, profiles.length); assertEquals("987600000000005432", profiles[0].getIccid()); assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState()); verifyStoreData(channel, "BF2D0D5C0B5A909192B79F709599BF76"); } private void verifyStoreData(int channel, String command) { verify(mMockCi, times(1)) .iccTransmitApduLogicalChannel(eq(channel), eq(0x80 | channel), eq(0xE2), eq(0x91), eq(0), eq(command.length() / 2), eq(command), any()); } private int mockLogicalChannelResponses(Object... responses) { int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "E00582030200009000"); LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, responses); LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); return channel; } } tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/IccUtilsTest.java +11 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.telephony.uicc.euicc.asn1; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import android.test.suitebuilder.annotation.SmallTest; Loading Loading @@ -241,4 +242,14 @@ public class IccUtilsTest { IccUtils.bcdToBytes("12345", output); assertArrayEquals(new byte[] {0x21, 0x43, 0x05, 0}, output); } @SmallTest @Test public void testStripTrailingFs() { assertNull(IccUtils.stripTrailingFs(null)); assertEquals("", IccUtils.stripTrailingFs("")); assertEquals("1234", IccUtils.stripTrailingFs("1234")); assertEquals("1234", IccUtils.stripTrailingFs("1234ff")); assertEquals("1234", IccUtils.stripTrailingFs("1234FF")); } } Loading
src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java +4 −13 Original line number Diff line number Diff line Loading @@ -343,11 +343,11 @@ public class SubscriptionInfoUpdater extends Handler { String iccId = mIccId[slotId]; if (iccId == null) { IccRecords records = mPhone[slotId].getIccCard().getIccRecords(); if (stripIccIdSuffix(records.getFullIccId()) == null) { if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) { logd("handleSimLocked: IccID null"); return; } mIccId[slotId] = stripIccIdSuffix(records.getFullIccId()); mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId()); } else { logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId); } Loading Loading @@ -390,11 +390,11 @@ public class SubscriptionInfoUpdater extends Handler { logd("handleSimLoaded: IccRecords null"); return; } if (stripIccIdSuffix(records.getFullIccId()) == null) { if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) { logd("handleSimLoaded: IccID null"); return; } mIccId[slotId] = stripIccIdSuffix(records.getFullIccId()); mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId()); if (isAllIccIdQueryDone()) { updateSubscriptionInfoByIccId(); Loading Loading @@ -896,15 +896,6 @@ public class SubscriptionInfoUpdater extends Handler { } } // Remove trailing F's from full hexadecimal IccId, as they should be considered padding private String stripIccIdSuffix(String hexIccId) { if (hexIccId == null) { return null; } else { return hexIccId.replaceAll("(?i)f*$", ""); } } public void dispose() { logd("[dispose]"); mContext.unregisterReceiver(sReceiver); Loading
src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java +137 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,10 @@ package com.android.internal.telephony.uicc.euicc; import android.annotation.Nullable; import android.content.Context; import android.os.Handler; import android.service.carrier.CarrierIdentifier; import android.service.euicc.EuiccProfileInfo; import android.telephony.Rlog; import android.telephony.UiccAccessRule; import android.telephony.euicc.EuiccCardManager; import android.telephony.euicc.EuiccNotification; import android.telephony.euicc.EuiccRulesAuthTable; Loading @@ -28,6 +31,8 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.uicc.IccCardStatus; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccCard; import com.android.internal.telephony.uicc.asn1.Asn1Decoder; import com.android.internal.telephony.uicc.asn1.Asn1Node; import com.android.internal.telephony.uicc.asn1.InvalidAsn1DataException; import com.android.internal.telephony.uicc.asn1.TagNotFoundException; import com.android.internal.telephony.uicc.euicc.apdu.ApduSender; Loading @@ -36,11 +41,15 @@ import com.android.internal.telephony.uicc.euicc.apdu.RequestProvider; import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper; import java.util.List; /** * This represents an eUICC card to perform profile management operations asynchronously. This class * includes methods defined by different versions of GSMA Spec (SGP.22). */ public class EuiccCard extends UiccCard { private static final String LOG_TAG = "EuiccCard"; private static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100"; private static final EuiccSpecVersion SGP_2_0 = new EuiccSpecVersion(2, 0, 0); Loading Loading @@ -88,7 +97,32 @@ public class EuiccCard extends UiccCard { * @since 1.1.0 [GSMA SGP.22] */ public void getAllProfiles(AsyncResultCallback<EuiccProfileInfo[]> callback, Handler handler) { // TODO: to be implemented. sendApdu( newRequestProvider((RequestBuilder requestBuilder) -> requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES) .addChildAsBytes(Tags.TAG_TAG_LIST, Tags.EUICC_PROFILE_TAGS) .build().toHex())), (byte[] response) -> { List<Asn1Node> profileNodes = new Asn1Decoder(response).nextNode() .getChild(Tags.TAG_CTX_COMP_0).getChildren(Tags.TAG_PROFILE_INFO); int size = profileNodes.size(); EuiccProfileInfo[] profiles = new EuiccProfileInfo[size]; int profileCount = 0; for (int i = 0; i < size; i++) { Asn1Node profileNode = profileNodes.get(i); if (!profileNode.hasChild(Tags.TAG_ICCID)) { loge("Profile must have an ICCID."); continue; } EuiccProfileInfo.Builder profileBuilder = new EuiccProfileInfo.Builder(); buildProfile(profileNode, profileBuilder); EuiccProfileInfo profile = profileBuilder.build(); profiles[profileCount++] = profile; } return profiles; }, callback, handler); } /** Loading Loading @@ -436,4 +470,106 @@ public class EuiccCard extends UiccCard { } }, handler); } private static void buildProfile(Asn1Node profileNode, EuiccProfileInfo.Builder profileBuilder) throws TagNotFoundException, InvalidAsn1DataException { String strippedIccIdString = stripTrailingFs(profileNode.getChild(Tags.TAG_ICCID).asBytes()); profileBuilder.setIccid(strippedIccIdString); if (profileNode.hasChild(Tags.TAG_NICKNAME)) { profileBuilder.setNickname(profileNode.getChild(Tags.TAG_NICKNAME).asString()); } if (profileNode.hasChild(Tags.TAG_SERVICE_PROVIDER_NAME)) { profileBuilder.setServiceProviderName( profileNode.getChild(Tags.TAG_SERVICE_PROVIDER_NAME).asString()); } if (profileNode.hasChild(Tags.TAG_PROFILE_NAME)) { profileBuilder.setProfileName( profileNode.getChild(Tags.TAG_PROFILE_NAME).asString()); } if (profileNode.hasChild(Tags.TAG_OPERATOR_ID)) { profileBuilder.setCarrierIdentifier( buildCarrierIdentifier(profileNode.getChild(Tags.TAG_OPERATOR_ID))); } if (profileNode.hasChild(Tags.TAG_PROFILE_STATE)) { // noinspection WrongConstant profileBuilder.setState(profileNode.getChild(Tags.TAG_PROFILE_STATE).asInteger()); } else { profileBuilder.setState(EuiccProfileInfo.PROFILE_STATE_DISABLED); } if (profileNode.hasChild(Tags.TAG_PROFILE_CLASS)) { // noinspection WrongConstant profileBuilder.setProfileClass( profileNode.getChild(Tags.TAG_PROFILE_CLASS).asInteger()); } else { profileBuilder.setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL); } if (profileNode.hasChild(Tags.TAG_PROFILE_POLICY_RULE)) { // noinspection WrongConstant profileBuilder.setPolicyRules( profileNode.getChild(Tags.TAG_PROFILE_POLICY_RULE).asBits()); } if (profileNode.hasChild(Tags.TAG_CARRIER_PRIVILEGE_RULES)) { List<Asn1Node> refArDoNodes = profileNode.getChild(Tags.TAG_CARRIER_PRIVILEGE_RULES) .getChildren(Tags.TAG_REF_AR_DO); profileBuilder.setUiccAccessRule(buildUiccAccessRule(refArDoNodes)); } } private static CarrierIdentifier buildCarrierIdentifier(Asn1Node node) throws InvalidAsn1DataException, TagNotFoundException { String gid1 = null; if (node.hasChild(Tags.TAG_CTX_1)) { gid1 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_1).asBytes()); } String gid2 = null; if (node.hasChild(Tags.TAG_CTX_2)) { gid2 = IccUtils.bytesToHexString(node.getChild(Tags.TAG_CTX_2).asBytes()); } return new CarrierIdentifier(node.getChild(Tags.TAG_CTX_0).asBytes(), gid1, gid2); } @Nullable private static UiccAccessRule[] buildUiccAccessRule(List<Asn1Node> nodes) throws InvalidAsn1DataException, TagNotFoundException { if (nodes.isEmpty()) { return null; } int count = nodes.size(); UiccAccessRule[] rules = new UiccAccessRule[count]; for (int i = 0; i < count; i++) { Asn1Node node = nodes.get(i); Asn1Node refDoNode = node.getChild(Tags.TAG_REF_DO); byte[] signature = refDoNode.getChild(Tags.TAG_DEVICE_APP_ID_REF_DO).asBytes(); String packageName = null; if (refDoNode.hasChild(Tags.TAG_PKG_REF_DO)) { packageName = refDoNode.getChild(Tags.TAG_PKG_REF_DO).asString(); } long accessType = 0; if (node.hasChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO)) { Asn1Node permArDoNode = node.getChild(Tags.TAG_AR_DO, Tags.TAG_PERM_AR_DO); accessType = permArDoNode.asRawLong(); } rules[i] = new UiccAccessRule(signature, packageName, accessType); } return rules; } /** Strip all the trailing 'F' characters of an iccId. */ private static String stripTrailingFs(byte[] iccId) { return IccUtils.stripTrailingFs(IccUtils.bchToString(iccId, 0, iccId.length)); } private static void loge(String message) { Rlog.e(LOG_TAG, message); } }
tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java 0 → 100644 +182 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.uicc.euicc; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.HandlerThread; import android.service.euicc.EuiccProfileInfo; import android.util.ExceptionUtils; import android.util.Log; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccCardApplicationStatus; import com.android.internal.telephony.uicc.IccCardStatus; import com.android.internal.telephony.uicc.euicc.apdu.LogicalChannelMocker; import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class EuiccCardTest extends TelephonyTest { private static final long WAIT_TIMEOUT_MLLIS = 5000; private static class ResultCaptor<T> extends AsyncResultCallback<T> { public T result; public Throwable exception; private CountDownLatch mLatch; private ResultCaptor() { mLatch = new CountDownLatch(1); } public void await() { try { mLatch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail("Execution is interrupted: " + e); } } @Override public void onResult(T r) { result = r; mLatch.countDown(); } @Override public void onException(Throwable e) { exception = e; mLatch.countDown(); } } private class UiccCardHandlerThread extends HandlerThread { private UiccCardHandlerThread(String name) { super(name); } @Override public void onLooperPrepared() { mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus, 0 /* phoneId */); mHandler = new Handler(mTestHandlerThread.getLooper()); setReady(true); } } @Mock private CommandsInterface mMockCi; @Mock private IccCardStatus mMockIccCardStatus; private UiccCardHandlerThread mTestHandlerThread; private Handler mHandler; private EuiccCard mEuiccCard; @Before public void setUp() throws Exception { super.setUp(getClass().getSimpleName()); mMockIccCardStatus.mApplications = new IccCardApplicationStatus[]{}; mMockIccCardStatus.mCdmaSubscriptionAppIndex = mMockIccCardStatus.mImsSubscriptionAppIndex = mMockIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1; mMockIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT; mTestHandlerThread = new UiccCardHandlerThread(getClass().getSimpleName()); mTestHandlerThread.start(); waitUntilReady(); } @After public void tearDown() throws Exception { mTestHandlerThread.quit(); super.tearDown(); } private void assertUnexpectedException(Throwable e) { if (e != null) { fail("Unexpected exception: " + ExceptionUtils.getCompleteMessage(e) + "\n-----\n" + Log.getStackTraceString(e.getCause()) + "-----"); } } @Test public void testGetAllProfiles() { int channel = mockLogicalChannelResponses( "BF2D14A012E3105A0A896700000000004523019F7001019000"); ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>(); mEuiccCard.getAllProfiles(resultCaptor, mHandler); resultCaptor.await(); assertUnexpectedException(resultCaptor.exception); EuiccProfileInfo[] profiles = resultCaptor.result; assertEquals(1, profiles.length); assertEquals("98760000000000543210", profiles[0].getIccid()); assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState()); verifyStoreData(channel, "BF2D0D5C0B5A909192B79F709599BF76"); } @Test public void testFSuffix() { // iccID is 987600000000005432FF. int channel = mockLogicalChannelResponses( "BF2D14A012E3105A0A896700000000004523FF9F7001019000"); ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>(); mEuiccCard.getAllProfiles(resultCaptor, mHandler); resultCaptor.await(); assertUnexpectedException(resultCaptor.exception); EuiccProfileInfo[] profiles = resultCaptor.result; assertEquals(1, profiles.length); assertEquals("987600000000005432", profiles[0].getIccid()); assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState()); verifyStoreData(channel, "BF2D0D5C0B5A909192B79F709599BF76"); } private void verifyStoreData(int channel, String command) { verify(mMockCi, times(1)) .iccTransmitApduLogicalChannel(eq(channel), eq(0x80 | channel), eq(0xE2), eq(0x91), eq(0), eq(command.length() / 2), eq(command), any()); } private int mockLogicalChannelResponses(Object... responses) { int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "E00582030200009000"); LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, responses); LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel); return channel; } }
tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/asn1/IccUtilsTest.java +11 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.telephony.uicc.euicc.asn1; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import android.test.suitebuilder.annotation.SmallTest; Loading Loading @@ -241,4 +242,14 @@ public class IccUtilsTest { IccUtils.bcdToBytes("12345", output); assertArrayEquals(new byte[] {0x21, 0x43, 0x05, 0}, output); } @SmallTest @Test public void testStripTrailingFs() { assertNull(IccUtils.stripTrailingFs(null)); assertEquals("", IccUtils.stripTrailingFs("")); assertEquals("1234", IccUtils.stripTrailingFs("1234")); assertEquals("1234", IccUtils.stripTrailingFs("1234ff")); assertEquals("1234", IccUtils.stripTrailingFs("1234FF")); } }