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

Commit 26e6d034 authored by Susheel Nyamala's avatar Susheel Nyamala Committed by Sandeep Gutta
Browse files

Support Fetching IMSI MCC and MNC for RuimRecords.

The Following changes are done with this.

- Read IMSI using SIM I/O for CSIM/RUIM.

Use Sim I/O command to read the EF_IMSIM to retrieve the IMSI
from the card, rather than using CommandsInterface.getIMSIForApp.
When both SIM and RUIM app exists, we cannot specify the
application for which we should retrieve the IMSI using the getIMSI
API, since SIM and RUIM have no AID.

- Change to return proper MNC in case of CSIM.

As per spec C.S0065 section 5.2.2 MNC length in case of CSIM
IMSI is 2. So modify the code to return the MNC of proper length.

- Introduce getOperatorNumeric api in RuimRecords

Bug: 23018205
Test: atest RuimRecordsTest
Change-Id: I905d4a271dfba168f2bc3830b8a50733cbe17f8c
parent 5053c343
Loading
Loading
Loading
Loading
+93 −72
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.MccTable;
@@ -84,8 +85,11 @@ public class RuimRecords extends IccRecords {
                + " mHomeNetworkId=" + mHomeNetworkId;
    }

    //Constants
    //MNC length in case of CSIM/RUIM IMSI is 2 as per spec C.S0065 section 5.2.2
    private static final int CSIM_IMSI_MNC_LENGTH = 2;

    // ***** Event Constants
    private static final int EVENT_GET_IMSI_DONE = 3;
    private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
    private static final int EVENT_GET_ICCID_DONE = 5;
    private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
@@ -203,15 +207,62 @@ public class RuimRecords extends IccRecords {
        }
    }

    private int adjstMinDigits (int digits) {
    private int decodeImsiDigits(int digits, int length) {
        // Per C.S0005 section 2.3.1.
        digits += 111;
        digits = (digits % 10 == 0)?(digits - 10):digits;
        digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
        digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
        for (int i = 0, denominator = 1; i < length; i++) {
            digits += denominator;
            if ((digits / denominator) % 10 == 0) {
                digits = digits - (10 * denominator);
            }
            denominator *= 10;
        }
        return digits;
    }

    /**
     * Decode utility to decode IMSI from data read from EF_IMSIM
     * Please refer to
     *       // C.S0065 section 5.2.2 for IMSI_M encoding
     *       // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
     */
    private String decodeImsi(byte[] data) {
        // Retrieve the MCC and digits 11 and 12
        int mcc_data = ((0x03 & data[9]) << 8) | (0xFF & data[8]);
        int mcc = decodeImsiDigits(mcc_data, 3);
        int digits_11_12_data = data[6] & 0x7f;
        int digits_11_12 = decodeImsiDigits(digits_11_12_data, 2);

        // Retrieve 10 MIN digits
        int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
        int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
        int digit7 = 0x0F & (data[4] >> 2);
        if (digit7 > 0x09) digit7 = 0;
        int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);

        first3digits = decodeImsiDigits(first3digits, 3);
        second3digits = decodeImsiDigits(second3digits, 3);
        last3digits = decodeImsiDigits(last3digits, 3);

        return new StringBuilder()
                .append(String.format(Locale.US, "%03d", mcc))
                .append(String.format(Locale.US, "%02d", digits_11_12))
                .append(String.format(Locale.US, "%03d", first3digits))
                .append(String.format(Locale.US, "%03d", second3digits))
                .append(String.format(Locale.US, "%d", digit7))
                .append(String.format(Locale.US, "%03d", last3digits))
                .toString();
    }

     /**
     * Introduce Generic API returns the 5 or 6 digit MCC/MNC
     * of the operator that provided the RUIM card.
     * Returns null of RUIM is not yet ready
     */
    @Override
    public String getOperatorNumeric() {
        return getRUIMOperatorNumeric();
    }

    /**
     * Returns the 5 or 6 digit MCC/MNC of the operator that
     *  provided the RUIM card. Returns null of RUIM is not yet ready
@@ -223,17 +274,16 @@ public class RuimRecords extends IccRecords {
            return null;
        }

        int mncLen = 0;
        if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) {
            // Length = length of MCC + length of MNC
            // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
            return imsi.substring(0, 3 + mMncLength);
            mncLen = mMncLength;
        } else {
            mncLen = CSIM_IMSI_MNC_LENGTH;
        }

        // Guess the MNC length based on the MCC if we don't
        // have a valid value in ef[ad]

        int mcc = Integer.parseInt(imsi.substring(0, 3));
        return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
        return imsi.substring(0, 3 + mncLen);
    }

    // Refer to ETSI TS 102.221
@@ -369,7 +419,8 @@ public class RuimRecords extends IccRecords {
        }
    }

    private class EfCsimImsimLoaded implements IccRecordLoaded {
    @VisibleForTesting
    public class EfCsimImsimLoaded implements IccRecordLoaded {
        @Override
        public String getEfName() {
            return "EF_CSIM_IMSIM";
@@ -378,31 +429,41 @@ public class RuimRecords extends IccRecords {
        @Override
        public void onRecordLoaded(AsyncResult ar) {
            byte[] data = (byte[]) ar.result;
            if (VDBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));

            if (data == null || data.length < 10) {
                log("Invalid IMSI from EF_CSIM_IMSIM " + IccUtils.bytesToHexString(data));
                mImsi = null;
                mMin = null;
                return;
            }
            if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data) +
                    Rlog.pii(LOG_TAG, IccUtils.bytesToHexString(data)));

            // C.S0065 section 5.2.2 for IMSI_M encoding
            // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
            boolean provisioned = ((data[7] & 0x80) == 0x80);

            if (provisioned) {
                int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
                int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
                int digit7 = 0x0F & (data[4] >> 2);
                if (digit7 > 0x09) digit7 = 0;
                int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
                first3digits = adjstMinDigits(first3digits);
                second3digits = adjstMinDigits(second3digits);
                last3digits = adjstMinDigits(last3digits);

                StringBuilder builder = new StringBuilder();
                builder.append(String.format(Locale.US, "%03d", first3digits));
                builder.append(String.format(Locale.US, "%03d", second3digits));
                builder.append(String.format(Locale.US, "%d", digit7));
                builder.append(String.format(Locale.US, "%03d", last3digits));
                mMin = builder.toString();
                if (DBG) log("min present=" + mMin);
                mImsi = decodeImsi(data);
                if (null != mImsi) {
                    mMin = mImsi.substring(5, 15);
                }
                log("IMSI: " + mImsi.substring(0, 5) + "xxxxxxxxx" +
                         Rlog.pii(LOG_TAG, mImsi.substring(0, 5)));

            } else {
                if (DBG) log("min not present");
                if (DBG) log("IMSI not provisioned in card");
            }

            //Update MccTable with the retrieved IMSI
            String operatorNumeric = getOperatorNumeric();
            if (operatorNumeric != null) {
                if(operatorNumeric.length() <= 6){
                    MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
                }
            }

            mImsiReadyRegistrants.notifyRegistrants();
        }
   }

@@ -618,43 +679,6 @@ public class RuimRecords extends IccRecords {
                log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
            break;

            /* IO events */
            case EVENT_GET_IMSI_DONE:
                isRecordLoadResponse = true;

                ar = (AsyncResult)msg.obj;
                if (ar.exception != null) {
                    loge("Exception querying IMSI, Exception:" + ar.exception);
                    break;
                }

                mImsi = (String) ar.result;

                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
                // than 15 (and usually 15).
                if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
                    loge("invalid IMSI " + mImsi);
                    mImsi = null;
                }

                // FIXME: CSIM IMSI may not contain the MNC.
                if (false) {
                    log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");

                    String operatorNumeric = getRUIMOperatorNumeric();
                    if (operatorNumeric != null) {
                        if (operatorNumeric.length() <= 6) {
                            log("update mccmnc=" + operatorNumeric);
                            MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
                        }
                    }
                } else {
                    String operatorNumeric = getRUIMOperatorNumeric();
                    log("NO update mccmnc=" + operatorNumeric);
                }

            break;

            case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
                ar = (AsyncResult)msg.obj;
                String localTemp[] = (String[])ar.result;
@@ -842,9 +866,6 @@ public class RuimRecords extends IccRecords {

        if (DBG) log("fetchRuimRecords " + mRecordsToLoad);

        mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
        mRecordsToLoad++;

        mFh.loadEFTransparent(EF_ICCID,
                obtainMessage(EVENT_GET_ICCID_DONE));
        mRecordsToLoad++;
+88 −0
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package com.android.internal.telephony.uicc;

import org.mockito.Mock;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import org.mockito.MockitoAnnotations;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.android.internal.telephony.TelephonyTest;

import android.content.Context;
import android.os.AsyncResult;
import android.os.HandlerThread;

public class RuimRecordsTest extends TelephonyTest {

    private RuimRecords mRuimRecords;

    private class RuimRecordsTestHandler extends HandlerThread {
        private RuimRecordsTestHandler(String name) {
            super(name);
        }

        @Override
        public void onLooperPrepared() {
            mRuimRecords = new RuimRecords(mUiccCardApplication3gpp2, mContext, mSimulatedCommands);
            setReady(true);
        }
    }

    @Before
    public void setUp() throws Exception {
        super.setUp(this.getClass().getSimpleName());
        new RuimRecordsTestHandler(TAG).start();
        waitUntilReady();
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @Test
    public void testCsimImsiLoaded() {
         RuimRecords.EfCsimImsimLoaded mImsiLoaded = mRuimRecords.new EfCsimImsimLoaded();
         AsyncResult ar = new AsyncResult(null, null, null);
         mImsiLoaded.onRecordLoaded(ar);
         String mccmnc = mRuimRecords.getOperatorNumeric();
         assertNull(mccmnc);

         byte[] byteArray = new byte[]{0,19,3,75,68,88,99,(byte)128,(byte)209,0};
         AsyncResult ar2 = new AsyncResult(null, byteArray, null);
         mImsiLoaded.onRecordLoaded(ar2);
         mccmnc = mRuimRecords.getOperatorNumeric();
         assertNotNull(mccmnc);
         assertEquals("31000", mccmnc);
    }
}