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