Home | History | Annotate | Download | only in cdma
      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.cdma;
     18 
     19 
     20 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
     21 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
     22 
     23 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
     24 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM;
     25 
     26 import java.util.ArrayList;
     27 import java.util.Locale;
     28 import android.content.Context;
     29 import android.os.AsyncResult;
     30 import android.os.Handler;
     31 import android.os.Message;
     32 import android.os.Registrant;
     33 import android.os.SystemProperties;
     34 import android.util.Log;
     35 
     36 import com.android.internal.telephony.AdnRecord;
     37 import com.android.internal.telephony.AdnRecordCache;
     38 import com.android.internal.telephony.AdnRecordLoader;
     39 import com.android.internal.telephony.CommandsInterface;
     40 import com.android.internal.telephony.IccCardConstants;
     41 import com.android.internal.telephony.GsmAlphabet;
     42 import com.android.internal.telephony.IccRefreshResponse;
     43 import com.android.internal.telephony.PhoneBase;
     44 import com.android.internal.telephony.TelephonyProperties;
     45 import com.android.internal.telephony.MccTable;
     46 import com.android.internal.telephony.UiccCardApplication;
     47 
     48 // can't be used since VoiceMailConstants is not public
     49 //import com.android.internal.telephony.gsm.VoiceMailConstants;
     50 import com.android.internal.telephony.IccException;
     51 import com.android.internal.telephony.IccRecords;
     52 import com.android.internal.telephony.IccUtils;
     53 import com.android.internal.telephony.PhoneProxy;
     54 import com.android.internal.telephony.IccCardApplicationStatus.AppType;
     55 import com.android.internal.telephony.IccRecords.IccRecordLoaded;
     56 import com.android.internal.telephony.cdma.sms.UserData;
     57 
     58 
     59 /**
     60  * {@hide}
     61  */
     62 public final class RuimRecords extends IccRecords {
     63     static final String LOG_TAG = "CDMA";
     64 
     65     private static final boolean DBG = true;
     66     private boolean  m_ota_commited=false;
     67 
     68     // ***** Instance Variables
     69 
     70     private String mMyMobileNumber;
     71     private String mMin2Min1;
     72 
     73     private String mPrlVersion;
     74     // From CSIM application
     75     private byte[] mEFpl = null;
     76     private byte[] mEFli = null;
     77     boolean mCsimSpnDisplayCondition = false;
     78     private String mMdn;
     79     private String mMin;
     80     private String mHomeSystemId;
     81     private String mHomeNetworkId;
     82 
     83     // ***** Event Constants
     84     private static final int EVENT_APP_READY = 1;
     85     private static final int EVENT_GET_IMSI_DONE = 3;
     86     private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
     87     private static final int EVENT_GET_ICCID_DONE = 5;
     88     private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
     89     private static final int EVENT_UPDATE_DONE = 14;
     90     private static final int EVENT_GET_SST_DONE = 17;
     91     private static final int EVENT_GET_ALL_SMS_DONE = 18;
     92     private static final int EVENT_MARK_SMS_READ_DONE = 19;
     93 
     94     private static final int EVENT_SMS_ON_RUIM = 21;
     95     private static final int EVENT_GET_SMS_DONE = 22;
     96 
     97     private static final int EVENT_RUIM_REFRESH = 31;
     98 
     99     public RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
    100         super(app, c, ci);
    101 
    102         adnCache = new AdnRecordCache(mFh);
    103 
    104         recordsRequested = false;  // No load request is made till SIM ready
    105 
    106         // recordsToLoad is set to 0 because no requests are made yet
    107         recordsToLoad = 0;
    108 
    109         // NOTE the EVENT_SMS_ON_RUIM is not registered
    110         mCi.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
    111 
    112         // Start off by setting empty state
    113         resetRecords();
    114 
    115         mParentApp.registerForReady(this, EVENT_APP_READY, null);
    116     }
    117 
    118     @Override
    119     public void dispose() {
    120         if (DBG) log("Disposing RuimRecords " + this);
    121         //Unregister for all events
    122         mCi.unregisterForIccRefresh(this);
    123         mParentApp.unregisterForReady(this);
    124         resetRecords();
    125         super.dispose();
    126     }
    127 
    128     @Override
    129     protected void finalize() {
    130         if(DBG) log("RuimRecords finalized");
    131     }
    132 
    133     protected void resetRecords() {
    134         countVoiceMessages = 0;
    135         mncLength = UNINITIALIZED;
    136         iccid = null;
    137 
    138         adnCache.reset();
    139 
    140         // Don't clean up PROPERTY_ICC_OPERATOR_ISO_COUNTRY and
    141         // PROPERTY_ICC_OPERATOR_NUMERIC here. Since not all CDMA
    142         // devices have RUIM, these properties should keep the original
    143         // values, e.g. build time settings, when there is no RUIM but
    144         // set new values when RUIM is available and loaded.
    145 
    146         // recordsRequested is set to false indicating that the SIM
    147         // read requests made so far are not valid. This is set to
    148         // true only when fresh set of read requests are made.
    149         recordsRequested = false;
    150     }
    151 
    152     @Override
    153     public String getIMSI() {
    154         return mImsi;
    155     }
    156 
    157     public String getMdnNumber() {
    158         return mMyMobileNumber;
    159     }
    160 
    161     public String getCdmaMin() {
    162          return mMin2Min1;
    163     }
    164 
    165     /** Returns null if RUIM is not yet ready */
    166     public String getPrlVersion() {
    167         return mPrlVersion;
    168     }
    169 
    170     @Override
    171     public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){
    172         // In CDMA this is Operator/OEM dependent
    173         AsyncResult.forMessage((onComplete)).exception =
    174                 new IccException("setVoiceMailNumber not implemented");
    175         onComplete.sendToTarget();
    176         loge("method setVoiceMailNumber is not implemented");
    177     }
    178 
    179     /**
    180      * Called by CCAT Service when REFRESH is received.
    181      * @param fileChanged indicates whether any files changed
    182      * @param fileList if non-null, a list of EF files that changed
    183      */
    184     @Override
    185     public void onRefresh(boolean fileChanged, int[] fileList) {
    186         if (fileChanged) {
    187             // A future optimization would be to inspect fileList and
    188             // only reload those files that we care about.  For now,
    189             // just re-fetch all RUIM records that we cache.
    190             fetchRuimRecords();
    191         }
    192     }
    193 
    194     private int adjstMinDigits (int digits) {
    195         // Per C.S0005 section 2.3.1.
    196         digits += 111;
    197         digits = (digits % 10 == 0)?(digits - 10):digits;
    198         digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
    199         digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
    200         return digits;
    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     public String getRUIMOperatorNumeric() {
    208         if (mImsi == null) {
    209             return null;
    210         }
    211 
    212         if (mncLength != UNINITIALIZED && mncLength != UNKNOWN) {
    213             // Length = length of MCC + length of MNC
    214             // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
    215             return mImsi.substring(0, 3 + mncLength);
    216         }
    217 
    218         // Guess the MNC length based on the MCC if we don't
    219         // have a valid value in ef[ad]
    220 
    221         int mcc = Integer.parseInt(mImsi.substring(0,3));
    222         return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
    223     }
    224 
    225     // Refer to ETSI TS 102.221
    226     private class EfPlLoaded implements IccRecordLoaded {
    227         public String getEfName() {
    228             return "EF_PL";
    229         }
    230 
    231         public void onRecordLoaded(AsyncResult ar) {
    232             mEFpl = (byte[]) ar.result;
    233             if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl));
    234         }
    235     }
    236 
    237     // Refer to C.S0065 5.2.26
    238     private class EfCsimLiLoaded implements IccRecordLoaded {
    239         public String getEfName() {
    240             return "EF_CSIM_LI";
    241         }
    242 
    243         public void onRecordLoaded(AsyncResult ar) {
    244             mEFli = (byte[]) ar.result;
    245             // convert csim efli data to iso 639 format
    246             for (int i = 0; i < mEFli.length; i+=2) {
    247                 switch(mEFli[i+1]) {
    248                 case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break;
    249                 case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break;
    250                 case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break;
    251                 case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break;
    252                 case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break;
    253                 case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break;
    254                 case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break;
    255                 default: mEFli[i] = ' '; mEFli[i+1] = ' ';
    256                 }
    257             }
    258 
    259             if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli));
    260         }
    261     }
    262 
    263     // Refer to C.S0065 5.2.32
    264     private class EfCsimSpnLoaded implements IccRecordLoaded {
    265         public String getEfName() {
    266             return "EF_CSIM_SPN";
    267         }
    268 
    269         public void onRecordLoaded(AsyncResult ar) {
    270             byte[] data = (byte[]) ar.result;
    271             if (DBG) log("CSIM_SPN=" +
    272                          IccUtils.bytesToHexString(data));
    273 
    274             // C.S0065 for EF_SPN decoding
    275             mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0);
    276 
    277             int encoding = data[1];
    278             int language = data[2];
    279             byte[] spnData = new byte[32];
    280             int len = ((data.length - 3) < 32) ? (data.length - 3) : 32;
    281             System.arraycopy(data, 3, spnData, 0, len);
    282 
    283             int numBytes;
    284             for (numBytes = 0; numBytes < spnData.length; numBytes++) {
    285                 if ((spnData[numBytes] & 0xFF) == 0xFF) break;
    286             }
    287 
    288             if (numBytes == 0) {
    289                 spn = "";
    290                 return;
    291             }
    292             try {
    293                 switch (encoding) {
    294                 case UserData.ENCODING_OCTET:
    295                 case UserData.ENCODING_LATIN:
    296                     spn = new String(spnData, 0, numBytes, "ISO-8859-1");
    297                     break;
    298                 case UserData.ENCODING_IA5:
    299                 case UserData.ENCODING_GSM_7BIT_ALPHABET:
    300                 case UserData.ENCODING_7BIT_ASCII:
    301                     spn = GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7);
    302                     break;
    303                 case UserData.ENCODING_UNICODE_16:
    304                     spn =  new String(spnData, 0, numBytes, "utf-16");
    305                     break;
    306                 default:
    307                     log("SPN encoding not supported");
    308                 }
    309             } catch(Exception e) {
    310                 log("spn decode error: " + e);
    311             }
    312             if (DBG) log("spn=" + spn);
    313             if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition);
    314             SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, spn);
    315         }
    316     }
    317 
    318     private class EfCsimMdnLoaded implements IccRecordLoaded {
    319         public String getEfName() {
    320             return "EF_CSIM_MDN";
    321         }
    322 
    323         public void onRecordLoaded(AsyncResult ar) {
    324             byte[] data = (byte[]) ar.result;
    325             if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
    326             // Refer to C.S0065 5.2.35
    327             int mdnDigitsNum = 0x0F & data[0];
    328             mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
    329             if (DBG) log("CSIM MDN=" + mMdn);
    330         }
    331     }
    332 
    333     private class EfCsimImsimLoaded implements IccRecordLoaded {
    334         public String getEfName() {
    335             return "EF_CSIM_IMSIM";
    336         }
    337 
    338         public void onRecordLoaded(AsyncResult ar) {
    339             byte[] data = (byte[]) ar.result;
    340             if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
    341             // C.S0065 section 5.2.2 for IMSI_M encoding
    342             // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
    343             boolean provisioned = ((data[7] & 0x80) == 0x80);
    344 
    345             if (provisioned) {
    346                 int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
    347                 int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
    348                 int digit7 = 0x0F & (data[4] >> 2);
    349                 if (digit7 > 0x09) digit7 = 0;
    350                 int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
    351                 first3digits = adjstMinDigits(first3digits);
    352                 second3digits = adjstMinDigits(second3digits);
    353                 last3digits = adjstMinDigits(last3digits);
    354 
    355                 StringBuilder builder = new StringBuilder();
    356                 builder.append(String.format(Locale.US, "%03d", first3digits));
    357                 builder.append(String.format(Locale.US, "%03d", second3digits));
    358                 builder.append(String.format(Locale.US, "%d", digit7));
    359                 builder.append(String.format(Locale.US, "%03d", last3digits));
    360                 mMin = builder.toString();
    361                 if (DBG) log("min present=" + mMin);
    362             } else {
    363                 if (DBG) log("min not present");
    364             }
    365         }
    366     }
    367 
    368     private class EfCsimCdmaHomeLoaded implements IccRecordLoaded {
    369         public String getEfName() {
    370             return "EF_CSIM_CDMAHOME";
    371         }
    372 
    373         public void onRecordLoaded(AsyncResult ar) {
    374             // Per C.S0065 section 5.2.8
    375             ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
    376             if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
    377             if (dataList.isEmpty()) {
    378                 return;
    379             }
    380             StringBuilder sidBuf = new StringBuilder();
    381             StringBuilder nidBuf = new StringBuilder();
    382 
    383             for (byte[] data : dataList) {
    384                 if (data.length == 5) {
    385                     int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
    386                     int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
    387                     sidBuf.append(sid).append(',');
    388                     nidBuf.append(nid).append(',');
    389                 }
    390             }
    391             // remove trailing ","
    392             sidBuf.setLength(sidBuf.length()-1);
    393             nidBuf.setLength(nidBuf.length()-1);
    394 
    395             mHomeSystemId = sidBuf.toString();
    396             mHomeNetworkId = nidBuf.toString();
    397         }
    398     }
    399 
    400     private class EfCsimEprlLoaded implements IccRecordLoaded {
    401         public String getEfName() {
    402             return "EF_CSIM_EPRL";
    403         }
    404         public void onRecordLoaded(AsyncResult ar) {
    405             onGetCSimEprlDone(ar);
    406         }
    407     }
    408 
    409     private void onGetCSimEprlDone(AsyncResult ar) {
    410         // C.S0065 section 5.2.57 for EFeprl encoding
    411         // C.S0016 section 3.5.5 for PRL format.
    412         byte[] data = (byte[]) ar.result;
    413         if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
    414 
    415         // Only need the first 4 bytes of record
    416         if (data.length > 3) {
    417             int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
    418             mPrlVersion = Integer.toString(prlId);
    419         }
    420         if (DBG) log("CSIM PRL version=" + mPrlVersion);
    421     }
    422 
    423     @Override
    424     public void handleMessage(Message msg) {
    425         AsyncResult ar;
    426 
    427         byte data[];
    428 
    429         boolean isRecordLoadResponse = false;
    430 
    431         if (mDestroyed.get()) {
    432             loge("Received message " + msg +
    433                     "[" + msg.what + "] while being destroyed. Ignoring.");
    434             return;
    435         }
    436 
    437         try { switch (msg.what) {
    438             case EVENT_APP_READY:
    439                 onReady();
    440                 break;
    441 
    442             case EVENT_GET_DEVICE_IDENTITY_DONE:
    443                 log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
    444             break;
    445 
    446             /* IO events */
    447             case EVENT_GET_IMSI_DONE:
    448                 isRecordLoadResponse = true;
    449 
    450                 ar = (AsyncResult)msg.obj;
    451                 if (ar.exception != null) {
    452                     loge("Exception querying IMSI, Exception:" + ar.exception);
    453                     break;
    454                 }
    455 
    456                 mImsi = (String) ar.result;
    457 
    458                 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
    459                 // than 15 (and usually 15).
    460                 if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
    461                     loge("invalid IMSI " + mImsi);
    462                     mImsi = null;
    463                 }
    464 
    465                 log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");
    466 
    467                 String operatorNumeric = getRUIMOperatorNumeric();
    468                 if (operatorNumeric != null) {
    469                     if(operatorNumeric.length() <= 6){
    470                         MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
    471                     }
    472                 }
    473             break;
    474 
    475             case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
    476                 ar = (AsyncResult)msg.obj;
    477                 String localTemp[] = (String[])ar.result;
    478                 if (ar.exception != null) {
    479                     break;
    480                 }
    481 
    482                 mMyMobileNumber = localTemp[0];
    483                 mMin2Min1 = localTemp[3];
    484                 mPrlVersion = localTemp[4];
    485 
    486                 log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
    487 
    488             break;
    489 
    490             case EVENT_GET_ICCID_DONE:
    491                 isRecordLoadResponse = true;
    492 
    493                 ar = (AsyncResult)msg.obj;
    494                 data = (byte[])ar.result;
    495 
    496                 if (ar.exception != null) {
    497                     break;
    498                 }
    499 
    500                 iccid = IccUtils.bcdToString(data, 0, data.length);
    501 
    502                 log("iccid: " + iccid);
    503 
    504             break;
    505 
    506             case EVENT_UPDATE_DONE:
    507                 ar = (AsyncResult)msg.obj;
    508                 if (ar.exception != null) {
    509                     Log.i(LOG_TAG, "RuimRecords update failed", ar.exception);
    510                 }
    511             break;
    512 
    513             case EVENT_GET_ALL_SMS_DONE:
    514             case EVENT_MARK_SMS_READ_DONE:
    515             case EVENT_SMS_ON_RUIM:
    516             case EVENT_GET_SMS_DONE:
    517                 Log.w(LOG_TAG, "Event not supported: " + msg.what);
    518                 break;
    519 
    520             // TODO: probably EF_CST should be read instead
    521             case EVENT_GET_SST_DONE:
    522                 log("Event EVENT_GET_SST_DONE Received");
    523             break;
    524 
    525             case EVENT_RUIM_REFRESH:
    526                 isRecordLoadResponse = false;
    527                 ar = (AsyncResult)msg.obj;
    528                 if (ar.exception == null) {
    529                     handleRuimRefresh((IccRefreshResponse)ar.result);
    530                 }
    531                 break;
    532 
    533             default:
    534                 super.handleMessage(msg);   // IccRecords handles generic record load responses
    535 
    536         }}catch (RuntimeException exc) {
    537             // I don't want these exceptions to be fatal
    538             Log.w(LOG_TAG, "Exception parsing RUIM record", exc);
    539         } finally {
    540             // Count up record load responses even if they are fails
    541             if (isRecordLoadResponse) {
    542                 onRecordLoaded();
    543             }
    544         }
    545     }
    546 
    547     private String findBestLanguage(byte[] languages) {
    548         String bestMatch = null;
    549         String[] locales = mContext.getAssets().getLocales();
    550 
    551         if ((languages == null) || (locales == null)) return null;
    552 
    553         // Each 2-bytes consists of one language
    554         for (int i = 0; (i + 1) < languages.length; i += 2) {
    555             try {
    556                 String lang = new String(languages, i, 2, "ISO-8859-1");
    557                 for (int j = 0; j < locales.length; j++) {
    558                     if (locales[j] != null && locales[j].length() >= 2 &&
    559                         locales[j].substring(0, 2).equals(lang)) {
    560                         return lang;
    561                     }
    562                 }
    563                 if (bestMatch != null) break;
    564             } catch(java.io.UnsupportedEncodingException e) {
    565                 log ("Failed to parse SIM language records");
    566             }
    567         }
    568         // no match found. return null
    569         return null;
    570     }
    571 
    572     private void setLocaleFromCsim() {
    573         String prefLang = null;
    574         // check EFli then EFpl
    575         prefLang = findBestLanguage(mEFli);
    576 
    577         if (prefLang == null) {
    578             prefLang = findBestLanguage(mEFpl);
    579         }
    580 
    581         if (prefLang != null) {
    582             // check country code from SIM
    583             String imsi = getIMSI();
    584             String country = null;
    585             if (imsi != null) {
    586                 country = MccTable.countryCodeForMcc(
    587                                     Integer.parseInt(imsi.substring(0,3)));
    588             }
    589             log("Setting locale to " + prefLang + "_" + country);
    590             MccTable.setSystemLocale(mContext, prefLang, country);
    591         } else {
    592             log ("No suitable CSIM selected locale");
    593         }
    594     }
    595 
    596     @Override
    597     protected void onRecordLoaded() {
    598         // One record loaded successfully or failed, In either case
    599         // we need to update the recordsToLoad count
    600         recordsToLoad -= 1;
    601         if (DBG) log("onRecordLoaded " + recordsToLoad + " requested: " + recordsRequested);
    602 
    603         if (recordsToLoad == 0 && recordsRequested == true) {
    604             onAllRecordsLoaded();
    605         } else if (recordsToLoad < 0) {
    606             loge("recordsToLoad <0, programmer error suspected");
    607             recordsToLoad = 0;
    608         }
    609     }
    610 
    611     @Override
    612     protected void onAllRecordsLoaded() {
    613         if (DBG) log("record load complete");
    614 
    615         // Further records that can be inserted are Operator/OEM dependent
    616 
    617         String operator = getRUIMOperatorNumeric();
    618         log("RuimRecords: onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
    619                 operator + "'");
    620         SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
    621 
    622         if (mImsi != null) {
    623             SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
    624                     MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
    625         }
    626 
    627         setLocaleFromCsim();
    628         recordsLoadedRegistrants.notifyRegistrants(
    629             new AsyncResult(null, null, null));
    630     }
    631 
    632     @Override
    633     public void onReady() {
    634         fetchRuimRecords();
    635 
    636         mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
    637     }
    638 
    639 
    640     private void fetchRuimRecords() {
    641         recordsRequested = true;
    642 
    643         if (DBG) log("fetchRuimRecords " + recordsToLoad);
    644 
    645         mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
    646         recordsToLoad++;
    647 
    648         mFh.loadEFTransparent(EF_ICCID,
    649                 obtainMessage(EVENT_GET_ICCID_DONE));
    650         recordsToLoad++;
    651 
    652         mFh.loadEFTransparent(EF_PL,
    653                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
    654         recordsToLoad++;
    655 
    656         mFh.loadEFTransparent(EF_CSIM_LI,
    657                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
    658         recordsToLoad++;
    659 
    660         mFh.loadEFTransparent(EF_CSIM_SPN,
    661                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));
    662         recordsToLoad++;
    663 
    664         mFh.loadEFLinearFixed(EF_CSIM_MDN, 1,
    665                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded()));
    666         recordsToLoad++;
    667 
    668         mFh.loadEFTransparent(EF_CSIM_IMSIM,
    669                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded()));
    670         recordsToLoad++;
    671 
    672         mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
    673                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded()));
    674         recordsToLoad++;
    675 
    676         // Entire PRL could be huge. We are only interested in
    677         // the first 4 bytes of the record.
    678         mFh.loadEFTransparent(EF_CSIM_EPRL, 4,
    679                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded()));
    680         recordsToLoad++;
    681 
    682         if (DBG) log("fetchRuimRecords " + recordsToLoad + " requested: " + recordsRequested);
    683         // Further records that can be inserted are Operator/OEM dependent
    684     }
    685 
    686     /**
    687      * {@inheritDoc}
    688      *
    689      * No Display rule for RUIMs yet.
    690      */
    691     @Override
    692     public int getDisplayRule(String plmn) {
    693         // TODO together with spn
    694         return 0;
    695     }
    696 
    697     @Override
    698     public boolean isProvisioned() {
    699         // If UICC card has CSIM app, look for MDN and MIN field
    700         // to determine if the SIM is provisioned.  Otherwise,
    701         // consider the SIM is provisioned. (for case of ordinal
    702         // USIM only UICC.)
    703         // If PROPERTY_TEST_CSIM is defined, bypess provision check
    704         // and consider the SIM is provisioned.
    705         if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) {
    706             return true;
    707         }
    708 
    709         if (mParentApp == null) {
    710             return false;
    711         }
    712 
    713         if (mParentApp.getType() == AppType.APPTYPE_CSIM &&
    714             ((mMdn == null) || (mMin == null))) {
    715             return false;
    716         }
    717         return true;
    718     }
    719 
    720     @Override
    721     public void setVoiceMessageWaiting(int line, int countWaiting) {
    722         if (line != 1) {
    723             // only profile 1 is supported
    724             return;
    725         }
    726 
    727         // range check
    728         if (countWaiting < 0) {
    729             countWaiting = -1;
    730         } else if (countWaiting > 0xff) {
    731             // C.S0015-B v2, 4.5.12
    732             // range: 0-99
    733             countWaiting = 0xff;
    734         }
    735         countVoiceMessages = countWaiting;
    736 
    737         mRecordsEventsRegistrants.notifyResult(EVENT_MWI);
    738     }
    739 
    740     private void handleRuimRefresh(IccRefreshResponse refreshResponse) {
    741         if (refreshResponse == null) {
    742             if (DBG) log("handleRuimRefresh received without input");
    743             return;
    744         }
    745 
    746         if (refreshResponse.aid != null &&
    747                 !refreshResponse.aid.equals(mParentApp.getAid())) {
    748             // This is for different app. Ignore.
    749             return;
    750         }
    751 
    752         switch (refreshResponse.refreshResult) {
    753             case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE:
    754                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED");
    755                 adnCache.reset();
    756                 fetchRuimRecords();
    757                 break;
    758             case IccRefreshResponse.REFRESH_RESULT_INIT:
    759                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT");
    760                 // need to reload all files (that we care about)
    761                 fetchRuimRecords();
    762                 break;
    763             case IccRefreshResponse.REFRESH_RESULT_RESET:
    764                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET");
    765                 mCi.setRadioPower(false, null);
    766                 /* Note: no need to call setRadioPower(true).  Assuming the desired
    767                 * radio power state is still ON (as tracked by ServiceStateTracker),
    768                 * ServiceStateTracker will call setRadioPower when it receives the
    769                 * RADIO_STATE_CHANGED notification for the power off.  And if the
    770                 * desired power state has changed in the interim, we don't want to
    771                 * override it with an unconditional power on.
    772                 */
    773                 break;
    774             default:
    775                 // unknown refresh operation
    776                 if (DBG) log("handleRuimRefresh with unknown operation");
    777                 break;
    778         }
    779     }
    780 
    781     public String getMdn() {
    782         return mMdn;
    783     }
    784 
    785     public String getMin() {
    786         return mMin;
    787     }
    788 
    789     public String getSid() {
    790         return mHomeSystemId;
    791     }
    792 
    793     public String getNid() {
    794         return mHomeNetworkId;
    795     }
    796 
    797     public boolean getCsimSpnDisplayCondition() {
    798         return mCsimSpnDisplayCondition;
    799     }
    800     @Override
    801     protected void log(String s) {
    802         Log.d(LOG_TAG, "[RuimRecords] " + s);
    803     }
    804 
    805     @Override
    806     protected void loge(String s) {
    807         Log.e(LOG_TAG, "[RuimRecords] " + s);
    808     }
    809 }
    810