Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2699b068 authored by Hunter Knepshield's avatar Hunter Knepshield
Browse files

Merge "Fix EuiccCard's IMEI encoding." am: 561aae59 am: 17f67b4f

am: cd77ac47

Bug: 113071724
Change-Id: Ie80023d8ac23961a1c161219ff4cec806c3f53b4
(cherry picked from commit ab477274)
parent 6db723fd
Loading
Loading
Loading
Loading
+65 −5
Original line number Diff line number Diff line
@@ -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";
@@ -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;
    }
@@ -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);
+16 −0
Original line number Diff line number Diff line
@@ -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),