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