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