Loading src/java/com/android/internal/telephony/uicc/IccRecords.java +140 −13 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.internal.telephony.uicc; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.AsyncResult; Loading @@ -25,6 +26,7 @@ import android.os.Message; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; import android.telephony.CellIdentity; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; Loading Loading @@ -53,6 +55,11 @@ public abstract class IccRecords extends Handler implements IccConstants { protected static final boolean DBG = true; protected static final boolean VDBG = false; // STOPSHIP if true public static final int PLMN_MIN_LENGTH = CellIdentity.MCC_LENGTH + CellIdentity.MNC_MIN_LENGTH; public static final int PLMN_MAX_LENGTH = CellIdentity.MCC_LENGTH + CellIdentity.MNC_MAX_LENGTH; // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length. private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = { "302370", "302720", "310260", Loading Loading @@ -148,6 +155,14 @@ public abstract class IccRecords extends Handler implements IccConstants { // Reference: 3GPP TS 31.102 Section 4.2.66 protected String[] mSpdi; // A list of PLMN Network Name (PNN). // Reference: 3GPP TS 31.102 Section 4.2.58 protected PlmnNetworkName[] mPnns; // Operator PLMN List (OPL). // Reference: 3GPP TS 31.102 Section 4.2.59 protected OperatorPlmnInfo[] mOpl; // Carrier name display condition bitmask // Reference: 3GPP TS 131.102 section 4.2.12 EF_SPN Display Condition Loading Loading @@ -697,6 +712,14 @@ public abstract class IccRecords extends Handler implements IccConstants { return mPnnHomeName; } public PlmnNetworkName[] getPnns() { return mPnns; } public OperatorPlmnInfo[] getOpl() { return mOpl; } public void setMsisdnNumber(String alphaTag, String number, Message onComplete) { loge("setMsisdn() should not be invoked on base IccRecords"); Loading Loading @@ -1353,6 +1376,36 @@ public abstract class IccRecords extends Handler implements IccConstants { pw.flush(); } /** * Get network name in PNN for the provided PLMN and LAC/TAC. * * @param opls OPL. * @param pnns PNN list. * @param plmn PLMN. * @param lacTac LAC/TAC * @return network Name for the provided PLMN and LAC/TAC. */ @Nullable public static String getNetworkNameForPlmnFromPnnOpl(PlmnNetworkName[] pnns, OperatorPlmnInfo[] opls, @Nullable String plmn, int lacTac) { if (opls == null || pnns == null || plmn == null || plmn.length() < PLMN_MIN_LENGTH || plmn.length() > PLMN_MAX_LENGTH) { return null; } for (OperatorPlmnInfo operatorPlmnInfo: opls) { int pnnIdx = operatorPlmnInfo.getPnnIdx(plmn, lacTac); if (pnnIdx >= 0) { if (pnnIdx < pnns.length && pnns[pnnIdx] != null) { return pnns[pnnIdx].getName(); } else { Rlog.e("IccRecords", "Invalid PNN record for Record" + pnnIdx); break; } } } return null; } /** * Operator PLMN information. This contains the location area information or tracking area * that are used to associate a specific name contained in EF_PNN. Loading @@ -1360,28 +1413,75 @@ public abstract class IccRecords extends Handler implements IccConstants { * Reference: 3GPP TS 31.102 section 4.2.59 EF_OPL */ public static final class OperatorPlmnInfo { // PLMN numeric that may contains wildcard character ".". // For example, the pattern "123..." could match all PLMN which mcc is 123. // PLMN numeric that may contains wildcard character "D". // A BCD value of 'D' in any of the MCC and/or MNC digits shall be used to indicate // a "wild" value for that corresponding MCC/MNC digit. // For example, the pattern "123DDD" could match all PLMN which mcc is 123. public final String plmnNumericPattern; public final int lacTacStart; public final int lacTacEnd; // Identifier of operator name in PNN to be displayed. // 0 indicates that the name is to be taken from other sources, see 3GPP TS 22.101. // pnnRecordId > 0 indicates record # (pnnRecordId - 1) in PNNs. public final int pnnRecordId; public final int plmnNetworkNameIndex; public OperatorPlmnInfo(String plmnNumericPattern, int lacTacStart, int lacTacEnd, int plmnNetworkNameIndex) { public OperatorPlmnInfo(@NonNull String plmnNumericPattern, int lacTacStart, int lacTacEnd, int pnnRecordId) { this.plmnNumericPattern = plmnNumericPattern; this.lacTacStart = lacTacStart; this.lacTacEnd = lacTacEnd; this.plmnNetworkNameIndex = plmnNetworkNameIndex; this.pnnRecordId = pnnRecordId; } /** * Check whether provided plmn and lacTac matches the stored OperatorPlmnInfo. * * @return -1 if not matching. */ public int getPnnIdx(@Nullable String plmn, int lacTac) { if (plmn == null || plmn.length() != plmnNumericPattern.length()) return -1; // Check whether PLMN matches with the plmnNumericPattern // Character-by-character check is for performance reasons. for (int i = 0; i < plmn.length(); i++) { if (plmn.charAt(i) != plmnNumericPattern.charAt(i) && plmnNumericPattern.charAt(i) != 'D') { return -1; } } // As defiend in 3GPP TS 31.102 section 4.2.59 , lacTacStart = 0 and lacTacEnd = 0xFFFE // are used to indicate the entire range of LACs/TACs for the given PLMN. if (lacTacStart == 0 && lacTacEnd == 0xFFFE) { return pnnRecordId - 1; } if (lacTac < lacTacStart || lacTac > lacTacEnd) return -1; return pnnRecordId - 1; } @Override public int hashCode() { return Objects.hash(plmnNumericPattern, lacTacStart, lacTacEnd, pnnRecordId); } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof OperatorPlmnInfo)) return false; OperatorPlmnInfo opi = (OperatorPlmnInfo) other; return TextUtils.equals(plmnNumericPattern, opi.plmnNumericPattern) && lacTacStart == opi.lacTacStart && lacTacEnd == opi.lacTacEnd && pnnRecordId == opi.pnnRecordId; } @Override public String toString() { return "{ plmnNumericPattern = " + plmnNumericPattern + "lacTacStart = " + lacTacStart + "lacTacEnd = " + lacTacEnd + "plmnNetworkNameIndex = " + plmnNetworkNameIndex return "{plmnNumericPattern = " + plmnNumericPattern + ", " + "lacTacStart = " + lacTacStart + ", " + "lacTacEnd = " + lacTacEnd + ", " + "pnnRecordId = " + pnnRecordId + "}"; } } Loading @@ -1398,9 +1498,36 @@ public abstract class IccRecords extends Handler implements IccConstants { this.shortName = shortName; } /** * Get the name stored in the PlmnNetworkName. * @return the full name if it's available; otherwise, short Name. */ @Nullable public String getName() { if (!TextUtils.isEmpty(fullName)) { return fullName; } else { return shortName; } } @Override public int hashCode() { return Objects.hash(fullName, shortName); } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof PlmnNetworkName)) return false; PlmnNetworkName pnn = (PlmnNetworkName) other; return TextUtils.equals(fullName, pnn.fullName) && TextUtils.equals(shortName, pnn.shortName); } @Override public String toString() { return "{ fullName = " + fullName + " shortName = " + shortName + " }"; return "{fullName = " + fullName + ", shortName = " + shortName + "}"; } } } src/java/com/android/internal/telephony/uicc/SIMRecords.java +105 −14 Original line number Diff line number Diff line Loading @@ -122,6 +122,9 @@ public class SIMRecords extends IccRecords { // Short Name IEI from TS 24.008 static final int TAG_SHORT_NETWORK_NAME = 0x45; // PLMN Additional Information tag from from TS 24.008 static final int TAG_PLMN_ADDITIONAL_INFORMATION = 0x80; // active CFF from CPHS 4.2 B.4.5 static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a; static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05; Loading Loading @@ -157,7 +160,8 @@ public class SIMRecords extends IccRecords { private static final int EVENT_GET_SPN_DONE = 12 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_SPDI_DONE = 13 + SIM_RECORD_EVENT_BASE; private static final int EVENT_UPDATE_DONE = 14 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE; protected static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE; protected static final int EVENT_GET_OPL_DONE = 16 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_ALL_SMS_DONE = 18 + SIM_RECORD_EVENT_BASE; private static final int EVENT_MARK_SMS_READ_DONE = 19 + SIM_RECORD_EVENT_BASE; Loading Loading @@ -228,6 +232,8 @@ public class SIMRecords extends IccRecords { mEfCPHS_MWI = null; mSpdi = null; mPnnHomeName = null; mPnns = null; mOpl = null; mGid1 = null; mGid2 = null; mPlmnActRecords = null; Loading Loading @@ -902,22 +908,22 @@ public class SIMRecords extends IccRecords { isRecordLoadResponse = true; ar = (AsyncResult) msg.obj; data = (byte[]) ar.result; if (ar.exception != null) { break; } SimTlv tlv = new SimTlv(data, 0, data.length); parseEfPnn((ArrayList<byte[]>) ar.result); break; for (; tlv.isValidObject(); tlv.nextObject()) { if (tlv.getTag() == TAG_FULL_NETWORK_NAME) { mPnnHomeName = IccUtils.networkNameToString( tlv.getData(), 0, tlv.getData().length); log("PNN: " + mPnnHomeName); case EVENT_GET_OPL_DONE: isRecordLoadResponse = true; ar = (AsyncResult) msg.obj; if (ar.exception != null) { break; } } parseEfOpl((ArrayList<byte[]>) ar.result); break; case EVENT_GET_ALL_SMS_DONE: Loading Loading @@ -1618,7 +1624,10 @@ public class SIMRecords extends IccRecords { mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); mRecordsToLoad++; mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); mFh.loadEFLinearFixedAll(EF_PNN, obtainMessage(EVENT_GET_PNN_DONE)); mRecordsToLoad++; mFh.loadEFLinearFixedAll(EF_OPL, obtainMessage(EVENT_GET_OPL_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); Loading Loading @@ -1878,13 +1887,95 @@ public class SIMRecords extends IccRecords { for (int i = 0; i + 2 < plmnEntries.length; i += 3) { String plmnCode = IccUtils.bcdPlmnToString(plmnEntries, i); if (!TextUtils.isEmpty(plmnCode)) { log("EF_SPDI PLMN: " + plmnCode); tmpSpdi.add(plmnCode); } } log("parseEfSpdi: " + tmpSpdi); mSpdi = tmpSpdi.toArray(new String[tmpSpdi.size()]); } /** * Parse EF PLMN Network Name (PNN) record from SIM * Reference: 3GPP TS 31.102 Section 4.2.58. */ private void parseEfPnn(ArrayList<byte[]> dataArray) { if (dataArray == null) return; final int count = dataArray.size(); List<PlmnNetworkName> tmpPnns = new ArrayList<>(count); for (int i = 0; i < count; i++) { byte[] data = dataArray.get(i); SimTlv tlv = new SimTlv(data, 0, data.length); String longName = null; String shortName = null; for (; tlv.isValidObject(); tlv.nextObject()) { switch (tlv.getTag()) { case TAG_FULL_NETWORK_NAME: longName = IccUtils.networkNameToString(tlv.getData(), 0, tlv.getData().length); break; case TAG_SHORT_NETWORK_NAME: shortName = IccUtils.networkNameToString(tlv.getData(), 0, tlv.getData().length); break; case TAG_PLMN_ADDITIONAL_INFORMATION: // TODO(b/154300344): read PLMN Additional Information. break; } } // PNNs must maintain their original indices. They will be referred to by index in OPL. tmpPnns.add(new PlmnNetworkName(longName, shortName)); } log("parseEfPnn: " + tmpPnns); mPnns = tmpPnns.toArray(new PlmnNetworkName[0]); // For compatiblility with legacy code. if (mPnns.length > 0) mPnnHomeName = mPnns[0].getName(); } /** * Parse EF Operator PLMN List (OPL) record from SIM * Reference: 3GPP TS 31.102 Section 4.2.59. */ private void parseEfOpl(ArrayList<byte[]> dataArray) { if (dataArray == null) return; final int count = dataArray.size(); List<OperatorPlmnInfo> tmpOpl = new ArrayList<>(count); for (int i = 0; i < count; i++) { byte[] data = dataArray.get(i); // data.length is 8 as defined in 3GPP TS 31.102 Section 4.2.59. // Byte 0 to 2 are for PLMN. // Byte 3 and 4 are for lacTacStart. // Byte 5 and 6 are for lacTacEnd. // Byte 7 is for PNN Record Identifier. if (data.length != 8) { loge("Invalid length for OPL record " + data); continue; } // A BCD value of 'D' in any of the MCC and/or MNC digits shall be used to indicate // a "wild" value for that corresponding MCC/MNC digit. String plmn = IccUtils.bcdPlmnToString(data, 0); if (plmn.length() < PLMN_MIN_LENGTH) { loge("Invalid length for decoded PLMN " + plmn); continue; } int lacTacStart = IccUtils.bytesToInt(data, 3, 2); int lacTacEnd = IccUtils.bytesToInt(data, 5, 2); int pnnRecordId = IccUtils.bytesToInt(data, 7, 1); tmpOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnRecordId)); } log("parseEfOpl: " + tmpOpl); mOpl = tmpOpl.toArray(new OperatorPlmnInfo[0]); } /** * convert a byte array of packed plmns to an array of strings */ Loading tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java +103 −0 Original line number Diff line number Diff line Loading @@ -48,11 +48,16 @@ import android.util.Log; import android.util.Pair; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo; import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.util.ArrayList; import java.util.List; public class IccRecordsTest extends TelephonyTest { private IccRecords mIccRecords; Loading Loading @@ -244,4 +249,102 @@ public class IccRecordsTest extends TelephonyTest { assertTrue("Two responses must be different.", !response.equals(response2)); } @Test public void testOperatorPlmnInfo() { String plmn = "123456"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 2; OperatorPlmnInfo opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex); assertEquals(opi.getPnnIdx(plmn, lacTacStart), pnnIndex - 1); assertEquals(opi.getPnnIdx(plmn, lacTacEnd), pnnIndex - 1); assertEquals(opi.getPnnIdx(plmn, 0xFFFF), pnnIndex - 1); assertEquals(opi.getPnnIdx("654321", 0xFFFF), -1); assertEquals(opi.getPnnIdx("12345", 0xFFFF), -1); lacTacStart = 0x0001; lacTacEnd = 0x1FFF; opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex); assertEquals(opi.getPnnIdx(plmn, 2), pnnIndex - 1); assertEquals(opi.getPnnIdx(plmn, 0x2FFF), -1); assertEquals(opi.getPnnIdx(plmn, 0xFFFF), -1); plmn = "123DDD"; opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex); assertEquals(opi.getPnnIdx("123123", lacTacStart), pnnIndex - 1); assertEquals(opi.getPnnIdx("12345", lacTacStart), -1); } @Test public void testGetNetworkNameForPlmn() { // Set up PNN String fullName1 = "Name full 1"; String shortName1 = "Name short 1"; String fullName2 = "Name full 2"; String shortName2 = "Name short 2"; List<PlmnNetworkName> pnns = new ArrayList<PlmnNetworkName>(); pnns.add(new PlmnNetworkName(fullName1, shortName1)); pnns.add(new PlmnNetworkName(fullName2, shortName2)); pnns.add(new PlmnNetworkName(null, shortName2)); PlmnNetworkName[] pnnsArray = pnns.toArray(new PlmnNetworkName[0]); // Set up OPL String plmn1 = "345678"; String plmn2 = "456789"; String plmn3 = "567890"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 1; List<OperatorPlmnInfo> opl = new ArrayList<OperatorPlmnInfo>(); opl.add(new OperatorPlmnInfo(plmn1, lacTacStart, lacTacEnd, pnnIndex)); opl.add(new OperatorPlmnInfo(plmn2, lacTacStart, lacTacEnd, pnnIndex + 1)); opl.add(new OperatorPlmnInfo(plmn3, lacTacStart, lacTacEnd, pnnIndex + 2)); OperatorPlmnInfo[] oplArray = opl.toArray(new OperatorPlmnInfo[0]); // Test assertNull(IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, null, 0)); assertEquals(fullName1, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn1, 0)); assertEquals(fullName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn2, 0)); assertEquals(shortName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn3, 0)); } @Test public void testGetNetworkNameForPlmnFromPnnOpl() { // Set up PNN String fullName1 = "Name full 1"; String shortName1 = "Name short 1"; String fullName2 = "Name full 2"; String shortName2 = "Name short 2"; List<PlmnNetworkName> pnns = new ArrayList<PlmnNetworkName>(); pnns.add(new PlmnNetworkName(fullName1, shortName1)); pnns.add(new PlmnNetworkName(fullName2, shortName2)); pnns.add(new PlmnNetworkName(null, shortName2)); PlmnNetworkName[] pnnsArray = pnns.toArray(new PlmnNetworkName[0]); // Set up OPL String plmn1 = "345678"; String plmn2 = "456789"; String plmn3 = "567890"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 1; List<OperatorPlmnInfo> opl = new ArrayList<OperatorPlmnInfo>(); opl.add(new OperatorPlmnInfo(plmn1, lacTacStart, lacTacEnd, pnnIndex)); opl.add(new OperatorPlmnInfo(plmn2, lacTacStart, lacTacEnd, pnnIndex + 1)); opl.add(new OperatorPlmnInfo(plmn3, lacTacStart, lacTacEnd, pnnIndex + 2)); OperatorPlmnInfo[] oplArray = opl.toArray(new OperatorPlmnInfo[0]); // Test assertNull(IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, null, 0)); assertEquals(fullName1, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn1, 0)); assertEquals(fullName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn2, 0)); assertEquals(shortName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn3, 0)); } } tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java +113 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.internal.telephony.uicc; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.eq; Loading @@ -35,7 +36,10 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo; import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName; import org.junit.After; import org.junit.Before; Loading Loading @@ -286,4 +290,113 @@ public class SIMRecordsTest extends TelephonyTest { assertNull(ar.exception); assertNull(ar.result); } @Test public void testGetEfPnn() { ArrayList<byte[]> rawPnns = new ArrayList<byte[]>(); List<PlmnNetworkName> targetPnns = new ArrayList<PlmnNetworkName>(); String name = "Test 1"; rawPnns.add(encodePnn(name)); targetPnns.add(new PlmnNetworkName(name, null)); name = "Test 2"; rawPnns.add(encodePnn(name)); targetPnns.add(new PlmnNetworkName(name, null)); Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_PNN_DONE); AsyncResult ar = AsyncResult.forMessage(message, rawPnns, null); mSIMRecordsUT.handleMessage(message); List<PlmnNetworkName> parsedPnns = Arrays.asList(mSIMRecordsUT.getPnns()); assertEquals(parsedPnns, targetPnns); } private static byte[] encodePnn(String name) { byte[] gsm7BitName = new byte[] {}; try { gsm7BitName = GsmAlphabet.stringToGsm7BitPacked(name); gsm7BitName[0] = (byte) (name.length() % 8 | 0x80); } catch (Exception ex) { fail("SimRecordsTest: GsmAlphabet.stringToGsm7BitPacked() exception:" + ex); } byte[] encodedName = new byte[gsm7BitName.length + 2]; encodedName[0] = 0x43; encodedName[1] = (byte) gsm7BitName.length; System.arraycopy(gsm7BitName, 0, encodedName, 2, gsm7BitName.length); return encodedName; } @Test public void testGetEfOpl() { ArrayList<byte[]> rawOpl = new ArrayList<byte[]>(); List<OperatorPlmnInfo> targetOpl = new ArrayList<OperatorPlmnInfo>(); // OperatorPlmnInfo 1 String plmn = "123456"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 0; rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex)); targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex)); Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE); AsyncResult ar = AsyncResult.forMessage(message, rawOpl, null); mSIMRecordsUT.handleMessage(message); List<OperatorPlmnInfo> parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl()); assertEquals(targetOpl, parsedOpl); // OperatorPlmnInfo 2 plmn = "123DDD"; lacTacStart = 0x0000; lacTacEnd = 0xFFFE; pnnIndex = 123; rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex)); targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex)); message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE); ar = AsyncResult.forMessage(message, rawOpl, null); mSIMRecordsUT.handleMessage(message); parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl()); assertEquals(targetOpl, parsedOpl); // OperatorPlmnInfo 3 plmn = "123"; lacTacStart = 0x0000; lacTacEnd = 0xFFFE; pnnIndex = 123; rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex)); message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE); ar = AsyncResult.forMessage(message, rawOpl, null); mSIMRecordsUT.handleMessage(message); parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl()); assertEquals(targetOpl, parsedOpl); } private byte[] encodeOpl(String plmn, int lacTacStart, int lacTacEnd, int pnnIndex) { byte[] data = new byte[8]; if (plmn.length() == 5 || plmn.length() == 6) { IccUtils.stringToBcdPlmn(plmn, data, 0); } else { data[0] = (byte) 0xFF; data[1] = (byte) 0xFF; data[2] = (byte) 0xFF; } data[3] = (byte) (lacTacStart >>> 8); data[4] = (byte) lacTacStart; data[5] = (byte) (lacTacEnd >>> 8); data[6] = (byte) lacTacEnd; data[7] = (byte) pnnIndex; return data; } } Loading
src/java/com/android/internal/telephony/uicc/IccRecords.java +140 −13 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.internal.telephony.uicc; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.AsyncResult; Loading @@ -25,6 +26,7 @@ import android.os.Message; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; import android.telephony.CellIdentity; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; Loading Loading @@ -53,6 +55,11 @@ public abstract class IccRecords extends Handler implements IccConstants { protected static final boolean DBG = true; protected static final boolean VDBG = false; // STOPSHIP if true public static final int PLMN_MIN_LENGTH = CellIdentity.MCC_LENGTH + CellIdentity.MNC_MIN_LENGTH; public static final int PLMN_MAX_LENGTH = CellIdentity.MCC_LENGTH + CellIdentity.MNC_MAX_LENGTH; // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length. private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = { "302370", "302720", "310260", Loading Loading @@ -148,6 +155,14 @@ public abstract class IccRecords extends Handler implements IccConstants { // Reference: 3GPP TS 31.102 Section 4.2.66 protected String[] mSpdi; // A list of PLMN Network Name (PNN). // Reference: 3GPP TS 31.102 Section 4.2.58 protected PlmnNetworkName[] mPnns; // Operator PLMN List (OPL). // Reference: 3GPP TS 31.102 Section 4.2.59 protected OperatorPlmnInfo[] mOpl; // Carrier name display condition bitmask // Reference: 3GPP TS 131.102 section 4.2.12 EF_SPN Display Condition Loading Loading @@ -697,6 +712,14 @@ public abstract class IccRecords extends Handler implements IccConstants { return mPnnHomeName; } public PlmnNetworkName[] getPnns() { return mPnns; } public OperatorPlmnInfo[] getOpl() { return mOpl; } public void setMsisdnNumber(String alphaTag, String number, Message onComplete) { loge("setMsisdn() should not be invoked on base IccRecords"); Loading Loading @@ -1353,6 +1376,36 @@ public abstract class IccRecords extends Handler implements IccConstants { pw.flush(); } /** * Get network name in PNN for the provided PLMN and LAC/TAC. * * @param opls OPL. * @param pnns PNN list. * @param plmn PLMN. * @param lacTac LAC/TAC * @return network Name for the provided PLMN and LAC/TAC. */ @Nullable public static String getNetworkNameForPlmnFromPnnOpl(PlmnNetworkName[] pnns, OperatorPlmnInfo[] opls, @Nullable String plmn, int lacTac) { if (opls == null || pnns == null || plmn == null || plmn.length() < PLMN_MIN_LENGTH || plmn.length() > PLMN_MAX_LENGTH) { return null; } for (OperatorPlmnInfo operatorPlmnInfo: opls) { int pnnIdx = operatorPlmnInfo.getPnnIdx(plmn, lacTac); if (pnnIdx >= 0) { if (pnnIdx < pnns.length && pnns[pnnIdx] != null) { return pnns[pnnIdx].getName(); } else { Rlog.e("IccRecords", "Invalid PNN record for Record" + pnnIdx); break; } } } return null; } /** * Operator PLMN information. This contains the location area information or tracking area * that are used to associate a specific name contained in EF_PNN. Loading @@ -1360,28 +1413,75 @@ public abstract class IccRecords extends Handler implements IccConstants { * Reference: 3GPP TS 31.102 section 4.2.59 EF_OPL */ public static final class OperatorPlmnInfo { // PLMN numeric that may contains wildcard character ".". // For example, the pattern "123..." could match all PLMN which mcc is 123. // PLMN numeric that may contains wildcard character "D". // A BCD value of 'D' in any of the MCC and/or MNC digits shall be used to indicate // a "wild" value for that corresponding MCC/MNC digit. // For example, the pattern "123DDD" could match all PLMN which mcc is 123. public final String plmnNumericPattern; public final int lacTacStart; public final int lacTacEnd; // Identifier of operator name in PNN to be displayed. // 0 indicates that the name is to be taken from other sources, see 3GPP TS 22.101. // pnnRecordId > 0 indicates record # (pnnRecordId - 1) in PNNs. public final int pnnRecordId; public final int plmnNetworkNameIndex; public OperatorPlmnInfo(String plmnNumericPattern, int lacTacStart, int lacTacEnd, int plmnNetworkNameIndex) { public OperatorPlmnInfo(@NonNull String plmnNumericPattern, int lacTacStart, int lacTacEnd, int pnnRecordId) { this.plmnNumericPattern = plmnNumericPattern; this.lacTacStart = lacTacStart; this.lacTacEnd = lacTacEnd; this.plmnNetworkNameIndex = plmnNetworkNameIndex; this.pnnRecordId = pnnRecordId; } /** * Check whether provided plmn and lacTac matches the stored OperatorPlmnInfo. * * @return -1 if not matching. */ public int getPnnIdx(@Nullable String plmn, int lacTac) { if (plmn == null || plmn.length() != plmnNumericPattern.length()) return -1; // Check whether PLMN matches with the plmnNumericPattern // Character-by-character check is for performance reasons. for (int i = 0; i < plmn.length(); i++) { if (plmn.charAt(i) != plmnNumericPattern.charAt(i) && plmnNumericPattern.charAt(i) != 'D') { return -1; } } // As defiend in 3GPP TS 31.102 section 4.2.59 , lacTacStart = 0 and lacTacEnd = 0xFFFE // are used to indicate the entire range of LACs/TACs for the given PLMN. if (lacTacStart == 0 && lacTacEnd == 0xFFFE) { return pnnRecordId - 1; } if (lacTac < lacTacStart || lacTac > lacTacEnd) return -1; return pnnRecordId - 1; } @Override public int hashCode() { return Objects.hash(plmnNumericPattern, lacTacStart, lacTacEnd, pnnRecordId); } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof OperatorPlmnInfo)) return false; OperatorPlmnInfo opi = (OperatorPlmnInfo) other; return TextUtils.equals(plmnNumericPattern, opi.plmnNumericPattern) && lacTacStart == opi.lacTacStart && lacTacEnd == opi.lacTacEnd && pnnRecordId == opi.pnnRecordId; } @Override public String toString() { return "{ plmnNumericPattern = " + plmnNumericPattern + "lacTacStart = " + lacTacStart + "lacTacEnd = " + lacTacEnd + "plmnNetworkNameIndex = " + plmnNetworkNameIndex return "{plmnNumericPattern = " + plmnNumericPattern + ", " + "lacTacStart = " + lacTacStart + ", " + "lacTacEnd = " + lacTacEnd + ", " + "pnnRecordId = " + pnnRecordId + "}"; } } Loading @@ -1398,9 +1498,36 @@ public abstract class IccRecords extends Handler implements IccConstants { this.shortName = shortName; } /** * Get the name stored in the PlmnNetworkName. * @return the full name if it's available; otherwise, short Name. */ @Nullable public String getName() { if (!TextUtils.isEmpty(fullName)) { return fullName; } else { return shortName; } } @Override public int hashCode() { return Objects.hash(fullName, shortName); } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof PlmnNetworkName)) return false; PlmnNetworkName pnn = (PlmnNetworkName) other; return TextUtils.equals(fullName, pnn.fullName) && TextUtils.equals(shortName, pnn.shortName); } @Override public String toString() { return "{ fullName = " + fullName + " shortName = " + shortName + " }"; return "{fullName = " + fullName + ", shortName = " + shortName + "}"; } } }
src/java/com/android/internal/telephony/uicc/SIMRecords.java +105 −14 Original line number Diff line number Diff line Loading @@ -122,6 +122,9 @@ public class SIMRecords extends IccRecords { // Short Name IEI from TS 24.008 static final int TAG_SHORT_NETWORK_NAME = 0x45; // PLMN Additional Information tag from from TS 24.008 static final int TAG_PLMN_ADDITIONAL_INFORMATION = 0x80; // active CFF from CPHS 4.2 B.4.5 static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a; static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05; Loading Loading @@ -157,7 +160,8 @@ public class SIMRecords extends IccRecords { private static final int EVENT_GET_SPN_DONE = 12 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_SPDI_DONE = 13 + SIM_RECORD_EVENT_BASE; private static final int EVENT_UPDATE_DONE = 14 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE; protected static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE; protected static final int EVENT_GET_OPL_DONE = 16 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE; private static final int EVENT_GET_ALL_SMS_DONE = 18 + SIM_RECORD_EVENT_BASE; private static final int EVENT_MARK_SMS_READ_DONE = 19 + SIM_RECORD_EVENT_BASE; Loading Loading @@ -228,6 +232,8 @@ public class SIMRecords extends IccRecords { mEfCPHS_MWI = null; mSpdi = null; mPnnHomeName = null; mPnns = null; mOpl = null; mGid1 = null; mGid2 = null; mPlmnActRecords = null; Loading Loading @@ -902,22 +908,22 @@ public class SIMRecords extends IccRecords { isRecordLoadResponse = true; ar = (AsyncResult) msg.obj; data = (byte[]) ar.result; if (ar.exception != null) { break; } SimTlv tlv = new SimTlv(data, 0, data.length); parseEfPnn((ArrayList<byte[]>) ar.result); break; for (; tlv.isValidObject(); tlv.nextObject()) { if (tlv.getTag() == TAG_FULL_NETWORK_NAME) { mPnnHomeName = IccUtils.networkNameToString( tlv.getData(), 0, tlv.getData().length); log("PNN: " + mPnnHomeName); case EVENT_GET_OPL_DONE: isRecordLoadResponse = true; ar = (AsyncResult) msg.obj; if (ar.exception != null) { break; } } parseEfOpl((ArrayList<byte[]>) ar.result); break; case EVENT_GET_ALL_SMS_DONE: Loading Loading @@ -1618,7 +1624,10 @@ public class SIMRecords extends IccRecords { mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); mRecordsToLoad++; mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); mFh.loadEFLinearFixedAll(EF_PNN, obtainMessage(EVENT_GET_PNN_DONE)); mRecordsToLoad++; mFh.loadEFLinearFixedAll(EF_OPL, obtainMessage(EVENT_GET_OPL_DONE)); mRecordsToLoad++; mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); Loading Loading @@ -1878,13 +1887,95 @@ public class SIMRecords extends IccRecords { for (int i = 0; i + 2 < plmnEntries.length; i += 3) { String plmnCode = IccUtils.bcdPlmnToString(plmnEntries, i); if (!TextUtils.isEmpty(plmnCode)) { log("EF_SPDI PLMN: " + plmnCode); tmpSpdi.add(plmnCode); } } log("parseEfSpdi: " + tmpSpdi); mSpdi = tmpSpdi.toArray(new String[tmpSpdi.size()]); } /** * Parse EF PLMN Network Name (PNN) record from SIM * Reference: 3GPP TS 31.102 Section 4.2.58. */ private void parseEfPnn(ArrayList<byte[]> dataArray) { if (dataArray == null) return; final int count = dataArray.size(); List<PlmnNetworkName> tmpPnns = new ArrayList<>(count); for (int i = 0; i < count; i++) { byte[] data = dataArray.get(i); SimTlv tlv = new SimTlv(data, 0, data.length); String longName = null; String shortName = null; for (; tlv.isValidObject(); tlv.nextObject()) { switch (tlv.getTag()) { case TAG_FULL_NETWORK_NAME: longName = IccUtils.networkNameToString(tlv.getData(), 0, tlv.getData().length); break; case TAG_SHORT_NETWORK_NAME: shortName = IccUtils.networkNameToString(tlv.getData(), 0, tlv.getData().length); break; case TAG_PLMN_ADDITIONAL_INFORMATION: // TODO(b/154300344): read PLMN Additional Information. break; } } // PNNs must maintain their original indices. They will be referred to by index in OPL. tmpPnns.add(new PlmnNetworkName(longName, shortName)); } log("parseEfPnn: " + tmpPnns); mPnns = tmpPnns.toArray(new PlmnNetworkName[0]); // For compatiblility with legacy code. if (mPnns.length > 0) mPnnHomeName = mPnns[0].getName(); } /** * Parse EF Operator PLMN List (OPL) record from SIM * Reference: 3GPP TS 31.102 Section 4.2.59. */ private void parseEfOpl(ArrayList<byte[]> dataArray) { if (dataArray == null) return; final int count = dataArray.size(); List<OperatorPlmnInfo> tmpOpl = new ArrayList<>(count); for (int i = 0; i < count; i++) { byte[] data = dataArray.get(i); // data.length is 8 as defined in 3GPP TS 31.102 Section 4.2.59. // Byte 0 to 2 are for PLMN. // Byte 3 and 4 are for lacTacStart. // Byte 5 and 6 are for lacTacEnd. // Byte 7 is for PNN Record Identifier. if (data.length != 8) { loge("Invalid length for OPL record " + data); continue; } // A BCD value of 'D' in any of the MCC and/or MNC digits shall be used to indicate // a "wild" value for that corresponding MCC/MNC digit. String plmn = IccUtils.bcdPlmnToString(data, 0); if (plmn.length() < PLMN_MIN_LENGTH) { loge("Invalid length for decoded PLMN " + plmn); continue; } int lacTacStart = IccUtils.bytesToInt(data, 3, 2); int lacTacEnd = IccUtils.bytesToInt(data, 5, 2); int pnnRecordId = IccUtils.bytesToInt(data, 7, 1); tmpOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnRecordId)); } log("parseEfOpl: " + tmpOpl); mOpl = tmpOpl.toArray(new OperatorPlmnInfo[0]); } /** * convert a byte array of packed plmns to an array of strings */ Loading
tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java +103 −0 Original line number Diff line number Diff line Loading @@ -48,11 +48,16 @@ import android.util.Log; import android.util.Pair; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo; import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.util.ArrayList; import java.util.List; public class IccRecordsTest extends TelephonyTest { private IccRecords mIccRecords; Loading Loading @@ -244,4 +249,102 @@ public class IccRecordsTest extends TelephonyTest { assertTrue("Two responses must be different.", !response.equals(response2)); } @Test public void testOperatorPlmnInfo() { String plmn = "123456"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 2; OperatorPlmnInfo opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex); assertEquals(opi.getPnnIdx(plmn, lacTacStart), pnnIndex - 1); assertEquals(opi.getPnnIdx(plmn, lacTacEnd), pnnIndex - 1); assertEquals(opi.getPnnIdx(plmn, 0xFFFF), pnnIndex - 1); assertEquals(opi.getPnnIdx("654321", 0xFFFF), -1); assertEquals(opi.getPnnIdx("12345", 0xFFFF), -1); lacTacStart = 0x0001; lacTacEnd = 0x1FFF; opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex); assertEquals(opi.getPnnIdx(plmn, 2), pnnIndex - 1); assertEquals(opi.getPnnIdx(plmn, 0x2FFF), -1); assertEquals(opi.getPnnIdx(plmn, 0xFFFF), -1); plmn = "123DDD"; opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex); assertEquals(opi.getPnnIdx("123123", lacTacStart), pnnIndex - 1); assertEquals(opi.getPnnIdx("12345", lacTacStart), -1); } @Test public void testGetNetworkNameForPlmn() { // Set up PNN String fullName1 = "Name full 1"; String shortName1 = "Name short 1"; String fullName2 = "Name full 2"; String shortName2 = "Name short 2"; List<PlmnNetworkName> pnns = new ArrayList<PlmnNetworkName>(); pnns.add(new PlmnNetworkName(fullName1, shortName1)); pnns.add(new PlmnNetworkName(fullName2, shortName2)); pnns.add(new PlmnNetworkName(null, shortName2)); PlmnNetworkName[] pnnsArray = pnns.toArray(new PlmnNetworkName[0]); // Set up OPL String plmn1 = "345678"; String plmn2 = "456789"; String plmn3 = "567890"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 1; List<OperatorPlmnInfo> opl = new ArrayList<OperatorPlmnInfo>(); opl.add(new OperatorPlmnInfo(plmn1, lacTacStart, lacTacEnd, pnnIndex)); opl.add(new OperatorPlmnInfo(plmn2, lacTacStart, lacTacEnd, pnnIndex + 1)); opl.add(new OperatorPlmnInfo(plmn3, lacTacStart, lacTacEnd, pnnIndex + 2)); OperatorPlmnInfo[] oplArray = opl.toArray(new OperatorPlmnInfo[0]); // Test assertNull(IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, null, 0)); assertEquals(fullName1, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn1, 0)); assertEquals(fullName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn2, 0)); assertEquals(shortName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn3, 0)); } @Test public void testGetNetworkNameForPlmnFromPnnOpl() { // Set up PNN String fullName1 = "Name full 1"; String shortName1 = "Name short 1"; String fullName2 = "Name full 2"; String shortName2 = "Name short 2"; List<PlmnNetworkName> pnns = new ArrayList<PlmnNetworkName>(); pnns.add(new PlmnNetworkName(fullName1, shortName1)); pnns.add(new PlmnNetworkName(fullName2, shortName2)); pnns.add(new PlmnNetworkName(null, shortName2)); PlmnNetworkName[] pnnsArray = pnns.toArray(new PlmnNetworkName[0]); // Set up OPL String plmn1 = "345678"; String plmn2 = "456789"; String plmn3 = "567890"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 1; List<OperatorPlmnInfo> opl = new ArrayList<OperatorPlmnInfo>(); opl.add(new OperatorPlmnInfo(plmn1, lacTacStart, lacTacEnd, pnnIndex)); opl.add(new OperatorPlmnInfo(plmn2, lacTacStart, lacTacEnd, pnnIndex + 1)); opl.add(new OperatorPlmnInfo(plmn3, lacTacStart, lacTacEnd, pnnIndex + 2)); OperatorPlmnInfo[] oplArray = opl.toArray(new OperatorPlmnInfo[0]); // Test assertNull(IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, null, 0)); assertEquals(fullName1, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn1, 0)); assertEquals(fullName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn2, 0)); assertEquals(shortName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, plmn3, 0)); } }
tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java +113 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.internal.telephony.uicc; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.eq; Loading @@ -35,7 +36,10 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo; import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName; import org.junit.After; import org.junit.Before; Loading Loading @@ -286,4 +290,113 @@ public class SIMRecordsTest extends TelephonyTest { assertNull(ar.exception); assertNull(ar.result); } @Test public void testGetEfPnn() { ArrayList<byte[]> rawPnns = new ArrayList<byte[]>(); List<PlmnNetworkName> targetPnns = new ArrayList<PlmnNetworkName>(); String name = "Test 1"; rawPnns.add(encodePnn(name)); targetPnns.add(new PlmnNetworkName(name, null)); name = "Test 2"; rawPnns.add(encodePnn(name)); targetPnns.add(new PlmnNetworkName(name, null)); Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_PNN_DONE); AsyncResult ar = AsyncResult.forMessage(message, rawPnns, null); mSIMRecordsUT.handleMessage(message); List<PlmnNetworkName> parsedPnns = Arrays.asList(mSIMRecordsUT.getPnns()); assertEquals(parsedPnns, targetPnns); } private static byte[] encodePnn(String name) { byte[] gsm7BitName = new byte[] {}; try { gsm7BitName = GsmAlphabet.stringToGsm7BitPacked(name); gsm7BitName[0] = (byte) (name.length() % 8 | 0x80); } catch (Exception ex) { fail("SimRecordsTest: GsmAlphabet.stringToGsm7BitPacked() exception:" + ex); } byte[] encodedName = new byte[gsm7BitName.length + 2]; encodedName[0] = 0x43; encodedName[1] = (byte) gsm7BitName.length; System.arraycopy(gsm7BitName, 0, encodedName, 2, gsm7BitName.length); return encodedName; } @Test public void testGetEfOpl() { ArrayList<byte[]> rawOpl = new ArrayList<byte[]>(); List<OperatorPlmnInfo> targetOpl = new ArrayList<OperatorPlmnInfo>(); // OperatorPlmnInfo 1 String plmn = "123456"; int lacTacStart = 0x0000; int lacTacEnd = 0xFFFE; int pnnIndex = 0; rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex)); targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex)); Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE); AsyncResult ar = AsyncResult.forMessage(message, rawOpl, null); mSIMRecordsUT.handleMessage(message); List<OperatorPlmnInfo> parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl()); assertEquals(targetOpl, parsedOpl); // OperatorPlmnInfo 2 plmn = "123DDD"; lacTacStart = 0x0000; lacTacEnd = 0xFFFE; pnnIndex = 123; rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex)); targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex)); message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE); ar = AsyncResult.forMessage(message, rawOpl, null); mSIMRecordsUT.handleMessage(message); parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl()); assertEquals(targetOpl, parsedOpl); // OperatorPlmnInfo 3 plmn = "123"; lacTacStart = 0x0000; lacTacEnd = 0xFFFE; pnnIndex = 123; rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex)); message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE); ar = AsyncResult.forMessage(message, rawOpl, null); mSIMRecordsUT.handleMessage(message); parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl()); assertEquals(targetOpl, parsedOpl); } private byte[] encodeOpl(String plmn, int lacTacStart, int lacTacEnd, int pnnIndex) { byte[] data = new byte[8]; if (plmn.length() == 5 || plmn.length() == 6) { IccUtils.stringToBcdPlmn(plmn, data, 0); } else { data[0] = (byte) 0xFF; data[1] = (byte) 0xFF; data[2] = (byte) 0xFF; } data[3] = (byte) (lacTacStart >>> 8); data[4] = (byte) lacTacStart; data[5] = (byte) (lacTacEnd >>> 8); data[6] = (byte) lacTacEnd; data[7] = (byte) pnnIndex; return data; } }