• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 
17 package com.android.internal.telephony.uicc;
18 
19 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM;
20 
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.os.AsyncResult;
24 import android.os.Message;
25 import android.os.SystemProperties;
26 import android.telephony.Rlog;
27 import android.telephony.SubscriptionInfo;
28 import android.telephony.SubscriptionManager;
29 import android.text.TextUtils;
30 import android.util.Log;
31 
32 import com.android.internal.telephony.CommandsInterface;
33 import com.android.internal.telephony.GsmAlphabet;
34 import com.android.internal.telephony.MccTable;
35 import com.android.internal.telephony.SubscriptionController;
36 import com.android.internal.telephony.cdma.sms.UserData;
37 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
38 import com.android.internal.util.BitwiseInputStream;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Locale;
45 
46 /**
47  * {@hide}
48  */
49 public class RuimRecords extends IccRecords {
50     static final String LOG_TAG = "RuimRecords";
51 
52     private boolean  mOtaCommited=false;
53 
54     // ***** Instance Variables
55 
56     private String mMyMobileNumber;
57     private String mMin2Min1;
58 
59     private String mPrlVersion;
60     // From CSIM application
61     private byte[] mEFpl = null;
62     private byte[] mEFli = null;
63     boolean mCsimSpnDisplayCondition = false;
64     private String mMdn;
65     private String mMin;
66     private String mHomeSystemId;
67     private String mHomeNetworkId;
68     private String mNai;
69 
70     @Override
toString()71     public String toString() {
72         return "RuimRecords: " + super.toString()
73                 + " m_ota_commited" + mOtaCommited
74                 + " mMyMobileNumber=" + "xxxx"
75                 + " mMin2Min1=" + mMin2Min1
76                 + " mPrlVersion=" + mPrlVersion
77                 + " mEFpl=" + mEFpl
78                 + " mEFli=" + mEFli
79                 + " mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition
80                 + " mMdn=" + mMdn
81                 + " mMin=" + mMin
82                 + " mHomeSystemId=" + mHomeSystemId
83                 + " mHomeNetworkId=" + mHomeNetworkId;
84     }
85 
86     // ***** Event Constants
87     private static final int EVENT_GET_IMSI_DONE = 3;
88     private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
89     private static final int EVENT_GET_ICCID_DONE = 5;
90     private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
91     private static final int EVENT_UPDATE_DONE = 14;
92     private static final int EVENT_GET_SST_DONE = 17;
93     private static final int EVENT_GET_ALL_SMS_DONE = 18;
94     private static final int EVENT_MARK_SMS_READ_DONE = 19;
95 
96     private static final int EVENT_SMS_ON_RUIM = 21;
97     private static final int EVENT_GET_SMS_DONE = 22;
98 
99     private static final int EVENT_RUIM_REFRESH = 31;
100 
RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci)101     public RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
102         super(app, c, ci);
103 
104         mAdnCache = new AdnRecordCache(mFh);
105 
106         mRecordsRequested = false;  // No load request is made till SIM ready
107 
108         // recordsToLoad is set to 0 because no requests are made yet
109         mRecordsToLoad = 0;
110 
111         // NOTE the EVENT_SMS_ON_RUIM is not registered
112         mCi.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
113 
114         // Start off by setting empty state
115         resetRecords();
116 
117         mParentApp.registerForReady(this, EVENT_APP_READY, null);
118         if (DBG) log("RuimRecords X ctor this=" + this);
119     }
120 
121     @Override
dispose()122     public void dispose() {
123         if (DBG) log("Disposing RuimRecords " + this);
124         //Unregister for all events
125         mCi.unregisterForIccRefresh(this);
126         mParentApp.unregisterForReady(this);
127         resetRecords();
128         super.dispose();
129     }
130 
131     @Override
finalize()132     protected void finalize() {
133         if(DBG) log("RuimRecords finalized");
134     }
135 
resetRecords()136     protected void resetRecords() {
137         mMncLength = UNINITIALIZED;
138         log("setting0 mMncLength" + mMncLength);
139         mIccId = null;
140         mFullIccId = null;
141 
142         mAdnCache.reset();
143 
144         // Don't clean up PROPERTY_ICC_OPERATOR_ISO_COUNTRY and
145         // PROPERTY_ICC_OPERATOR_NUMERIC here. Since not all CDMA
146         // devices have RUIM, these properties should keep the original
147         // values, e.g. build time settings, when there is no RUIM but
148         // set new values when RUIM is available and loaded.
149 
150         // recordsRequested is set to false indicating that the SIM
151         // read requests made so far are not valid. This is set to
152         // true only when fresh set of read requests are made.
153         mRecordsRequested = false;
154     }
155 
getMdnNumber()156     public String getMdnNumber() {
157         return mMyMobileNumber;
158     }
159 
getCdmaMin()160     public String getCdmaMin() {
161          return mMin2Min1;
162     }
163 
164     /** Returns null if RUIM is not yet ready */
getPrlVersion()165     public String getPrlVersion() {
166         return mPrlVersion;
167     }
168 
169     @Override
170     /** Returns null if RUIM is not yet ready */
getNAI()171     public String getNAI() {
172         return mNai;
173     }
174 
175     @Override
setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete)176     public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){
177         // In CDMA this is Operator/OEM dependent
178         AsyncResult.forMessage((onComplete)).exception =
179                 new IccException("setVoiceMailNumber not implemented");
180         onComplete.sendToTarget();
181         loge("method setVoiceMailNumber is not implemented");
182     }
183 
184     /**
185      * Called by CCAT Service when REFRESH is received.
186      * @param fileChanged indicates whether any files changed
187      * @param fileList if non-null, a list of EF files that changed
188      */
189     @Override
onRefresh(boolean fileChanged, int[] fileList)190     public void onRefresh(boolean fileChanged, int[] fileList) {
191         if (fileChanged) {
192             // A future optimization would be to inspect fileList and
193             // only reload those files that we care about.  For now,
194             // just re-fetch all RUIM records that we cache.
195             fetchRuimRecords();
196         }
197     }
198 
adjstMinDigits(int digits)199     private int adjstMinDigits (int digits) {
200         // Per C.S0005 section 2.3.1.
201         digits += 111;
202         digits = (digits % 10 == 0)?(digits - 10):digits;
203         digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
204         digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
205         return digits;
206     }
207 
208     /**
209      * Returns the 5 or 6 digit MCC/MNC of the operator that
210      *  provided the RUIM card. Returns null of RUIM is not yet ready
211      */
getRUIMOperatorNumeric()212     public String getRUIMOperatorNumeric() {
213         String imsi = getIMSI();
214 
215         if (imsi == null) {
216             return null;
217         }
218 
219         if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) {
220             // Length = length of MCC + length of MNC
221             // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
222             return imsi.substring(0, 3 + mMncLength);
223         }
224 
225         // Guess the MNC length based on the MCC if we don't
226         // have a valid value in ef[ad]
227 
228         int mcc = Integer.parseInt(imsi.substring(0, 3));
229         return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
230     }
231 
232     // Refer to ETSI TS 102.221
233     private class EfPlLoaded implements IccRecordLoaded {
234         @Override
getEfName()235         public String getEfName() {
236             return "EF_PL";
237         }
238 
239         @Override
onRecordLoaded(AsyncResult ar)240         public void onRecordLoaded(AsyncResult ar) {
241             mEFpl = (byte[]) ar.result;
242             if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
243         }
244     }
245 
246     // Refer to C.S0065 5.2.26
247     private class EfCsimLiLoaded implements IccRecordLoaded {
248         @Override
getEfName()249         public String getEfName() {
250             return "EF_CSIM_LI";
251         }
252 
253         @Override
onRecordLoaded(AsyncResult ar)254         public void onRecordLoaded(AsyncResult ar) {
255             mEFli = (byte[]) ar.result;
256             // convert csim efli data to iso 639 format
257             for (int i = 0; i < mEFli.length; i+=2) {
258                 switch(mEFli[i+1]) {
259                 case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break;
260                 case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break;
261                 case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break;
262                 case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break;
263                 case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break;
264                 case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break;
265                 case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break;
266                 default: mEFli[i] = ' '; mEFli[i+1] = ' ';
267                 }
268             }
269 
270             if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
271         }
272     }
273 
274     // Refer to C.S0065 5.2.32
275     private class EfCsimSpnLoaded implements IccRecordLoaded {
276         @Override
getEfName()277         public String getEfName() {
278             return "EF_CSIM_SPN";
279         }
280 
281         @Override
onRecordLoaded(AsyncResult ar)282         public void onRecordLoaded(AsyncResult ar) {
283             byte[] data = (byte[]) ar.result;
284             if (DBG) log("CSIM_SPN=" +
285                          IccUtils.bytesToHexString(data));
286 
287             // C.S0065 for EF_SPN decoding
288             mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0);
289 
290             int encoding = data[1];
291             int language = data[2];
292             byte[] spnData = new byte[32];
293             int len = ((data.length - 3) < 32) ? (data.length - 3) : 32;
294             System.arraycopy(data, 3, spnData, 0, len);
295 
296             int numBytes;
297             for (numBytes = 0; numBytes < spnData.length; numBytes++) {
298                 if ((spnData[numBytes] & 0xFF) == 0xFF) break;
299             }
300 
301             if (numBytes == 0) {
302                 setServiceProviderName("");
303                 return;
304             }
305             try {
306                 switch (encoding) {
307                 case UserData.ENCODING_OCTET:
308                 case UserData.ENCODING_LATIN:
309                     setServiceProviderName(new String(spnData, 0, numBytes, "ISO-8859-1"));
310                     break;
311                 case UserData.ENCODING_IA5:
312                 case UserData.ENCODING_GSM_7BIT_ALPHABET:
313                     setServiceProviderName(
314                             GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7));
315                     break;
316                 case UserData.ENCODING_7BIT_ASCII:
317                     String spn = new String(spnData, 0, numBytes, "US-ASCII");
318                     // To address issues with incorrect encoding scheme
319                     // programmed in some commercial CSIM cards, the decoded
320                     // SPN is checked to have characters in printable ASCII
321                     // range. If not, they are decoded with
322                     // ENCODING_GSM_7BIT_ALPHABET scheme.
323                     if (TextUtils.isPrintableAsciiOnly(spn)) {
324                         setServiceProviderName(spn);
325                     } else {
326                         if (DBG) log("Some corruption in SPN decoding = " + spn);
327                         if (DBG) log("Using ENCODING_GSM_7BIT_ALPHABET scheme...");
328                         setServiceProviderName(
329                                 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes * 8) / 7));
330                     }
331                 break;
332                 case UserData.ENCODING_UNICODE_16:
333                     setServiceProviderName(new String(spnData, 0, numBytes, "utf-16"));
334                     break;
335                 default:
336                     log("SPN encoding not supported");
337                 }
338             } catch(Exception e) {
339                 log("spn decode error: " + e);
340             }
341             if (DBG) log("spn=" + getServiceProviderName());
342             if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
343             mTelephonyManager.setSimOperatorNameForPhone(
344                     mParentApp.getPhoneId(), getServiceProviderName());
345         }
346     }
347 
348     private class EfCsimMdnLoaded implements IccRecordLoaded {
349         @Override
getEfName()350         public String getEfName() {
351             return "EF_CSIM_MDN";
352         }
353 
354         @Override
onRecordLoaded(AsyncResult ar)355         public void onRecordLoaded(AsyncResult ar) {
356             byte[] data = (byte[]) ar.result;
357             if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
358             // Refer to C.S0065 5.2.35
359             int mdnDigitsNum = 0x0F & data[0];
360             mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
361             if (DBG) log("CSIM MDN=" + mMdn);
362         }
363     }
364 
365     private class EfCsimImsimLoaded implements IccRecordLoaded {
366         @Override
getEfName()367         public String getEfName() {
368             return "EF_CSIM_IMSIM";
369         }
370 
371         @Override
onRecordLoaded(AsyncResult ar)372         public void onRecordLoaded(AsyncResult ar) {
373             byte[] data = (byte[]) ar.result;
374             if (VDBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
375             // C.S0065 section 5.2.2 for IMSI_M encoding
376             // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
377             boolean provisioned = ((data[7] & 0x80) == 0x80);
378 
379             if (provisioned) {
380                 int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
381                 int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
382                 int digit7 = 0x0F & (data[4] >> 2);
383                 if (digit7 > 0x09) digit7 = 0;
384                 int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
385                 first3digits = adjstMinDigits(first3digits);
386                 second3digits = adjstMinDigits(second3digits);
387                 last3digits = adjstMinDigits(last3digits);
388 
389                 StringBuilder builder = new StringBuilder();
390                 builder.append(String.format(Locale.US, "%03d", first3digits));
391                 builder.append(String.format(Locale.US, "%03d", second3digits));
392                 builder.append(String.format(Locale.US, "%d", digit7));
393                 builder.append(String.format(Locale.US, "%03d", last3digits));
394                 mMin = builder.toString();
395                 if (DBG) log("min present=" + mMin);
396             } else {
397                 if (DBG) log("min not present");
398             }
399         }
400     }
401 
402     private class EfCsimCdmaHomeLoaded implements IccRecordLoaded {
403         @Override
getEfName()404         public String getEfName() {
405             return "EF_CSIM_CDMAHOME";
406         }
407 
408         @Override
onRecordLoaded(AsyncResult ar)409         public void onRecordLoaded(AsyncResult ar) {
410             // Per C.S0065 section 5.2.8
411             ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
412             if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
413             if (dataList.isEmpty()) {
414                 return;
415             }
416             StringBuilder sidBuf = new StringBuilder();
417             StringBuilder nidBuf = new StringBuilder();
418 
419             for (byte[] data : dataList) {
420                 if (data.length == 5) {
421                     int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
422                     int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
423                     sidBuf.append(sid).append(',');
424                     nidBuf.append(nid).append(',');
425                 }
426             }
427             // remove trailing ","
428             sidBuf.setLength(sidBuf.length()-1);
429             nidBuf.setLength(nidBuf.length()-1);
430 
431             mHomeSystemId = sidBuf.toString();
432             mHomeNetworkId = nidBuf.toString();
433         }
434     }
435 
436     private class EfCsimEprlLoaded implements IccRecordLoaded {
437         @Override
getEfName()438         public String getEfName() {
439             return "EF_CSIM_EPRL";
440         }
441         @Override
onRecordLoaded(AsyncResult ar)442         public void onRecordLoaded(AsyncResult ar) {
443             onGetCSimEprlDone(ar);
444         }
445     }
446 
onGetCSimEprlDone(AsyncResult ar)447     private void onGetCSimEprlDone(AsyncResult ar) {
448         // C.S0065 section 5.2.57 for EFeprl encoding
449         // C.S0016 section 3.5.5 for PRL format.
450         byte[] data = (byte[]) ar.result;
451         if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
452 
453         // Only need the first 4 bytes of record
454         if (data.length > 3) {
455             int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
456             mPrlVersion = Integer.toString(prlId);
457         }
458         if (DBG) log("CSIM PRL version=" + mPrlVersion);
459     }
460 
461     private class EfCsimMipUppLoaded implements IccRecordLoaded {
462         @Override
getEfName()463         public String getEfName() {
464             return "EF_CSIM_MIPUPP";
465         }
466 
checkLengthLegal(int length, int expectLength)467         boolean checkLengthLegal(int length, int expectLength) {
468             if(length < expectLength) {
469                 Log.e(LOG_TAG, "CSIM MIPUPP format error, length = " + length  +
470                         "expected length at least =" + expectLength);
471                 return false;
472             } else {
473                 return true;
474             }
475         }
476 
477         @Override
onRecordLoaded(AsyncResult ar)478         public void onRecordLoaded(AsyncResult ar) {
479             // 3GPP2 C.S0065 section 5.2.24
480             byte[] data = (byte[]) ar.result;
481 
482             if(data.length < 1) {
483                 Log.e(LOG_TAG,"MIPUPP read error");
484                 return;
485             }
486 
487             BitwiseInputStream bitStream = new BitwiseInputStream(data);
488             try {
489                 int  mipUppLength = bitStream.read(8);
490                 //transfer length from byte to bit
491                 mipUppLength = (mipUppLength << 3);
492 
493                 if (!checkLengthLegal(mipUppLength, 1)) {
494                     return;
495                 }
496                 //parse the MIPUPP body 3GPP2 C.S0016-C 3.5.8.6
497                 int retryInfoInclude = bitStream.read(1);
498                 mipUppLength--;
499 
500                 if(retryInfoInclude == 1) {
501                     if (!checkLengthLegal(mipUppLength, 11)) {
502                         return;
503                     }
504                     bitStream.skip(11); //not used now
505                     //transfer length from byte to bit
506                     mipUppLength -= 11;
507                 }
508 
509                 if (!checkLengthLegal(mipUppLength, 4)) {
510                     return;
511                 }
512                 int numNai = bitStream.read(4);
513                 mipUppLength -= 4;
514 
515                 //start parse NAI body
516                 for(int index = 0; index < numNai; index++) {
517                     if (!checkLengthLegal(mipUppLength, 4)) {
518                         return;
519                     }
520                     int naiEntryIndex = bitStream.read(4);
521                     mipUppLength -= 4;
522 
523                     if (!checkLengthLegal(mipUppLength, 8)) {
524                         return;
525                     }
526                     int naiLength = bitStream.read(8);
527                     mipUppLength -= 8;
528 
529                     if(naiEntryIndex == 0) {
530                         //we find the one!
531                         if (!checkLengthLegal(mipUppLength, naiLength << 3)) {
532                             return;
533                         }
534                         char naiCharArray[] = new char[naiLength];
535                         for(int index1 = 0; index1 < naiLength; index1++) {
536                             naiCharArray[index1] = (char)(bitStream.read(8) & 0xFF);
537                         }
538                         mNai =  new String(naiCharArray);
539                         if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
540                             Log.v(LOG_TAG,"MIPUPP Nai = " + mNai);
541                         }
542                         return; //need not parsing further
543                     } else {
544                         //ignore this NAI body
545                         if (!checkLengthLegal(mipUppLength, (naiLength << 3) + 102)) {
546                             return;
547                         }
548                         bitStream.skip((naiLength << 3) + 101);//not used
549                         int mnAaaSpiIndicator = bitStream.read(1);
550                         mipUppLength -= ((naiLength << 3) + 102);
551 
552                         if(mnAaaSpiIndicator == 1) {
553                             if (!checkLengthLegal(mipUppLength, 32)) {
554                                 return;
555                             }
556                             bitStream.skip(32); //not used
557                             mipUppLength -= 32;
558                         }
559 
560                         //MN-HA_AUTH_ALGORITHM
561                         if (!checkLengthLegal(mipUppLength, 5)) {
562                             return;
563                         }
564                         bitStream.skip(4);
565                         mipUppLength -= 4;
566                         int mnHaSpiIndicator = bitStream.read(1);
567                         mipUppLength--;
568 
569                         if(mnHaSpiIndicator == 1) {
570                             if (!checkLengthLegal(mipUppLength, 32)) {
571                                 return;
572                             }
573                             bitStream.skip(32);
574                             mipUppLength -= 32;
575                         }
576                     }
577                 }
578             } catch(Exception e) {
579               Log.e(LOG_TAG,"MIPUPP read Exception error!");
580                 return;
581             }
582         }
583     }
584 
585     @Override
handleMessage(Message msg)586     public void handleMessage(Message msg) {
587         AsyncResult ar;
588 
589         byte data[];
590 
591         boolean isRecordLoadResponse = false;
592 
593         if (mDestroyed.get()) {
594             loge("Received message " + msg +
595                     "[" + msg.what + "] while being destroyed. Ignoring.");
596             return;
597         }
598 
599         try { switch (msg.what) {
600             case EVENT_APP_READY:
601                 onReady();
602                 break;
603 
604             case EVENT_GET_DEVICE_IDENTITY_DONE:
605                 log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
606             break;
607 
608             /* IO events */
609             case EVENT_GET_IMSI_DONE:
610                 isRecordLoadResponse = true;
611 
612                 ar = (AsyncResult)msg.obj;
613                 if (ar.exception != null) {
614                     loge("Exception querying IMSI, Exception:" + ar.exception);
615                     break;
616                 }
617 
618                 mImsi = (String) ar.result;
619 
620                 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
621                 // than 15 (and usually 15).
622                 if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
623                     loge("invalid IMSI " + mImsi);
624                     mImsi = null;
625                 }
626 
627                 // FIXME: CSIM IMSI may not contain the MNC.
628                 if (false) {
629                     log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");
630 
631                     String operatorNumeric = getRUIMOperatorNumeric();
632                     if (operatorNumeric != null) {
633                         if (operatorNumeric.length() <= 6) {
634                             log("update mccmnc=" + operatorNumeric);
635                             MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
636                         }
637                     }
638                 } else {
639                     String operatorNumeric = getRUIMOperatorNumeric();
640                     log("NO update mccmnc=" + operatorNumeric);
641                 }
642 
643             break;
644 
645             case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
646                 ar = (AsyncResult)msg.obj;
647                 String localTemp[] = (String[])ar.result;
648                 if (ar.exception != null) {
649                     break;
650                 }
651 
652                 mMyMobileNumber = localTemp[0];
653                 mMin2Min1 = localTemp[3];
654                 mPrlVersion = localTemp[4];
655 
656                 log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
657 
658             break;
659 
660             case EVENT_GET_ICCID_DONE:
661                 isRecordLoadResponse = true;
662 
663                 ar = (AsyncResult)msg.obj;
664                 data = (byte[])ar.result;
665 
666                 if (ar.exception != null) {
667                     break;
668                 }
669 
670                 mIccId = IccUtils.bcdToString(data, 0, data.length);
671                 mFullIccId = IccUtils.bchToString(data, 0, data.length);
672 
673                 log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId));
674 
675             break;
676 
677             case EVENT_UPDATE_DONE:
678                 ar = (AsyncResult)msg.obj;
679                 if (ar.exception != null) {
680                     Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception);
681                 }
682             break;
683 
684             case EVENT_GET_ALL_SMS_DONE:
685             case EVENT_MARK_SMS_READ_DONE:
686             case EVENT_SMS_ON_RUIM:
687             case EVENT_GET_SMS_DONE:
688                 Rlog.w(LOG_TAG, "Event not supported: " + msg.what);
689                 break;
690 
691             // TODO: probably EF_CST should be read instead
692             case EVENT_GET_SST_DONE:
693                 log("Event EVENT_GET_SST_DONE Received");
694             break;
695 
696             case EVENT_RUIM_REFRESH:
697                 isRecordLoadResponse = false;
698                 ar = (AsyncResult)msg.obj;
699                 if (ar.exception == null) {
700                     handleRuimRefresh((IccRefreshResponse)ar.result);
701                 }
702                 break;
703 
704             default:
705                 super.handleMessage(msg);   // IccRecords handles generic record load responses
706 
707         }}catch (RuntimeException exc) {
708             // I don't want these exceptions to be fatal
709             Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc);
710         } finally {
711             // Count up record load responses even if they are fails
712             if (isRecordLoadResponse) {
713                 onRecordLoaded();
714             }
715         }
716     }
717 
718     /**
719      * Returns an array of languages we have assets for.
720      *
721      * NOTE: This array will have duplicates. If this method will be caused
722      * frequently or in a tight loop, it can be rewritten for efficiency.
723      */
getAssetLanguages(Context ctx)724     private static String[] getAssetLanguages(Context ctx) {
725         final String[] locales = ctx.getAssets().getLocales();
726         final String[] localeLangs = new String[locales.length];
727         for (int i = 0; i < locales.length; ++i) {
728             final String localeStr = locales[i];
729             final int separator = localeStr.indexOf('-');
730             if (separator < 0) {
731                 localeLangs[i] = localeStr;
732             } else {
733                 localeLangs[i] = localeStr.substring(0, separator);
734             }
735         }
736 
737         return localeLangs;
738     }
739 
740     @Override
onRecordLoaded()741     protected void onRecordLoaded() {
742         // One record loaded successfully or failed, In either case
743         // we need to update the recordsToLoad count
744         mRecordsToLoad -= 1;
745         if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
746 
747         if (mRecordsToLoad == 0 && mRecordsRequested == true) {
748             onAllRecordsLoaded();
749         } else if (mRecordsToLoad < 0) {
750             loge("recordsToLoad <0, programmer error suspected");
751             mRecordsToLoad = 0;
752         }
753     }
754 
755     @Override
onAllRecordsLoaded()756     protected void onAllRecordsLoaded() {
757         if (DBG) log("record load complete");
758 
759         // Further records that can be inserted are Operator/OEM dependent
760 
761         // FIXME: CSIM IMSI may not contain the MNC.
762         if (false) {
763             String operator = getRUIMOperatorNumeric();
764             if (!TextUtils.isEmpty(operator)) {
765                 log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
766                         operator + "'");
767                 log("update icc_operator_numeric=" + operator);
768                 mTelephonyManager.setSimOperatorNumericForPhone(
769                         mParentApp.getPhoneId(), operator);
770             } else {
771                 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
772             }
773 
774             String imsi = getIMSI();
775 
776             if (!TextUtils.isEmpty(imsi)) {
777                 log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + imsi) : ""));
778                 mTelephonyManager.setSimCountryIsoForPhone(
779                         mParentApp.getPhoneId(),
780                         MccTable.countryCodeForMcc(
781                         Integer.parseInt(imsi.substring(0, 3))));
782             } else {
783                 log("onAllRecordsLoaded empty imsi skipping setting mcc");
784             }
785         }
786 
787         Resources resource = Resources.getSystem();
788         if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
789             setSimLanguage(mEFli, mEFpl);
790         }
791 
792         mRecordsLoadedRegistrants.notifyRegistrants(
793             new AsyncResult(null, null, null));
794 
795         // TODO: The below is hacky since the SubscriptionController may not be ready at this time.
796         if (!TextUtils.isEmpty(mMdn)) {
797             int phoneId = mParentApp.getUiccCard().getPhoneId();
798             int subId = SubscriptionController.getInstance().getSubIdUsingPhoneId(phoneId);
799             if (SubscriptionManager.isValidSubscriptionId(subId)) {
800                 SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subId);
801             } else {
802                 log("Cannot call setDisplayNumber: invalid subId");
803             }
804         }
805     }
806 
807     @Override
onReady()808     public void onReady() {
809         fetchRuimRecords();
810 
811         mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
812     }
813 
814 
fetchRuimRecords()815     private void fetchRuimRecords() {
816         mRecordsRequested = true;
817 
818         if (DBG) log("fetchRuimRecords " + mRecordsToLoad);
819 
820         mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
821         mRecordsToLoad++;
822 
823         mFh.loadEFTransparent(EF_ICCID,
824                 obtainMessage(EVENT_GET_ICCID_DONE));
825         mRecordsToLoad++;
826 
827         mFh.loadEFTransparent(EF_PL,
828                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
829         mRecordsToLoad++;
830 
831         mFh.loadEFTransparent(EF_CSIM_LI,
832                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
833         mRecordsToLoad++;
834 
835         mFh.loadEFTransparent(EF_CSIM_SPN,
836                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));
837         mRecordsToLoad++;
838 
839         mFh.loadEFLinearFixed(EF_CSIM_MDN, 1,
840                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded()));
841         mRecordsToLoad++;
842 
843         mFh.loadEFTransparent(EF_CSIM_IMSIM,
844                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded()));
845         mRecordsToLoad++;
846 
847         mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
848                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded()));
849         mRecordsToLoad++;
850 
851         // Entire PRL could be huge. We are only interested in
852         // the first 4 bytes of the record.
853         mFh.loadEFTransparent(EF_CSIM_EPRL, 4,
854                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded()));
855         mRecordsToLoad++;
856 
857         mFh.loadEFTransparent(EF_CSIM_MIPUPP,
858                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMipUppLoaded()));
859         mRecordsToLoad++;
860 
861         if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
862         // Further records that can be inserted are Operator/OEM dependent
863     }
864 
865     /**
866      * {@inheritDoc}
867      *
868      * No Display rule for RUIMs yet.
869      */
870     @Override
getDisplayRule(String plmn)871     public int getDisplayRule(String plmn) {
872         // TODO together with spn
873         return 0;
874     }
875 
876     @Override
isProvisioned()877     public boolean isProvisioned() {
878         // If UICC card has CSIM app, look for MDN and MIN field
879         // to determine if the SIM is provisioned.  Otherwise,
880         // consider the SIM is provisioned. (for case of ordinal
881         // USIM only UICC.)
882         // If PROPERTY_TEST_CSIM is defined, bypess provision check
883         // and consider the SIM is provisioned.
884         if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) {
885             return true;
886         }
887 
888         if (mParentApp == null) {
889             return false;
890         }
891 
892         if (mParentApp.getType() == AppType.APPTYPE_CSIM &&
893             ((mMdn == null) || (mMin == null))) {
894             return false;
895         }
896         return true;
897     }
898 
899     @Override
setVoiceMessageWaiting(int line, int countWaiting)900     public void setVoiceMessageWaiting(int line, int countWaiting) {
901         // Will be used in future to store voice mail count in UIM
902         // C.S0023-D_v1.0 does not have a file id in UIM for MWI
903         log("RuimRecords:setVoiceMessageWaiting - NOP for CDMA");
904     }
905 
906     @Override
getVoiceMessageCount()907     public int getVoiceMessageCount() {
908         // Will be used in future to retrieve voice mail count for UIM
909         // C.S0023-D_v1.0 does not have a file id in UIM for MWI
910         log("RuimRecords:getVoiceMessageCount - NOP for CDMA");
911         return 0;
912     }
913 
handleRuimRefresh(IccRefreshResponse refreshResponse)914     private void handleRuimRefresh(IccRefreshResponse refreshResponse) {
915         if (refreshResponse == null) {
916             if (DBG) log("handleRuimRefresh received without input");
917             return;
918         }
919 
920         if (!TextUtils.isEmpty(refreshResponse.aid)
921                 && !refreshResponse.aid.equals(mParentApp.getAid())) {
922             // This is for different app. Ignore.
923             return;
924         }
925 
926         switch (refreshResponse.refreshResult) {
927             case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
928                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED");
929                 mAdnCache.reset();
930                 fetchRuimRecords();
931                 break;
932             case IccRefreshResponse.REFRESH_RESULT_INIT:
933                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT");
934                 // need to reload all files (that we care about)
935                 onIccRefreshInit();
936                 break;
937             case IccRefreshResponse.REFRESH_RESULT_RESET:
938                 // Refresh reset is handled by the UiccCard object.
939                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET");
940                 break;
941             default:
942                 // unknown refresh operation
943                 if (DBG) log("handleRuimRefresh with unknown operation");
944                 break;
945         }
946     }
947 
getMdn()948     public String getMdn() {
949         return mMdn;
950     }
951 
getMin()952     public String getMin() {
953         return mMin;
954     }
955 
getSid()956     public String getSid() {
957         return mHomeSystemId;
958     }
959 
getNid()960     public String getNid() {
961         return mHomeNetworkId;
962     }
963 
getCsimSpnDisplayCondition()964     public boolean getCsimSpnDisplayCondition() {
965         return mCsimSpnDisplayCondition;
966     }
967     @Override
log(String s)968     protected void log(String s) {
969         Rlog.d(LOG_TAG, "[RuimRecords] " + s);
970     }
971 
972     @Override
loge(String s)973     protected void loge(String s) {
974         Rlog.e(LOG_TAG, "[RuimRecords] " + s);
975     }
976 
977     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)978     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
979         pw.println("RuimRecords: " + this);
980         pw.println(" extends:");
981         super.dump(fd, pw, args);
982         pw.println(" mOtaCommited=" + mOtaCommited);
983         pw.println(" mMyMobileNumber=" + mMyMobileNumber);
984         pw.println(" mMin2Min1=" + mMin2Min1);
985         pw.println(" mPrlVersion=" + mPrlVersion);
986         pw.println(" mEFpl[]=" + Arrays.toString(mEFpl));
987         pw.println(" mEFli[]=" + Arrays.toString(mEFli));
988         pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition);
989         pw.println(" mMdn=" + mMdn);
990         pw.println(" mMin=" + mMin);
991         pw.println(" mHomeSystemId=" + mHomeSystemId);
992         pw.println(" mHomeNetworkId=" + mHomeNetworkId);
993         pw.flush();
994     }
995 }
996