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 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
     20 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
     21 import android.os.AsyncResult;
     22 import android.os.Handler;
     23 import android.os.Message;
     24 import android.os.Registrant;
     25 import android.os.SystemProperties;
     26 import android.util.Log;
     27 
     28 import com.android.internal.telephony.AdnRecord;
     29 import com.android.internal.telephony.AdnRecordCache;
     30 import com.android.internal.telephony.AdnRecordLoader;
     31 import com.android.internal.telephony.CommandsInterface;
     32 import com.android.internal.telephony.TelephonyProperties;
     33 import com.android.internal.telephony.cdma.RuimCard;
     34 import com.android.internal.telephony.MccTable;
     35 
     36 // can't be used since VoiceMailConstants is not public
     37 //import com.android.internal.telephony.gsm.VoiceMailConstants;
     38 import com.android.internal.telephony.IccException;
     39 import com.android.internal.telephony.IccRecords;
     40 import com.android.internal.telephony.IccUtils;
     41 import com.android.internal.telephony.PhoneProxy;
     42 
     43 
     44 /**
     45  * {@hide}
     46  */
     47 public final class RuimRecords extends IccRecords {
     48     static final String LOG_TAG = "CDMA";
     49 
     50     private static final boolean DBG = true;
     51     private boolean  m_ota_commited=false;
     52 
     53     // ***** Instance Variables
     54 
     55     private String mImsi;
     56     private String mMyMobileNumber;
     57     private String mMin2Min1;
     58 
     59     private String mPrlVersion;
     60 
     61     // ***** Event Constants
     62 
     63     private static final int EVENT_RUIM_READY = 1;
     64     private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
     65     private static final int EVENT_GET_IMSI_DONE = 3;
     66     private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
     67     private static final int EVENT_GET_ICCID_DONE = 5;
     68     private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
     69     private static final int EVENT_UPDATE_DONE = 14;
     70     private static final int EVENT_GET_SST_DONE = 17;
     71     private static final int EVENT_GET_ALL_SMS_DONE = 18;
     72     private static final int EVENT_MARK_SMS_READ_DONE = 19;
     73 
     74     private static final int EVENT_SMS_ON_RUIM = 21;
     75     private static final int EVENT_GET_SMS_DONE = 22;
     76 
     77     private static final int EVENT_RUIM_REFRESH = 31;
     78 
     79 
     80     RuimRecords(CDMAPhone p) {
     81         super(p);
     82 
     83         adnCache = new AdnRecordCache(phone);
     84 
     85         recordsRequested = false;  // No load request is made till SIM ready
     86 
     87         // recordsToLoad is set to 0 because no requests are made yet
     88         recordsToLoad = 0;
     89 
     90 
     91         p.mCM.registerForRUIMReady(this, EVENT_RUIM_READY, null);
     92         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
     93         // NOTE the EVENT_SMS_ON_RUIM is not registered
     94         p.mCM.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
     95 
     96         // Start off by setting empty state
     97         onRadioOffOrNotAvailable();
     98 
     99     }
    100 
    101     @Override
    102     public void dispose() {
    103         //Unregister for all events
    104         phone.mCM.unregisterForRUIMReady(this);
    105         phone.mCM.unregisterForOffOrNotAvailable( this);
    106         phone.mCM.unregisterForIccRefresh(this);
    107     }
    108 
    109     @Override
    110     protected void finalize() {
    111         if(DBG) Log.d(LOG_TAG, "RuimRecords finalized");
    112     }
    113 
    114     @Override
    115     protected void onRadioOffOrNotAvailable() {
    116         countVoiceMessages = 0;
    117         mncLength = UNINITIALIZED;
    118         iccid = null;
    119 
    120         adnCache.reset();
    121 
    122         // Don't clean up PROPERTY_ICC_OPERATOR_ISO_COUNTRY and
    123         // PROPERTY_ICC_OPERATOR_NUMERIC here. Since not all CDMA
    124         // devices have RUIM, these properties should keep the original
    125         // values, e.g. build time settings, when there is no RUIM but
    126         // set new values when RUIM is available and loaded.
    127 
    128         // recordsRequested is set to false indicating that the SIM
    129         // read requests made so far are not valid. This is set to
    130         // true only when fresh set of read requests are made.
    131         recordsRequested = false;
    132     }
    133 
    134     public String getMdnNumber() {
    135         return mMyMobileNumber;
    136     }
    137 
    138     public String getCdmaMin() {
    139          return mMin2Min1;
    140     }
    141 
    142     /** Returns null if RUIM is not yet ready */
    143     public String getPrlVersion() {
    144         return mPrlVersion;
    145     }
    146 
    147     @Override
    148     public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){
    149         // In CDMA this is Operator/OEM dependent
    150         AsyncResult.forMessage((onComplete)).exception =
    151                 new IccException("setVoiceMailNumber not implemented");
    152         onComplete.sendToTarget();
    153         Log.e(LOG_TAG, "method setVoiceMailNumber is not implemented");
    154     }
    155 
    156     /**
    157      * Called by CCAT Service when REFRESH is received.
    158      * @param fileChanged indicates whether any files changed
    159      * @param fileList if non-null, a list of EF files that changed
    160      */
    161     @Override
    162     public void onRefresh(boolean fileChanged, int[] fileList) {
    163         if (fileChanged) {
    164             // A future optimization would be to inspect fileList and
    165             // only reload those files that we care about.  For now,
    166             // just re-fetch all RUIM records that we cache.
    167             fetchRuimRecords();
    168         }
    169     }
    170 
    171     /**
    172      * Returns the 5 or 6 digit MCC/MNC of the operator that
    173      *  provided the RUIM card. Returns null of RUIM is not yet ready
    174      */
    175     public String getRUIMOperatorNumeric() {
    176         if (mImsi == null) {
    177             return null;
    178         }
    179 
    180         if (mncLength != UNINITIALIZED && mncLength != UNKNOWN) {
    181             // Length = length of MCC + length of MNC
    182             // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
    183             return mImsi.substring(0, 3 + mncLength);
    184         }
    185 
    186         // Guess the MNC length based on the MCC if we don't
    187         // have a valid value in ef[ad]
    188 
    189         int mcc = Integer.parseInt(mImsi.substring(0,3));
    190         return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
    191     }
    192 
    193     @Override
    194     public void handleMessage(Message msg) {
    195         AsyncResult ar;
    196 
    197         byte data[];
    198 
    199         boolean isRecordLoadResponse = false;
    200 
    201         try { switch (msg.what) {
    202             case EVENT_RUIM_READY:
    203                 onRuimReady();
    204             break;
    205 
    206             case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
    207                 onRadioOffOrNotAvailable();
    208             break;
    209 
    210             case EVENT_GET_DEVICE_IDENTITY_DONE:
    211                 Log.d(LOG_TAG, "Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
    212             break;
    213 
    214             /* IO events */
    215             case EVENT_GET_IMSI_DONE:
    216                 isRecordLoadResponse = true;
    217 
    218                 ar = (AsyncResult)msg.obj;
    219                 if (ar.exception != null) {
    220                     Log.e(LOG_TAG, "Exception querying IMSI, Exception:" + ar.exception);
    221                     break;
    222                 }
    223 
    224                 mImsi = (String) ar.result;
    225 
    226                 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
    227                 // than 15 (and usually 15).
    228                 if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
    229                     Log.e(LOG_TAG, "invalid IMSI " + mImsi);
    230                     mImsi = null;
    231                 }
    232 
    233                 Log.d(LOG_TAG, "IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");
    234 
    235                 String operatorNumeric = getRUIMOperatorNumeric();
    236                 if (operatorNumeric != null) {
    237                     if(operatorNumeric.length() <= 6){
    238                         MccTable.updateMccMncConfiguration(phone, operatorNumeric);
    239                     }
    240                 }
    241             break;
    242 
    243             case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
    244                 ar = (AsyncResult)msg.obj;
    245                 String localTemp[] = (String[])ar.result;
    246                 if (ar.exception != null) {
    247                     break;
    248                 }
    249 
    250                 mMyMobileNumber = localTemp[0];
    251                 mMin2Min1 = localTemp[3];
    252                 mPrlVersion = localTemp[4];
    253 
    254                 Log.d(LOG_TAG, "MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
    255 
    256             break;
    257 
    258             case EVENT_GET_ICCID_DONE:
    259                 isRecordLoadResponse = true;
    260 
    261                 ar = (AsyncResult)msg.obj;
    262                 data = (byte[])ar.result;
    263 
    264                 if (ar.exception != null) {
    265                     break;
    266                 }
    267 
    268                 iccid = IccUtils.bcdToString(data, 0, data.length);
    269 
    270                 Log.d(LOG_TAG, "iccid: " + iccid);
    271 
    272             break;
    273 
    274             case EVENT_UPDATE_DONE:
    275                 ar = (AsyncResult)msg.obj;
    276                 if (ar.exception != null) {
    277                     Log.i(LOG_TAG, "RuimRecords update failed", ar.exception);
    278                 }
    279             break;
    280 
    281             case EVENT_GET_ALL_SMS_DONE:
    282             case EVENT_MARK_SMS_READ_DONE:
    283             case EVENT_SMS_ON_RUIM:
    284             case EVENT_GET_SMS_DONE:
    285                 Log.w(LOG_TAG, "Event not supported: " + msg.what);
    286                 break;
    287 
    288             // TODO: probably EF_CST should be read instead
    289             case EVENT_GET_SST_DONE:
    290                 Log.d(LOG_TAG, "Event EVENT_GET_SST_DONE Received");
    291             break;
    292 
    293             case EVENT_RUIM_REFRESH:
    294                 isRecordLoadResponse = false;
    295                 ar = (AsyncResult)msg.obj;
    296                 if (ar.exception == null) {
    297                     handleRuimRefresh((int[])(ar.result));
    298                 }
    299                 break;
    300 
    301         }}catch (RuntimeException exc) {
    302             // I don't want these exceptions to be fatal
    303             Log.w(LOG_TAG, "Exception parsing RUIM record", exc);
    304         } finally {
    305             // Count up record load responses even if they are fails
    306             if (isRecordLoadResponse) {
    307                 onRecordLoaded();
    308             }
    309         }
    310     }
    311 
    312     @Override
    313     protected void onRecordLoaded() {
    314         // One record loaded successfully or failed, In either case
    315         // we need to update the recordsToLoad count
    316         recordsToLoad -= 1;
    317 
    318         if (recordsToLoad == 0 && recordsRequested == true) {
    319             onAllRecordsLoaded();
    320         } else if (recordsToLoad < 0) {
    321             Log.e(LOG_TAG, "RuimRecords: recordsToLoad <0, programmer error suspected");
    322             recordsToLoad = 0;
    323         }
    324     }
    325 
    326     @Override
    327     protected void onAllRecordsLoaded() {
    328         Log.d(LOG_TAG, "RuimRecords: record load complete");
    329 
    330         // Further records that can be inserted are Operator/OEM dependent
    331 
    332         String operator = getRUIMOperatorNumeric();
    333         SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
    334 
    335         if (mImsi != null) {
    336             SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
    337                     MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
    338         }
    339         recordsLoadedRegistrants.notifyRegistrants(
    340             new AsyncResult(null, null, null));
    341         phone.mIccCard.broadcastIccStateChangedIntent(
    342                 RuimCard.INTENT_VALUE_ICC_LOADED, null);
    343     }
    344 
    345     private void onRuimReady() {
    346         /* broadcast intent ICC_READY here so that we can make sure
    347           READY is sent before IMSI ready
    348         */
    349 
    350         phone.mIccCard.broadcastIccStateChangedIntent(
    351                 RuimCard.INTENT_VALUE_ICC_READY, null);
    352 
    353         fetchRuimRecords();
    354 
    355         phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
    356 
    357     }
    358 
    359 
    360     private void fetchRuimRecords() {
    361         recordsRequested = true;
    362 
    363         Log.v(LOG_TAG, "RuimRecords:fetchRuimRecords " + recordsToLoad);
    364 
    365         phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
    366         recordsToLoad++;
    367 
    368         phone.getIccFileHandler().loadEFTransparent(EF_ICCID,
    369                 obtainMessage(EVENT_GET_ICCID_DONE));
    370         recordsToLoad++;
    371 
    372         // Further records that can be inserted are Operator/OEM dependent
    373     }
    374 
    375     /**
    376      * {@inheritDoc}
    377      *
    378      * No Display rule for RUIMs yet.
    379      */
    380     @Override
    381     public int getDisplayRule(String plmn) {
    382         // TODO together with spn
    383         return 0;
    384     }
    385 
    386     @Override
    387     public void setVoiceMessageWaiting(int line, int countWaiting) {
    388         if (line != 1) {
    389             // only profile 1 is supported
    390             return;
    391         }
    392 
    393         // range check
    394         if (countWaiting < 0) {
    395             countWaiting = -1;
    396         } else if (countWaiting > 0xff) {
    397             // C.S0015-B v2, 4.5.12
    398             // range: 0-99
    399             countWaiting = 0xff;
    400         }
    401         countVoiceMessages = countWaiting;
    402 
    403         ((CDMAPhone) phone).notifyMessageWaitingIndicator();
    404     }
    405 
    406     private void handleRuimRefresh(int[] result) {
    407         if (result == null || result.length == 0) {
    408             if (DBG) log("handleRuimRefresh without input");
    409             return;
    410         }
    411 
    412         switch ((result[0])) {
    413             case CommandsInterface.SIM_REFRESH_FILE_UPDATED:
    414                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED");
    415                 adnCache.reset();
    416                 fetchRuimRecords();
    417                 break;
    418             case CommandsInterface.SIM_REFRESH_INIT:
    419                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT");
    420                 // need to reload all files (that we care about)
    421                 fetchRuimRecords();
    422                 break;
    423             case CommandsInterface.SIM_REFRESH_RESET:
    424                 if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET");
    425                 phone.mCM.setRadioPower(false, null);
    426                 /* Note: no need to call setRadioPower(true).  Assuming the desired
    427                 * radio power state is still ON (as tracked by ServiceStateTracker),
    428                 * ServiceStateTracker will call setRadioPower when it receives the
    429                 * RADIO_STATE_CHANGED notification for the power off.  And if the
    430                 * desired power state has changed in the interim, we don't want to
    431                 * override it with an unconditional power on.
    432                 */
    433                 break;
    434             default:
    435                 // unknown refresh operation
    436                 if (DBG) log("handleRuimRefresh with unknown operation");
    437                 break;
    438         }
    439     }
    440 
    441     @Override
    442     protected void log(String s) {
    443         Log.d(LOG_TAG, "[RuimRecords] " + s);
    444     }
    445 
    446     @Override
    447     protected void loge(String s) {
    448         Log.e(LOG_TAG, "[RuimRecords] " + s);
    449     }
    450 }
    451