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

Commit 561aae59 authored by Hunter Knepshield's avatar Hunter Knepshield Committed by Gerrit Code Review
Browse files

Merge "Fix EuiccCard's IMEI encoding."

parents d9c02b5c 61d2e9dd
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";
@@ -982,10 +983,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;
    }
@@ -1002,7 +1062,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),