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