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