OSDN Git Service

am 0fbe1dfb: Merge "cherrypick from master: Change-Id: I169749dc594ca1d79a802db4c53ec...
[android-x86/frameworks-base.git] / telephony / java / com / android / internal / telephony / cdma / CdmaLteUiccRecords.java
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.android.internal.telephony.cdma;
17
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;
33
34 /**
35  * {@hide}
36  */
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;
42     private String mMdn;
43     private String mMin;
44     private String mPrlVersion;
45     private String mHomeSystemId;
46     private String mHomeNetworkId;
47
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;
55
56     public CdmaLteUiccRecords(PhoneBase p) {
57         super(p);
58     }
59
60     @Override
61     public void handleMessage(Message msg) {
62         AsyncResult ar;
63         byte data[];
64
65         boolean isCsimRecordLoadResponse = false;
66
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;
72
73                 ar = (AsyncResult) msg.obj;
74
75                 if (ar.exception != null) {
76                     Log.e(LOG_TAG, "ar.exception = " + ar.exception);
77                     break;
78                 }
79
80                 mEFpl = (byte[]) ar.result;
81                 if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
82                 break;
83
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;
88
89                 ar = (AsyncResult) msg.obj;
90                 if (ar.exception != null) {
91                     Log.e(LOG_TAG, "ar.exception = " + ar.exception);
92                     break;
93                 }
94
95                 mEFli = (byte[]) ar.result;
96                 // convert csim efli data to iso 639 format
97                 for (int i = 0; i < mEFli.length; i+=2) {
98                     switch(mEFli[i+1]) {
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] = ' ';
107                     }
108                 }
109
110                 if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
111                 break;
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;
117
118                 if (ar.exception != null) {
119                     Log.e(LOG_TAG, "ar.exception=" + ar.exception);
120                     break;
121                 }
122                 onGetCSimSpnDone(ar);
123                 break;
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);
130                     break;
131                 }
132                 onGetCSimMdnDone(ar);
133                 break;
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);
140                     break;
141                 }
142                 onGetCSimImsimDone(ar);
143                 break;
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);
150                     break;
151                 }
152                 onGetCSimCdmaHomeDone(ar);
153                 break;
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);
160                     break;
161                 }
162                 onGetCSimEprlDone(ar);
163                 break;
164             default:
165                 super.handleMessage(msg);
166         }}catch (RuntimeException exc) {
167             Log.w(LOG_TAG, "Exception parsing SIM record", exc);
168         } finally {
169             if (isCsimRecordLoadResponse) {
170                 onRecordLoaded();
171             }
172         }
173     }
174
175     @Override
176     protected void onRecordLoaded() {
177         // One record loaded successfully or failed, In either case
178         // we need to update the recordsToLoad count
179         recordsToLoad -= 1;
180
181         if (recordsToLoad == 0 && recordsRequested == true) {
182             onAllRecordsLoaded();
183         } else if (recordsToLoad < 0) {
184             Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
185             recordsToLoad = 0;
186         }
187     }
188
189     @Override
190     protected void onAllRecordsLoaded() {
191         super.onAllRecordsLoaded();
192         setLocaleFromCsim();
193     }
194
195     @Override
196     protected void fetchSimRecords() {
197         IccFileHandler iccFh = phone.getIccFileHandler();
198         recordsRequested = true;
199
200         phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
201         recordsToLoad++;
202
203         iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
204         recordsToLoad++;
205
206         iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
207         recordsToLoad++;
208
209         iccFh.loadEFTransparent(EF_PL, obtainMessage(EVENT_GET_PL_DONE));
210         recordsToLoad++;
211
212         iccFh.loadEFTransparent(EF_CSIM_LI, obtainMessage(EVENT_GET_CSIM_LI_DONE));
213         recordsToLoad++;
214
215         iccFh.loadEFTransparent(EF_CSIM_SPN, obtainMessage(EVENT_GET_CSIM_SPN_DONE));
216         recordsToLoad++;
217
218         iccFh.loadEFLinearFixed(EF_CSIM_MDN, 1, obtainMessage(EVENT_GET_CSIM_MDN_DONE));
219         recordsToLoad++;
220
221         iccFh.loadEFTransparent(EF_CSIM_IMSIM, obtainMessage(EVENT_GET_CSIM_IMSIM_DONE));
222         recordsToLoad++;
223
224         iccFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
225                                    obtainMessage(EVENT_GET_CSIM_CDMAHOME_DONE));
226         recordsToLoad++;
227
228         iccFh.loadEFTransparent(EF_CSIM_EPRL, obtainMessage(EVENT_GET_CSIM_EPRL_DONE));
229         recordsToLoad++;
230     }
231
232     private void onGetCSimSpnDone(AsyncResult ar) {
233         byte[] data = (byte[]) ar.result;
234         if (DBG) log("CSIM_SPN=" +
235                      IccUtils.bytesToHexString(data));
236
237         // C.S0065 for EF_SPN decoding
238         mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0) ? true : false;
239
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);
244
245         int numBytes;
246         for (numBytes = 0; numBytes < spnData.length; numBytes++) {
247             if ((spnData[numBytes] & 0xFF) == 0xFF) break;
248         }
249
250         if (numBytes == 0) {
251             spn = "";
252             return;
253         }
254         try {
255             switch (encoding) {
256             case UserData.ENCODING_OCTET:
257             case UserData.ENCODING_LATIN:
258                 spn = new String(spnData, 0, numBytes, "ISO-8859-1");
259                 break;
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);
264                 break;
265             case UserData.ENCODING_UNICODE_16:
266                 spn =  new String(spnData, 0, numBytes, "utf-16");
267                 break;
268             default:
269                 log("SPN encoding not supported");
270             }
271         } catch(Exception e) {
272             log("spn decode error: " + e);
273         }
274         if (DBG) log("spn=" + spn);
275         if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
276         phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
277     }
278
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);
285     }
286
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);
293
294         if (provisioned) {
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);
303
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());
310
311             mMin = builder.toString();
312         } else {
313             if (DBG) log("min not present");
314         }
315     }
316
317     private int adjstMinDigits (int digits) {
318         // Per C.S0005 section 2.3.1.
319         digits += 111;
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;
323         return digits;
324     }
325
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()) {
331             return;
332         }
333         StringBuilder sidBuf = new StringBuilder();
334         StringBuilder nidBuf = new StringBuilder();
335
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(",");
342             }
343         }
344         // remove trailing ","
345         sidBuf.setLength(sidBuf.length()-1);
346         nidBuf.setLength(nidBuf.length()-1);
347
348         mHomeSystemId = sidBuf.toString();
349         mHomeNetworkId = nidBuf.toString();
350     }
351
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));
357
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);
362         }
363         if (DBG) log("CSIM PRL version=" + mPrlVersion);
364     }
365
366     private void setLocaleFromCsim() {
367         String prefLang = null;
368         // check EFli then EFpl
369         prefLang = findBestLanguage(mEFli);
370
371         if (prefLang == null) {
372             prefLang = findBestLanguage(mEFpl);
373         }
374
375         if (prefLang != null) {
376             // check country code from SIM
377             String imsi = getIMSI();
378             String country = null;
379             if (imsi != null) {
380                 country = MccTable.countryCodeForMcc(
381                                     Integer.parseInt(imsi.substring(0,3)));
382             }
383             log("Setting locale to " + prefLang + "_" + country);
384             phone.setSystemLocale(prefLang, country, false);
385         } else {
386             log ("No suitable CSIM selected locale");
387         }
388     }
389
390     private String findBestLanguage(byte[] languages) {
391         String bestMatch = null;
392         String[] locales = phone.getContext().getAssets().getLocales();
393
394         if ((languages == null) || (locales == null)) return null;
395
396         // Each 2-bytes consists of one language
397         for (int i = 0; (i + 1) < languages.length; i += 2) {
398             try {
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)) {
403                         return lang;
404                     }
405                 }
406                 if (bestMatch != null) break;
407             } catch(java.io.UnsupportedEncodingException e) {
408                 log ("Failed to parse SIM language records");
409             }
410         }
411         // no match found. return null
412         return null;
413     }
414
415     @Override
416     protected void log(String s) {
417         if (DBG) Log.d(LOG_TAG, "[CSIM] " + s);
418     }
419
420     public String getMdn() {
421         return mMdn;
422     }
423
424     public String getMin() {
425         return mMin;
426     }
427
428     public String getSid() {
429         return mHomeSystemId;
430     }
431
432     public String getNid() {
433         return mHomeNetworkId;
434     }
435
436     public String getPrlVersion() {
437         return mPrlVersion;
438     }
439
440     public boolean getCsimSpnDisplayCondition() {
441         return mCsimSpnDisplayCondition;
442     }
443
444     @Override
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
449         // USIM only UICC.)
450         if (phone.mIccCard.isApplicationOnIcc(AppType.APPTYPE_CSIM) &&
451             ((mMdn == null) || (mMin == null))) {
452             return false;
453         }
454         return true;
455     }
456 }