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.annotation.NonNull;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.os.AsyncResult;
24 import android.os.Build;
25 import android.os.Message;
26 import android.sysprop.TelephonyProperties;
27 import android.telephony.SubscriptionInfo;
28 import android.telephony.SubscriptionManager;
29 import android.text.TextUtils;
30 import android.util.Log;
31 
32 import com.android.internal.annotations.VisibleForTesting;
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 import com.android.telephony.Rlog;
41 
42 import java.io.FileDescriptor;
43 import java.io.PrintWriter;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Locale;
47 
48 /**
49  * {@hide}
50  */
51 public class RuimRecords extends IccRecords {
52     static final String LOG_TAG = "RuimRecords";
53     private final static int IMSI_MIN_LENGTH = 10;
54 
55     private boolean  mOtaCommited=false;
56 
57     // ***** Instance Variables
58 
59     private String mMyMobileNumber;
60     private String mMin2Min1;
61 
62     private String mPrlVersion;
63     // From CSIM application
64     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
65     private byte[] mEFpl = null;
66     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
67     private byte[] mEFli = null;
68     boolean mCsimSpnDisplayCondition = false;
69     private String mMdn;
70     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
71     private String mMin;
72     private String mHomeSystemId;
73     private String mHomeNetworkId;
74     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
75     private String mNai;
76 
77     @Override
toString()78     public String toString() {
79         return "RuimRecords: " + super.toString()
80                 + " m_ota_commited" + mOtaCommited
81                 + " mMyMobileNumber=" + "xxxx"
82                 + " mMin2Min1=" + mMin2Min1
83                 + " mPrlVersion=" + mPrlVersion
84                 + " mEFpl=" + mEFpl
85                 + " mEFli=" + mEFli
86                 + " mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition
87                 + " mMdn=" + mMdn
88                 + " mMin=" + mMin
89                 + " mHomeSystemId=" + mHomeSystemId
90                 + " mHomeNetworkId=" + mHomeNetworkId;
91     }
92 
93     // ***** Event Constants
94     private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
95     private static final int EVENT_GET_ICCID_DONE = 5;
96     private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
97     private static final int EVENT_UPDATE_DONE = 14;
98     private static final int EVENT_GET_SST_DONE = 17;
99     private static final int EVENT_GET_ALL_SMS_DONE = 18;
100     private static final int EVENT_MARK_SMS_READ_DONE = 19;
101 
102     private static final int EVENT_SMS_ON_RUIM = 21;
103     private static final int EVENT_GET_SMS_DONE = 22;
104 
105     private static final int EVENT_APP_LOCKED = 32;
106     private static final int EVENT_APP_NETWORK_LOCKED = 33;
107 
RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci)108     public RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
109         super(app, c, ci);
110 
111         mAdnCache = new AdnRecordCache(mFh);
112 
113         mRecordsRequested = false;  // No load request is made till SIM ready
114         mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
115 
116         // recordsToLoad is set to 0 because no requests are made yet
117         mRecordsToLoad = 0;
118 
119         // NOTE the EVENT_SMS_ON_RUIM is not registered
120 
121         // Start off by setting empty state
122         resetRecords();
123         if (DBG) log("RuimRecords X ctor this=" + this);
124     }
125 
126     @Override
dispose()127     public void dispose() {
128         if (DBG) log("Disposing RuimRecords " + this);
129         resetRecords();
130         super.dispose();
131     }
132 
133     @Override
finalize()134     protected void finalize() {
135         if(DBG) log("RuimRecords finalized");
136     }
137 
resetRecords()138     protected void resetRecords() {
139         mMncLength = UNINITIALIZED;
140         log("setting0 mMncLength" + mMncLength);
141         mIccId = null;
142         mFullIccId = null;
143 
144         mAdnCache.reset();
145 
146         // Don't clean up PROPERTY_ICC_OPERATOR_ISO_COUNTRY and
147         // PROPERTY_ICC_OPERATOR_NUMERIC here. Since not all CDMA
148         // devices have RUIM, these properties should keep the original
149         // values, e.g. build time settings, when there is no RUIM but
150         // set new values when RUIM is available and loaded.
151 
152         // recordsRequested is set to false indicating that the SIM
153         // read requests made so far are not valid. This is set to
154         // true only when fresh set of read requests are made.
155         mRecordsRequested = false;
156         mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
157         mLoaded.set(false);
158     }
159 
160     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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 
204     /**
205      * Returns the 5 or 6 digit MCC/MNC of the operator that
206      *  provided the RUIM card. Returns null of RUIM is not yet ready
207      */
208     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getRUIMOperatorNumeric()209     public String getRUIMOperatorNumeric() {
210         String imsi = getIMSI();
211 
212         if (imsi == null) {
213             return null;
214         }
215 
216         if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) {
217             // Length = length of MCC + length of MNC
218             // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
219             return imsi.substring(0, 3 + mMncLength);
220         }
221 
222         // Guess the MNC length based on the MCC if we don't
223         // have a valid value in ef[ad]
224 
225         int mcc = Integer.parseInt(imsi.substring(0, 3));
226         return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
227     }
228 
229     // Refer to ETSI TS 102.221
230     private class EfPlLoaded implements IccRecordLoaded {
231         @Override
getEfName()232         public String getEfName() {
233             return "EF_PL";
234         }
235 
236         @Override
onRecordLoaded(AsyncResult ar)237         public void onRecordLoaded(AsyncResult ar) {
238             mEFpl = (byte[]) ar.result;
239             if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
240         }
241     }
242 
243     // Refer to C.S0065 5.2.26
244     private class EfCsimLiLoaded implements IccRecordLoaded {
245         @Override
getEfName()246         public String getEfName() {
247             return "EF_CSIM_LI";
248         }
249 
250         @Override
onRecordLoaded(AsyncResult ar)251         public void onRecordLoaded(AsyncResult ar) {
252             mEFli = (byte[]) ar.result;
253             // convert csim efli data to iso 639 format
254             for (int i = 0; i < mEFli.length; i+=2) {
255                 switch(mEFli[i+1]) {
256                 case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break;
257                 case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break;
258                 case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break;
259                 case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break;
260                 case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break;
261                 case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break;
262                 case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break;
263                 default: mEFli[i] = ' '; mEFli[i+1] = ' ';
264                 }
265             }
266 
267             if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
268         }
269     }
270 
271     // Refer to C.S0065 5.2.32
272     private class EfCsimSpnLoaded implements IccRecordLoaded {
273         @Override
getEfName()274         public String getEfName() {
275             return "EF_CSIM_SPN";
276         }
277 
278         @Override
onRecordLoaded(AsyncResult ar)279         public void onRecordLoaded(AsyncResult ar) {
280             byte[] data = (byte[]) ar.result;
281             if (DBG) log("CSIM_SPN=" +
282                          IccUtils.bytesToHexString(data));
283 
284             // C.S0065 for EF_SPN decoding
285             mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0);
286 
287             int encoding = data[1];
288             int language = data[2];
289             byte[] spnData = new byte[32];
290             int len = ((data.length - 3) < 32) ? (data.length - 3) : 32;
291             System.arraycopy(data, 3, spnData, 0, len);
292 
293             int numBytes;
294             for (numBytes = 0; numBytes < spnData.length; numBytes++) {
295                 if ((spnData[numBytes] & 0xFF) == 0xFF) break;
296             }
297 
298             if (numBytes == 0) {
299                 setServiceProviderName("");
300                 return;
301             }
302             try {
303                 switch (encoding) {
304                 case UserData.ENCODING_OCTET:
305                 case UserData.ENCODING_LATIN:
306                     setServiceProviderName(new String(spnData, 0, numBytes, "ISO-8859-1"));
307                     break;
308                 case UserData.ENCODING_IA5:
309                 case UserData.ENCODING_GSM_7BIT_ALPHABET:
310                     setServiceProviderName(
311                             GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7));
312                     break;
313                 case UserData.ENCODING_7BIT_ASCII:
314                     String spn = new String(spnData, 0, numBytes, "US-ASCII");
315                     // To address issues with incorrect encoding scheme
316                     // programmed in some commercial CSIM cards, the decoded
317                     // SPN is checked to have characters in printable ASCII
318                     // range. If not, they are decoded with
319                     // ENCODING_GSM_7BIT_ALPHABET scheme.
320                     if (isPrintableAsciiOnly(spn)) {
321                         setServiceProviderName(spn);
322                     } else {
323                         if (DBG) log("Some corruption in SPN decoding = " + spn);
324                         if (DBG) log("Using ENCODING_GSM_7BIT_ALPHABET scheme...");
325                         setServiceProviderName(
326                                 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes * 8) / 7));
327                     }
328                 break;
329                 case UserData.ENCODING_UNICODE_16:
330                     setServiceProviderName(new String(spnData, 0, numBytes, "utf-16"));
331                     break;
332                 default:
333                     log("SPN encoding not supported");
334                 }
335             } catch(Exception e) {
336                 log("spn decode error: " + e);
337             }
338             if (DBG) log("spn=" + getServiceProviderName());
339             if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
340             mTelephonyManager.setSimOperatorNameForPhone(
341                     mParentApp.getPhoneId(), getServiceProviderName());
342         }
343     }
344 
isPrintableAsciiOnly(final CharSequence str)345     private static boolean isPrintableAsciiOnly(final CharSequence str) {
346         final int len = str.length();
347         for (int i = 0; i < len; i++) {
348             if (!isPrintableAscii(str.charAt(i))) {
349                 return false;
350             }
351         }
352         return true;
353     }
354 
isPrintableAscii(final char c)355     private static boolean isPrintableAscii(final char c) {
356         final int asciiFirst = 0x20;
357         final int asciiLast = 0x7E;  // included
358         return (asciiFirst <= c && c <= asciiLast) || c == '\r' || c == '\n';
359     }
360 
361     private class EfCsimMdnLoaded implements IccRecordLoaded {
362         @Override
getEfName()363         public String getEfName() {
364             return "EF_CSIM_MDN";
365         }
366 
367         @Override
onRecordLoaded(AsyncResult ar)368         public void onRecordLoaded(AsyncResult ar) {
369             byte[] data = (byte[]) ar.result;
370             if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
371             // Refer to C.S0065 5.2.35
372             int mdnDigitsNum = 0x0F & data[0];
373             mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
374             if (DBG) log("CSIM MDN=" + mMdn);
375         }
376     }
377 
378     /**
379      * Parses IMSI based on C.S0065 section 5.2.2 and C.S0005 section 2.3.1
380      */
381     @VisibleForTesting
382     public class EfCsimImsimLoaded implements IccRecordLoaded {
383         @Override
getEfName()384         public String getEfName() {
385             return "EF_CSIM_IMSIM";
386         }
387 
388         @Override
onRecordLoaded(AsyncResult ar)389         public void onRecordLoaded(AsyncResult ar) {
390             byte[] data = (byte[]) ar.result;
391             if (data == null || data.length < IMSI_MIN_LENGTH) {
392                 loge("Invalid IMSI from EF_CSIM_IMSIM");
393                 return;
394             }
395             if (DBG) log("data=" + Rlog.pii(LOG_TAG, IccUtils.bytesToHexString(data)));
396             // C.S0065 section 5.2.2 for IMSI_M encoding
397             // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
398             boolean provisioned = ((data[7] & 0x80) == 0x80);
399 
400             if (provisioned) {
401                 final String imsi = decodeImsi(data);
402                 if (TextUtils.isEmpty(mImsi)) {
403                     mImsi = imsi;
404                     if (DBG) log("IMSI=" + Rlog.pii(LOG_TAG, mImsi));
405                 }
406                 mMin = imsi.substring(5, 15);
407                 if (DBG) log("min present=" + Rlog.pii(LOG_TAG, mMin));
408             } else {
409                 if (DBG) log("min not present");
410             }
411         }
412 
decodeImsiDigits(int digits, int length)413         private int decodeImsiDigits(int digits, int length) {
414             // Per C.S0005 section 2.3.1.
415             for (int i = 0, denominator = 1; i < length; i++) {
416                 digits += denominator;
417                 if ((digits / denominator) % 10 == 0) {
418                     digits = digits - (10 * denominator);
419                 }
420                 denominator *= 10;
421             }
422             return digits;
423         }
424 
425         /**
426          * Decode utility to decode IMSI from data read from EF_IMSIM
427          * Please refer to
428          * C.S0065 section 5.2.2 for IMSI_M encoding
429          * C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
430          */
431         @VisibleForTesting
432         @NonNull
decodeImsi(byte[] data)433         public String decodeImsi(byte[] data) {
434             // Retrieve the MCC and digits 11 and 12
435             int mcc_data = ((0x03 & data[9]) << 8) | (0xFF & data[8]);
436             int mcc = decodeImsiDigits(mcc_data, 3);
437             int digits_11_12_data = data[6] & 0x7f;
438             int digits_11_12 = decodeImsiDigits(digits_11_12_data, 2);
439 
440             // Retrieve 10 MIN digits
441             int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
442             int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
443             int digit7 = 0x0F & (data[4] >> 2);
444             if (digit7 > 0x09) digit7 = 0;
445             int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
446 
447             first3digits = decodeImsiDigits(first3digits, 3);
448             second3digits = decodeImsiDigits(second3digits, 3);
449             last3digits = decodeImsiDigits(last3digits, 3);
450 
451             StringBuilder builder = new StringBuilder();
452             builder.append(String.format(Locale.US, "%03d", mcc));
453             builder.append(String.format(Locale.US, "%02d", digits_11_12));
454             builder.append(String.format(Locale.US, "%03d", first3digits));
455             builder.append(String.format(Locale.US, "%03d", second3digits));
456             builder.append(String.format(Locale.US, "%d", digit7));
457             builder.append(String.format(Locale.US, "%03d", last3digits));
458             return  builder.toString();
459         }
460     }
461 
462     private class EfCsimCdmaHomeLoaded implements IccRecordLoaded {
463         @Override
getEfName()464         public String getEfName() {
465             return "EF_CSIM_CDMAHOME";
466         }
467 
468         @Override
onRecordLoaded(AsyncResult ar)469         public void onRecordLoaded(AsyncResult ar) {
470             // Per C.S0065 section 5.2.8
471             ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
472             if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
473             if (dataList.isEmpty()) {
474                 return;
475             }
476             StringBuilder sidBuf = new StringBuilder();
477             StringBuilder nidBuf = new StringBuilder();
478 
479             for (byte[] data : dataList) {
480                 if (data.length == 5) {
481                     int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
482                     int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
483                     sidBuf.append(sid).append(',');
484                     nidBuf.append(nid).append(',');
485                 }
486             }
487             // remove trailing ","
488             sidBuf.setLength(sidBuf.length()-1);
489             nidBuf.setLength(nidBuf.length()-1);
490 
491             mHomeSystemId = sidBuf.toString();
492             mHomeNetworkId = nidBuf.toString();
493         }
494     }
495 
496     private class EfCsimEprlLoaded implements IccRecordLoaded {
497         @Override
getEfName()498         public String getEfName() {
499             return "EF_CSIM_EPRL";
500         }
501         @Override
onRecordLoaded(AsyncResult ar)502         public void onRecordLoaded(AsyncResult ar) {
503             onGetCSimEprlDone(ar);
504         }
505     }
506 
507     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
onGetCSimEprlDone(AsyncResult ar)508     private void onGetCSimEprlDone(AsyncResult ar) {
509         // C.S0065 section 5.2.57 for EFeprl encoding
510         // C.S0016 section 3.5.5 for PRL format.
511         byte[] data = (byte[]) ar.result;
512         if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
513 
514         // Only need the first 4 bytes of record
515         if (data.length > 3) {
516             int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
517             mPrlVersion = Integer.toString(prlId);
518         }
519         if (DBG) log("CSIM PRL version=" + mPrlVersion);
520     }
521 
522     private class EfCsimMipUppLoaded implements IccRecordLoaded {
523         @Override
getEfName()524         public String getEfName() {
525             return "EF_CSIM_MIPUPP";
526         }
527 
checkLengthLegal(int length, int expectLength)528         boolean checkLengthLegal(int length, int expectLength) {
529             if(length < expectLength) {
530                 Log.e(LOG_TAG, "CSIM MIPUPP format error, length = " + length  +
531                         "expected length at least =" + expectLength);
532                 return false;
533             } else {
534                 return true;
535             }
536         }
537 
538         @Override
onRecordLoaded(AsyncResult ar)539         public void onRecordLoaded(AsyncResult ar) {
540             // 3GPP2 C.S0065 section 5.2.24
541             byte[] data = (byte[]) ar.result;
542 
543             if(data.length < 1) {
544                 Log.e(LOG_TAG,"MIPUPP read error");
545                 return;
546             }
547 
548             BitwiseInputStream bitStream = new BitwiseInputStream(data);
549             try {
550                 int  mipUppLength = bitStream.read(8);
551                 //transfer length from byte to bit
552                 mipUppLength = (mipUppLength << 3);
553 
554                 if (!checkLengthLegal(mipUppLength, 1)) {
555                     return;
556                 }
557                 //parse the MIPUPP body 3GPP2 C.S0016-C 3.5.8.6
558                 int retryInfoInclude = bitStream.read(1);
559                 mipUppLength--;
560 
561                 if(retryInfoInclude == 1) {
562                     if (!checkLengthLegal(mipUppLength, 11)) {
563                         return;
564                     }
565                     bitStream.skip(11); //not used now
566                     //transfer length from byte to bit
567                     mipUppLength -= 11;
568                 }
569 
570                 if (!checkLengthLegal(mipUppLength, 4)) {
571                     return;
572                 }
573                 int numNai = bitStream.read(4);
574                 mipUppLength -= 4;
575 
576                 //start parse NAI body
577                 for(int index = 0; index < numNai; index++) {
578                     if (!checkLengthLegal(mipUppLength, 4)) {
579                         return;
580                     }
581                     int naiEntryIndex = bitStream.read(4);
582                     mipUppLength -= 4;
583 
584                     if (!checkLengthLegal(mipUppLength, 8)) {
585                         return;
586                     }
587                     int naiLength = bitStream.read(8);
588                     mipUppLength -= 8;
589 
590                     if(naiEntryIndex == 0) {
591                         //we find the one!
592                         if (!checkLengthLegal(mipUppLength, naiLength << 3)) {
593                             return;
594                         }
595                         char naiCharArray[] = new char[naiLength];
596                         for(int index1 = 0; index1 < naiLength; index1++) {
597                             naiCharArray[index1] = (char)(bitStream.read(8) & 0xFF);
598                         }
599                         mNai =  new String(naiCharArray);
600                         if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
601                             Log.v(LOG_TAG,"MIPUPP Nai = " + mNai);
602                         }
603                         return; //need not parsing further
604                     } else {
605                         //ignore this NAI body
606                         if (!checkLengthLegal(mipUppLength, (naiLength << 3) + 102)) {
607                             return;
608                         }
609                         bitStream.skip((naiLength << 3) + 101);//not used
610                         int mnAaaSpiIndicator = bitStream.read(1);
611                         mipUppLength -= ((naiLength << 3) + 102);
612 
613                         if(mnAaaSpiIndicator == 1) {
614                             if (!checkLengthLegal(mipUppLength, 32)) {
615                                 return;
616                             }
617                             bitStream.skip(32); //not used
618                             mipUppLength -= 32;
619                         }
620 
621                         //MN-HA_AUTH_ALGORITHM
622                         if (!checkLengthLegal(mipUppLength, 5)) {
623                             return;
624                         }
625                         bitStream.skip(4);
626                         mipUppLength -= 4;
627                         int mnHaSpiIndicator = bitStream.read(1);
628                         mipUppLength--;
629 
630                         if(mnHaSpiIndicator == 1) {
631                             if (!checkLengthLegal(mipUppLength, 32)) {
632                                 return;
633                             }
634                             bitStream.skip(32);
635                             mipUppLength -= 32;
636                         }
637                     }
638                 }
639             } catch(Exception e) {
640               Log.e(LOG_TAG,"MIPUPP read Exception error!");
641                 return;
642             }
643         }
644     }
645 
646     @Override
handleMessage(Message msg)647     public void handleMessage(Message msg) {
648         AsyncResult ar;
649 
650         byte data[];
651 
652         boolean isRecordLoadResponse = false;
653 
654         if (mDestroyed.get()) {
655             loge("Received message " + msg +
656                     "[" + msg.what + "] while being destroyed. Ignoring.");
657             return;
658         }
659 
660         try {
661             switch (msg.what) {
662             case EVENT_GET_DEVICE_IDENTITY_DONE:
663                 log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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 
840     @Override
onLocked()841     protected void onLocked() {
842         if (DBG) log("only fetch EF_ICCID in locked state");
843         super.onLocked();
844 
845         mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
846         mRecordsToLoad++;
847     }
848 
849     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
fetchRuimRecords()850     private void fetchRuimRecords() {
851         mRecordsRequested = true;
852 
853         if (DBG) log("fetchRuimRecords " + 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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getCsimSpnDisplayCondition()962     public boolean getCsimSpnDisplayCondition() {
963         return mCsimSpnDisplayCondition;
964     }
965     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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