Loading src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java +65 −5 Original line number Diff line number Diff line Loading @@ -83,7 +83,8 @@ public class EuiccCard extends UiccCard { // Error code for no result available when retrieving notifications. private static final int CODE_NO_RESULT_AVAILABLE = 1; private static final EuiccSpecVersion SGP_2_0 = new EuiccSpecVersion(2, 0, 0); private static final EuiccSpecVersion SGP22_V_2_0 = new EuiccSpecVersion(2, 0, 0); private static final EuiccSpecVersion SGP22_V_2_1 = new EuiccSpecVersion(2, 1, 0); // Device capabilities. private static final String DEV_CAP_GSM = "gsm"; Loading Loading @@ -976,10 +977,69 @@ public class EuiccCard extends UiccCard { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected byte[] getDeviceId() { byte[] imeiBytes = new byte[8]; Phone phone = PhoneFactory.getPhone(getPhoneId()); if (phone != null) { IccUtils.bcdToBytes(phone.getDeviceId(), imeiBytes); if (phone == null) { return new byte[8]; } return getDeviceId(phone.getDeviceId(), mSpecVersion); } /** * Different versions of SGP.22 specify different encodings of the device's IMEI, so we handle * those differences here. * * @param imei The IMEI of the device. Assumed to be 15 decimal digits. * @param specVersion The SGP.22 version which we're encoding the IMEI for. * @return A byte string representing the given IMEI according to the specified SGP.22 version. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public static byte[] getDeviceId(String imei, EuiccSpecVersion specVersion) { byte[] imeiBytes = new byte[8]; // The IMEI's encoding is version-dependent. if (specVersion.compareTo(SGP22_V_2_1) >= 0) { /* * In SGP.22 v2.1, a clarification was added to clause 4.2 that requires the nibbles of * the last byte to be swapped from normal TBCD encoding (so put back in normal order): * * The IMEI (including the check digit) SHALL be represented as a string of 8 octets * that is coded as a Telephony Binary Coded Decimal String as defined in 3GPP TS 29.002 * [63], except that the last octet contains the check digit (in high nibble) and an 'F' * filler (in low nibble). It SHOULD be present if the Device contains a non-removable * eUICC. * * 3GPP TS 29.002 clause 17.7.8 in turn says this: * * TBCD-STRING ::= OCTET STRING * This type (Telephony Binary Coded Decimal String) is used to represent several digits * from 0 through 9, *, #, a, b, c, two digits per octet, each digit encoded 0000 to * 1001 (0 to 9), 1010 (*), 1011 (#), 1100 (a), 1101 (b) or 1110 (c); 1111 used as * filler when there is an odd number of digits. * Bits 8765 of octet n encoding digit 2n * Bits 4321 of octet n encoding digit 2(n-1) + 1 */ // Since the IMEI is always just decimal digits, we can still use BCD encoding (which // correctly swaps digit ordering within bytes), but we have to manually pad a 0xF value // instead of 0. imei += 'F'; IccUtils.bcdToBytes(imei, imeiBytes); // And now the funky last byte flip (this is not normal TBCD, the GSMA added it on top // just for the IMEI for some reason). Bitwise operations promote to int first, so we // have to do some extra masking. byte last = imeiBytes[7]; imeiBytes[7] = (byte) ((last & 0xFF) << 4 | ((last & 0xFF) >>> 4)); } else { /* * Prior to SGP.22 v2.1, clause 4.2 reads as follows: * * The IMEI (including the check digit) SHALL be represented as a string of 8 octets * that is BCD coded as defined in 3GPP TS 23.003 [35]. It SHOULD be present if the * Device contains a non-removable eUICC. * * It appears that 3GPP TS 23.003 doesn't define anything about BCD encoding, it just * defines what IMEI and a few other telephony identifiers are. We default to normal BCD * encoding since the spec is unclear here. */ IccUtils.bcdToBytes(imei, imeiBytes); } return imeiBytes; } Loading @@ -996,7 +1056,7 @@ public class EuiccCard extends UiccCard { throw new EuiccCardException("Cannot get eUICC spec version."); } try { if (ver.compareTo(SGP_2_0) < 0) { if (ver.compareTo(SGP22_V_2_0) < 0) { throw new EuiccCardException("eUICC spec version is unsupported: " + ver); } builder.build(requestBuilder); Loading tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -984,6 +984,22 @@ public class EuiccCardTest extends TelephonyTest { assertFalse(node.hasChild(Tags.TAG_CTX_0)); } @Test public void testGetDeviceId() { // Unclear v2.0 definition assertArrayEquals( new byte[] {0x21, 0x43, 0x65, (byte) 0x87, 0x09, 0x21, 0x43, 0x05}, EuiccCard.getDeviceId("123456789012345", new EuiccSpecVersion(2, 0, 0))); // Clarified v2.1+ definition assertArrayEquals( new byte[] {0x21, 0x43, 0x65, (byte) 0x87, 0x09, 0x21, 0x43, 0x5F}, EuiccCard.getDeviceId("123456789012345", new EuiccSpecVersion(2, 1, 0))); // Same definition on v2.2 assertArrayEquals( new byte[] {0x21, 0x43, 0x65, (byte) 0x87, 0x09, 0x21, 0x43, 0x5F}, EuiccCard.getDeviceId("123456789012345", new EuiccSpecVersion(2, 2, 0))); } private void verifyStoreData(int channel, String command) { verify(mMockCi, times(1)) .iccTransmitApduLogicalChannel(eq(channel), eq(0x80 | channel), eq(0xE2), eq(0x91), Loading Loading
src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java +65 −5 Original line number Diff line number Diff line Loading @@ -83,7 +83,8 @@ public class EuiccCard extends UiccCard { // Error code for no result available when retrieving notifications. private static final int CODE_NO_RESULT_AVAILABLE = 1; private static final EuiccSpecVersion SGP_2_0 = new EuiccSpecVersion(2, 0, 0); private static final EuiccSpecVersion SGP22_V_2_0 = new EuiccSpecVersion(2, 0, 0); private static final EuiccSpecVersion SGP22_V_2_1 = new EuiccSpecVersion(2, 1, 0); // Device capabilities. private static final String DEV_CAP_GSM = "gsm"; Loading Loading @@ -976,10 +977,69 @@ public class EuiccCard extends UiccCard { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected byte[] getDeviceId() { byte[] imeiBytes = new byte[8]; Phone phone = PhoneFactory.getPhone(getPhoneId()); if (phone != null) { IccUtils.bcdToBytes(phone.getDeviceId(), imeiBytes); if (phone == null) { return new byte[8]; } return getDeviceId(phone.getDeviceId(), mSpecVersion); } /** * Different versions of SGP.22 specify different encodings of the device's IMEI, so we handle * those differences here. * * @param imei The IMEI of the device. Assumed to be 15 decimal digits. * @param specVersion The SGP.22 version which we're encoding the IMEI for. * @return A byte string representing the given IMEI according to the specified SGP.22 version. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public static byte[] getDeviceId(String imei, EuiccSpecVersion specVersion) { byte[] imeiBytes = new byte[8]; // The IMEI's encoding is version-dependent. if (specVersion.compareTo(SGP22_V_2_1) >= 0) { /* * In SGP.22 v2.1, a clarification was added to clause 4.2 that requires the nibbles of * the last byte to be swapped from normal TBCD encoding (so put back in normal order): * * The IMEI (including the check digit) SHALL be represented as a string of 8 octets * that is coded as a Telephony Binary Coded Decimal String as defined in 3GPP TS 29.002 * [63], except that the last octet contains the check digit (in high nibble) and an 'F' * filler (in low nibble). It SHOULD be present if the Device contains a non-removable * eUICC. * * 3GPP TS 29.002 clause 17.7.8 in turn says this: * * TBCD-STRING ::= OCTET STRING * This type (Telephony Binary Coded Decimal String) is used to represent several digits * from 0 through 9, *, #, a, b, c, two digits per octet, each digit encoded 0000 to * 1001 (0 to 9), 1010 (*), 1011 (#), 1100 (a), 1101 (b) or 1110 (c); 1111 used as * filler when there is an odd number of digits. * Bits 8765 of octet n encoding digit 2n * Bits 4321 of octet n encoding digit 2(n-1) + 1 */ // Since the IMEI is always just decimal digits, we can still use BCD encoding (which // correctly swaps digit ordering within bytes), but we have to manually pad a 0xF value // instead of 0. imei += 'F'; IccUtils.bcdToBytes(imei, imeiBytes); // And now the funky last byte flip (this is not normal TBCD, the GSMA added it on top // just for the IMEI for some reason). Bitwise operations promote to int first, so we // have to do some extra masking. byte last = imeiBytes[7]; imeiBytes[7] = (byte) ((last & 0xFF) << 4 | ((last & 0xFF) >>> 4)); } else { /* * Prior to SGP.22 v2.1, clause 4.2 reads as follows: * * The IMEI (including the check digit) SHALL be represented as a string of 8 octets * that is BCD coded as defined in 3GPP TS 23.003 [35]. It SHOULD be present if the * Device contains a non-removable eUICC. * * It appears that 3GPP TS 23.003 doesn't define anything about BCD encoding, it just * defines what IMEI and a few other telephony identifiers are. We default to normal BCD * encoding since the spec is unclear here. */ IccUtils.bcdToBytes(imei, imeiBytes); } return imeiBytes; } Loading @@ -996,7 +1056,7 @@ public class EuiccCard extends UiccCard { throw new EuiccCardException("Cannot get eUICC spec version."); } try { if (ver.compareTo(SGP_2_0) < 0) { if (ver.compareTo(SGP22_V_2_0) < 0) { throw new EuiccCardException("eUICC spec version is unsupported: " + ver); } builder.build(requestBuilder); Loading
tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -984,6 +984,22 @@ public class EuiccCardTest extends TelephonyTest { assertFalse(node.hasChild(Tags.TAG_CTX_0)); } @Test public void testGetDeviceId() { // Unclear v2.0 definition assertArrayEquals( new byte[] {0x21, 0x43, 0x65, (byte) 0x87, 0x09, 0x21, 0x43, 0x05}, EuiccCard.getDeviceId("123456789012345", new EuiccSpecVersion(2, 0, 0))); // Clarified v2.1+ definition assertArrayEquals( new byte[] {0x21, 0x43, 0x65, (byte) 0x87, 0x09, 0x21, 0x43, 0x5F}, EuiccCard.getDeviceId("123456789012345", new EuiccSpecVersion(2, 1, 0))); // Same definition on v2.2 assertArrayEquals( new byte[] {0x21, 0x43, 0x65, (byte) 0x87, 0x09, 0x21, 0x43, 0x5F}, EuiccCard.getDeviceId("123456789012345", new EuiccSpecVersion(2, 2, 0))); } private void verifyStoreData(int channel, String command) { verify(mMockCi, times(1)) .iccTransmitApduLogicalChannel(eq(channel), eq(0x80 | channel), eq(0xE2), eq(0x91), Loading