2 * Copyright (C) 2011 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.android.internal.telephony.cdma;
18 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
19 import com.android.internal.telephony.GsmAlphabet;
20 import com.android.internal.telephony.IccCardApplication.AppType;
21 import com.android.internal.telephony.IccFileHandler;
22 import com.android.internal.telephony.IccUtils;
23 import com.android.internal.telephony.MccTable;
24 import com.android.internal.telephony.PhoneBase;
25 import com.android.internal.telephony.cdma.sms.UserData;
26 import com.android.internal.telephony.gsm.SIMRecords;
27 import android.os.AsyncResult;
28 import android.os.Message;
29 import android.os.SystemProperties;
30 import android.util.Log;
31 import java.util.Locale;
32 import java.util.ArrayList;
37 public final class CdmaLteUiccRecords extends SIMRecords {
38 // From CSIM application
39 private byte[] mEFpl = null;
40 private byte[] mEFli = null;
41 boolean mCsimSpnDisplayCondition = false;
44 private String mPrlVersion;
45 private String mHomeSystemId;
46 private String mHomeNetworkId;
48 private static final int EVENT_GET_PL_DONE = CSIM_EVENT_BASE;
49 private static final int EVENT_GET_CSIM_LI_DONE = CSIM_EVENT_BASE + 1;
50 private static final int EVENT_GET_CSIM_SPN_DONE = CSIM_EVENT_BASE + 2;
51 private static final int EVENT_GET_CSIM_MDN_DONE = CSIM_EVENT_BASE + 3;
52 private static final int EVENT_GET_CSIM_IMSIM_DONE = CSIM_EVENT_BASE + 4;
53 private static final int EVENT_GET_CSIM_CDMAHOME_DONE = CSIM_EVENT_BASE + 5;
54 private static final int EVENT_GET_CSIM_EPRL_DONE = CSIM_EVENT_BASE + 6;
56 public CdmaLteUiccRecords(PhoneBase p) {
61 public void handleMessage(Message msg) {
65 boolean isCsimRecordLoadResponse = false;
67 try { switch (msg.what) {
68 case EVENT_GET_PL_DONE:
69 // Refer to ETSI TS.102.221
70 if (DBG) log("EF_GET_EF_PL_DONE");
71 isCsimRecordLoadResponse = true;
73 ar = (AsyncResult) msg.obj;
75 if (ar.exception != null) {
76 Log.e(LOG_TAG, "ar.exception = " + ar.exception);
80 mEFpl = (byte[]) ar.result;
81 if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
84 case EVENT_GET_CSIM_LI_DONE:
85 // Refer to C.S0065 5.2.26
86 if (DBG) log("EVENT_GET_CSIM_LI_DONE");
87 isCsimRecordLoadResponse = true;
89 ar = (AsyncResult) msg.obj;
90 if (ar.exception != null) {
91 Log.e(LOG_TAG, "ar.exception = " + ar.exception);
95 mEFli = (byte[]) ar.result;
96 // convert csim efli data to iso 639 format
97 for (int i = 0; i < mEFli.length; i+=2) {
99 case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break;
100 case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break;
101 case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break;
102 case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break;
103 case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break;
104 case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break;
105 case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break;
106 default: mEFli[i] = ' '; mEFli[i+1] = ' ';
110 if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
112 case EVENT_GET_CSIM_SPN_DONE:
113 // Refer to C.S0065 5.2.32
114 if (DBG) log("EVENT_GET_CSIM_SPN_DONE");
115 isCsimRecordLoadResponse = true;
116 ar = (AsyncResult) msg.obj;
118 if (ar.exception != null) {
119 Log.e(LOG_TAG, "ar.exception=" + ar.exception);
122 onGetCSimSpnDone(ar);
124 case EVENT_GET_CSIM_MDN_DONE:
125 if (DBG) log("EVENT_GET_CSIM_MDN_DONE");
126 isCsimRecordLoadResponse = true;
127 ar = (AsyncResult) msg.obj;
128 if (ar.exception != null) {
129 Log.e(LOG_TAG, "ar.exception=" + ar.exception);
132 onGetCSimMdnDone(ar);
134 case EVENT_GET_CSIM_IMSIM_DONE:
135 if (DBG) log("EVENT_GET_CSIM_IMSIM_DONE");
136 isCsimRecordLoadResponse = true;
137 ar = (AsyncResult) msg.obj;
138 if (ar.exception != null) {
139 Log.e(LOG_TAG, "ar.exception=" + ar.exception);
142 onGetCSimImsimDone(ar);
144 case EVENT_GET_CSIM_CDMAHOME_DONE:
145 if (DBG) log("EVENT_GET_CSIM_CDMAHOME_DONE");
146 isCsimRecordLoadResponse = true;
147 ar = (AsyncResult) msg.obj;
148 if (ar.exception != null) {
149 Log.e(LOG_TAG, "ar.exception=" + ar.exception);
152 onGetCSimCdmaHomeDone(ar);
154 case EVENT_GET_CSIM_EPRL_DONE:
155 if (DBG) log("EVENT_GET_CSIM_EPRL_DONE");
156 isCsimRecordLoadResponse = true;
157 ar = (AsyncResult) msg.obj;
158 if (ar.exception != null) {
159 Log.e(LOG_TAG, "ar.exception=" + ar.exception);
162 onGetCSimEprlDone(ar);
165 super.handleMessage(msg);
166 }}catch (RuntimeException exc) {
167 Log.w(LOG_TAG, "Exception parsing SIM record", exc);
169 if (isCsimRecordLoadResponse) {
176 protected void onRecordLoaded() {
177 // One record loaded successfully or failed, In either case
178 // we need to update the recordsToLoad count
181 if (recordsToLoad == 0 && recordsRequested == true) {
182 onAllRecordsLoaded();
183 } else if (recordsToLoad < 0) {
184 Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
190 protected void onAllRecordsLoaded() {
191 super.onAllRecordsLoaded();
196 protected void fetchSimRecords() {
197 IccFileHandler iccFh = phone.getIccFileHandler();
198 recordsRequested = true;
200 phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
203 iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
206 iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
209 iccFh.loadEFTransparent(EF_PL, obtainMessage(EVENT_GET_PL_DONE));
212 iccFh.loadEFTransparent(EF_CSIM_LI, obtainMessage(EVENT_GET_CSIM_LI_DONE));
215 iccFh.loadEFTransparent(EF_CSIM_SPN, obtainMessage(EVENT_GET_CSIM_SPN_DONE));
218 iccFh.loadEFLinearFixed(EF_CSIM_MDN, 1, obtainMessage(EVENT_GET_CSIM_MDN_DONE));
221 iccFh.loadEFTransparent(EF_CSIM_IMSIM, obtainMessage(EVENT_GET_CSIM_IMSIM_DONE));
224 iccFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
225 obtainMessage(EVENT_GET_CSIM_CDMAHOME_DONE));
228 iccFh.loadEFTransparent(EF_CSIM_EPRL, obtainMessage(EVENT_GET_CSIM_EPRL_DONE));
232 private void onGetCSimSpnDone(AsyncResult ar) {
233 byte[] data = (byte[]) ar.result;
234 if (DBG) log("CSIM_SPN=" +
235 IccUtils.bytesToHexString(data));
237 // C.S0065 for EF_SPN decoding
238 mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0) ? true : false;
240 int encoding = data[1];
241 int language = data[2];
242 byte[] spnData = new byte[32];
243 System.arraycopy(data, 3, spnData, 0, (data.length < 32)?data.length:32);
246 for (numBytes = 0; numBytes < spnData.length; numBytes++) {
247 if ((spnData[numBytes] & 0xFF) == 0xFF) break;
256 case UserData.ENCODING_OCTET:
257 case UserData.ENCODING_LATIN:
258 spn = new String(spnData, 0, numBytes, "ISO-8859-1");
260 case UserData.ENCODING_IA5:
261 case UserData.ENCODING_GSM_7BIT_ALPHABET:
262 case UserData.ENCODING_7BIT_ASCII:
263 spn = GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7);
265 case UserData.ENCODING_UNICODE_16:
266 spn = new String(spnData, 0, numBytes, "utf-16");
269 log("SPN encoding not supported");
271 } catch(Exception e) {
272 log("spn decode error: " + e);
274 if (DBG) log("spn=" + spn);
275 if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
276 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
279 private void onGetCSimMdnDone(AsyncResult ar) {
280 byte[] data = (byte[]) ar.result;
281 if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
282 int mdnDigitsNum = 0x0F & data[0];
283 mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
284 if (DBG) log("CSIM MDN=" + mMdn);
287 private void onGetCSimImsimDone(AsyncResult ar) {
288 byte[] data = (byte[]) ar.result;
289 if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
290 // C.S0065 section 5.2.2 for IMSI_M encoding
291 // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
292 boolean provisioned = ((data[7] & 0x80) == 0x80);
295 int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
296 int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
297 int digit7 = 0x0F & (data[4] >> 2);
298 if (digit7 > 0x09) digit7 = 0;
299 int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
300 first3digits = adjstMinDigits(first3digits);
301 second3digits = adjstMinDigits(second3digits);
302 last3digits = adjstMinDigits(last3digits);
304 StringBuilder builder = new StringBuilder();
305 builder.append(String.format(Locale.US, "%03d", first3digits));
306 builder.append(String.format(Locale.US, "%03d", second3digits));
307 builder.append(String.format(Locale.US, "%d", digit7));
308 builder.append(String.format(Locale.US, "%03d", last3digits));
309 if (DBG) log("min present=" + builder.toString());
311 mMin = builder.toString();
313 if (DBG) log("min not present");
317 private int adjstMinDigits (int digits) {
318 // Per C.S0005 section 2.3.1.
320 digits = (digits % 10 == 0)?(digits - 10):digits;
321 digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
322 digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
326 private void onGetCSimCdmaHomeDone(AsyncResult ar) {
327 // Per C.S0065 section 5.2.8
328 ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
329 if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
330 if (dataList.isEmpty()) {
333 StringBuilder sidBuf = new StringBuilder();
334 StringBuilder nidBuf = new StringBuilder();
336 for (byte[] data : dataList) {
337 if (data.length == 5) {
338 int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
339 int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
340 sidBuf.append(sid).append(",");
341 nidBuf.append(nid).append(",");
344 // remove trailing ","
345 sidBuf.setLength(sidBuf.length()-1);
346 nidBuf.setLength(nidBuf.length()-1);
348 mHomeSystemId = sidBuf.toString();
349 mHomeNetworkId = nidBuf.toString();
352 private void onGetCSimEprlDone(AsyncResult ar) {
353 // C.S0065 section 5.2.57 for EFeprl encoding
354 // C.S0016 section 3.5.5 for PRL format.
355 byte[] data = (byte[]) ar.result;
356 if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
358 // Only need the first 4 bytes of record
359 if (data.length > 3) {
360 int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
361 mPrlVersion = Integer.toString(prlId);
363 if (DBG) log("CSIM PRL version=" + mPrlVersion);
366 private void setLocaleFromCsim() {
367 String prefLang = null;
368 // check EFli then EFpl
369 prefLang = findBestLanguage(mEFli);
371 if (prefLang == null) {
372 prefLang = findBestLanguage(mEFpl);
375 if (prefLang != null) {
376 // check country code from SIM
377 String imsi = getIMSI();
378 String country = null;
380 country = MccTable.countryCodeForMcc(
381 Integer.parseInt(imsi.substring(0,3)));
383 log("Setting locale to " + prefLang + "_" + country);
384 phone.setSystemLocale(prefLang, country, false);
386 log ("No suitable CSIM selected locale");
390 private String findBestLanguage(byte[] languages) {
391 String bestMatch = null;
392 String[] locales = phone.getContext().getAssets().getLocales();
394 if ((languages == null) || (locales == null)) return null;
396 // Each 2-bytes consists of one language
397 for (int i = 0; (i + 1) < languages.length; i += 2) {
399 String lang = new String(languages, i, 2, "ISO-8859-1");
400 for (int j = 0; j < locales.length; j++) {
401 if (locales[j] != null && locales[j].length() >= 2 &&
402 locales[j].substring(0, 2).equals(lang)) {
406 if (bestMatch != null) break;
407 } catch(java.io.UnsupportedEncodingException e) {
408 log ("Failed to parse SIM language records");
411 // no match found. return null
416 protected void log(String s) {
417 if (DBG) Log.d(LOG_TAG, "[CSIM] " + s);
420 public String getMdn() {
424 public String getMin() {
428 public String getSid() {
429 return mHomeSystemId;
432 public String getNid() {
433 return mHomeNetworkId;
436 public String getPrlVersion() {
440 public boolean getCsimSpnDisplayCondition() {
441 return mCsimSpnDisplayCondition;
445 public boolean isProvisioned() {
446 // If UICC card has CSIM app, look for MDN and MIN field
447 // to determine if the SIM is provisioned. Otherwise,
448 // consider the SIM is provisioned. (for case of ordinal
450 if (phone.mIccCard.isApplicationOnIcc(AppType.APPTYPE_CSIM) &&
451 ((mMdn == null) || (mMin == null))) {