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