CDMAPhone.java revision f1099a9dfe0250a6b343de0646413db4034381de
1/*
2 * Copyright (C) 2006 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
17package com.android.internal.telephony.cdma;
18
19import android.app.ActivityManagerNative;
20import android.content.ContentValues;
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.database.SQLException;
25import android.net.Uri;
26import android.os.AsyncResult;
27import android.os.Handler;
28import android.os.Message;
29import android.os.PowerManager;
30import android.os.PowerManager.WakeLock;
31import android.os.Registrant;
32import android.os.RegistrantList;
33import android.os.SystemProperties;
34import android.os.UserHandle;
35import android.preference.PreferenceManager;
36import android.provider.Settings;
37import android.provider.Telephony;
38import android.telephony.CellLocation;
39import android.telephony.PhoneNumberUtils;
40import android.telephony.ServiceState;
41import android.telephony.SubscriptionManager;
42import android.telephony.cdma.CdmaCellLocation;
43import android.text.TextUtils;
44import android.telephony.Rlog;
45
46import com.android.ims.ImsManager;
47import com.android.internal.telephony.Call;
48import com.android.internal.telephony.CallStateException;
49import com.android.internal.telephony.CallTracker;
50import com.android.internal.telephony.CommandException;
51import com.android.internal.telephony.CommandsInterface;
52import com.android.internal.telephony.Connection;
53import com.android.internal.telephony.IccPhoneBookInterfaceManager;
54import com.android.internal.telephony.MccTable;
55import com.android.internal.telephony.MmiCode;
56import com.android.internal.telephony.PhoneBase;
57import com.android.internal.telephony.PhoneConstants;
58import com.android.internal.telephony.PhoneNotifier;
59import com.android.internal.telephony.PhoneProxy;
60import com.android.internal.telephony.PhoneSubInfo;
61import com.android.internal.telephony.ServiceStateTracker;
62import com.android.internal.telephony.SubscriptionController;
63import com.android.internal.telephony.TelephonyIntents;
64import com.android.internal.telephony.TelephonyProperties;
65import com.android.internal.telephony.UUSInfo;
66import com.android.internal.telephony.dataconnection.DcTracker;
67import com.android.internal.telephony.imsphone.ImsPhone;
68import com.android.internal.telephony.uicc.IccException;
69import com.android.internal.telephony.uicc.IccRecords;
70import com.android.internal.telephony.uicc.RuimRecords;
71import com.android.internal.telephony.uicc.UiccCard;
72import com.android.internal.telephony.uicc.UiccCardApplication;
73import com.android.internal.telephony.uicc.UiccController;
74
75import java.io.FileDescriptor;
76import java.io.PrintWriter;
77import java.util.ArrayList;
78import java.util.List;
79import java.util.regex.Matcher;
80import java.util.regex.Pattern;
81
82import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
83import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
84import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
85
86/**
87 * {@hide}
88 */
89public class CDMAPhone extends PhoneBase {
90    static final String LOG_TAG = "CDMAPhone";
91    private static final boolean DBG = true;
92    private static final boolean VDBG = false; /* STOP SHIP if true */
93
94    // Default Emergency Callback Mode exit timer
95    private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
96
97    private static final String VM_NUMBER_CDMA = "vm_number_key_cdma";
98    private String mVmNumber = null;
99
100    static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
101    static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
102
103    // Instance Variables
104    CdmaCallTracker mCT;
105    CdmaServiceStateTracker mSST;
106    CdmaSubscriptionSourceManager mCdmaSSM;
107    ArrayList <CdmaMmiCode> mPendingMmis = new ArrayList<CdmaMmiCode>();
108    RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager;
109    int mCdmaSubscriptionSource =
110            CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
111    PhoneSubInfo mSubInfo;
112    EriManager mEriManager;
113    WakeLock mWakeLock;
114
115    // mEriFileLoadedRegistrants are informed after the ERI text has been loaded
116    private final RegistrantList mEriFileLoadedRegistrants = new RegistrantList();
117
118    // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started
119    private final RegistrantList mEcmTimerResetRegistrants = new RegistrantList();
120
121    // mEcmExitRespRegistrant is informed after the phone has been exited
122    //the emergency callback mode
123    //keep track of if phone is in emergency callback mode
124    protected boolean mIsPhoneInEcmState;
125    private Registrant mEcmExitRespRegistrant;
126    protected String mImei;
127    protected String mImeiSv;
128    private String mEsn;
129    private String mMeid;
130    // string to define how the carrier specifies its own ota sp number
131    protected String mCarrierOtaSpNumSchema;
132
133    // A runnable which is used to automatically exit from Ecm after a period of time.
134    private Runnable mExitEcmRunnable = new Runnable() {
135        @Override
136        public void run() {
137            exitEmergencyCallbackMode();
138        }
139    };
140
141    Registrant mPostDialHandler;
142
143    static String PROPERTY_CDMA_HOME_OPERATOR_NUMERIC = "ro.cdma.home.operator.numeric";
144
145    public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
146            int phoneId) {
147        super("CDMA", notifier, context, ci, false, phoneId);
148        initSstIcc();
149        init(context, notifier);
150    }
151
152    protected void initSstIcc() {
153        mSST = new CdmaServiceStateTracker(this);
154    }
155
156    protected void init(Context context, PhoneNotifier notifier) {
157        mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
158        mCT = new CdmaCallTracker(this);
159        mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context, mCi, this,
160                EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
161        mDcTracker = new DcTracker(this);
162        mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
163        mSubInfo = new PhoneSubInfo(this);
164        mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
165
166        mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
167        mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
168        mCi.registerForOn(this, EVENT_RADIO_ON, null);
169        mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
170        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
171        mCi.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null);
172        mCi.registerForExitEmergencyCallbackMode(this, EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE,
173                null);
174
175        PowerManager pm
176            = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
177        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,LOG_TAG);
178
179        //Change the system setting
180        SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
181                Integer.toString(PhoneConstants.PHONE_TYPE_CDMA));
182
183        // This is needed to handle phone process crashes
184        String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
185        mIsPhoneInEcmState = inEcm.equals("true");
186        if (mIsPhoneInEcmState) {
187            // Send a message which will invoke handleExitEmergencyCallbackMode
188            mCi.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
189        }
190
191        // get the string that specifies the carrier OTA Sp number
192        mCarrierOtaSpNumSchema = SystemProperties.get(
193                TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA,"");
194
195        // Sets operator properties by retrieving from build-time system property
196        String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha");
197        String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC);
198        log("init: operatorAlpha='" + operatorAlpha
199                + "' operatorNumeric='" + operatorNumeric + "'");
200        if (mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP) == null) {
201            log("init: APP_FAM_3GPP == NULL");
202            if (!TextUtils.isEmpty(operatorAlpha)) {
203                log("init: set 'gsm.sim.operator.alpha' to operator='" + operatorAlpha + "'");
204                setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, operatorAlpha);
205            }
206            if (!TextUtils.isEmpty(operatorNumeric)) {
207                log("init: set 'gsm.sim.operator.numeric' to operator='" + operatorNumeric + "'");
208                log("update icc_operator_numeric=" + operatorNumeric);
209                setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operatorNumeric);
210
211                SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId());
212            }
213            setIsoCountryProperty(operatorNumeric);
214        }
215
216        // Sets current entry in the telephony carrier table
217        updateCurrentCarrierInProvider(operatorNumeric);
218    }
219
220    @Override
221    public void dispose() {
222        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
223            super.dispose();
224            log("dispose");
225
226            //Unregister from all former registered events
227            unregisterForRuimRecordEvents();
228            mCi.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
229            mCi.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
230            mCi.unregisterForOn(this); //EVENT_RADIO_ON
231            mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK
232            mCi.unSetOnSuppServiceNotification(this);
233            mCi.unregisterForExitEmergencyCallbackMode(this);
234            removeCallbacks(mExitEcmRunnable);
235
236            mPendingMmis.clear();
237
238            //Force all referenced classes to unregister their former registered events
239            mCT.dispose();
240            mDcTracker.dispose();
241            mSST.dispose();
242            mCdmaSSM.dispose(this);
243            mRuimPhoneBookInterfaceManager.dispose();
244            mSubInfo.dispose();
245            mEriManager.dispose();
246        }
247    }
248
249    @Override
250    public void removeReferences() {
251        log("removeReferences");
252        mRuimPhoneBookInterfaceManager = null;
253        mSubInfo = null;
254        mCT = null;
255        mSST = null;
256        mEriManager = null;
257        mExitEcmRunnable = null;
258
259        super.removeReferences();
260    }
261
262    @Override
263    protected void finalize() {
264        if(DBG) Rlog.d(LOG_TAG, "CDMAPhone finalized");
265        if (mWakeLock.isHeld()) {
266            Rlog.e(LOG_TAG, "UNEXPECTED; mWakeLock is held when finalizing.");
267            mWakeLock.release();
268        }
269    }
270
271    @Override
272    public ServiceState getServiceState() {
273        if (mSST == null || mSST.mSS.getState() != ServiceState.STATE_IN_SERVICE) {
274            if (mImsPhone != null) {
275                return ServiceState.mergeServiceStates(
276                        (mSST == null) ? new ServiceState() : mSST.mSS,
277                        mImsPhone.getServiceState());
278            }
279        }
280
281        if (mSST != null) {
282            return mSST.mSS;
283        } else {
284            // avoid potential NPE in EmergencyCallHelper during Phone switch
285            return new ServiceState();
286        }
287    }
288
289
290    @Override
291    public CallTracker getCallTracker() {
292        return mCT;
293    }
294
295    @Override
296    public PhoneConstants.State getState() {
297        return mCT.mState;
298    }
299
300    @Override
301    public ServiceStateTracker getServiceStateTracker() {
302        return mSST;
303    }
304
305    @Override
306    public int getPhoneType() {
307        return PhoneConstants.PHONE_TYPE_CDMA;
308    }
309
310    @Override
311    public boolean canTransfer() {
312        Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA");
313        return false;
314    }
315
316    @Override
317    public Call getRingingCall() {
318        ImsPhone imPhone = mImsPhone;
319        if ( mCT.mRingingCall != null && mCT.mRingingCall.isRinging() ) {
320            return mCT.mRingingCall;
321        } else if ( imPhone != null ) {
322            return imPhone.getRingingCall();
323        }
324        return mCT.mRingingCall;
325    }
326
327    @Override
328    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
329       if (mImsPhone != null) {
330           mImsPhone.setUiTTYMode(uiTtyMode, onComplete);
331       }
332    }
333
334    @Override
335    public void setMute(boolean muted) {
336        mCT.setMute(muted);
337    }
338
339    @Override
340    public boolean getMute() {
341        return mCT.getMute();
342    }
343
344    @Override
345    public void conference() {
346        if (mImsPhone != null && mImsPhone.canConference()) {
347            log("conference() - delegated to IMS phone");
348            mImsPhone.conference();
349            return;
350        }
351        // three way calls in CDMA will be handled by feature codes
352        Rlog.e(LOG_TAG, "conference: not possible in CDMA");
353    }
354
355    @Override
356    public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
357        mCi.setPreferredVoicePrivacy(enable, onComplete);
358    }
359
360    @Override
361    public void getEnhancedVoicePrivacy(Message onComplete) {
362        mCi.getPreferredVoicePrivacy(onComplete);
363    }
364
365    @Override
366    public void clearDisconnected() {
367        mCT.clearDisconnected();
368    }
369
370    @Override
371    public DataActivityState getDataActivityState() {
372        DataActivityState ret = DataActivityState.NONE;
373
374        if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
375
376            switch (mDcTracker.getActivity()) {
377                case DATAIN:
378                    ret = DataActivityState.DATAIN;
379                break;
380
381                case DATAOUT:
382                    ret = DataActivityState.DATAOUT;
383                break;
384
385                case DATAINANDOUT:
386                    ret = DataActivityState.DATAINANDOUT;
387                break;
388
389                case DORMANT:
390                    ret = DataActivityState.DORMANT;
391                break;
392
393                default:
394                    ret = DataActivityState.NONE;
395                break;
396            }
397        }
398        return ret;
399    }
400
401    @Override
402    public Connection
403    dial (String dialString, int videoState) throws CallStateException {
404        ImsPhone imsPhone = mImsPhone;
405
406        boolean imsUseEnabled =
407                ImsManager.isVolteEnabledByPlatform(mContext) &&
408                ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
409                ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext);
410        if (!imsUseEnabled) {
411            Rlog.w(LOG_TAG, "IMS is disabled: forced to CS");
412        }
413
414        if (DBG) {
415            Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", imsPhone=" + imsPhone
416                    + ", imsPhone.isVolteEnabled()="
417                    + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
418                    + ", imsPhone.getServiceState().getState()="
419                    + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
420        }
421
422        if (imsUseEnabled && imsPhone != null && imsPhone.isVolteEnabled()
423                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE
424                && !PhoneNumberUtils.isEmergencyNumber(dialString))
425                || (PhoneNumberUtils.isEmergencyNumber(dialString)
426                && mContext.getResources().getBoolean(
427                        com.android.internal.R.bool.useImsAlwaysForEmergencyCall))) ) {
428            try {
429                if (DBG) Rlog.d(LOG_TAG, "Trying IMS PS call");
430                return imsPhone.dial(dialString, videoState);
431            } catch (CallStateException e) {
432                if (DBG) Rlog.d(LOG_TAG, "IMS PS call exception " + e +
433                        "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
434                if (!ImsPhone.CS_FALLBACK.equals(e.getMessage())) {
435                    CallStateException ce = new CallStateException(e.getMessage());
436                    ce.setStackTrace(e.getStackTrace());
437                    throw ce;
438                }
439            }
440        }
441
442        if (DBG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call");
443        return dialInternal(dialString, null, videoState);
444    }
445
446
447    @Override
448    protected Connection
449    dialInternal (String dialString, UUSInfo uusInfo,
450            int videoState) throws CallStateException {
451        // Need to make sure dialString gets parsed properly
452        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
453        return mCT.dial(newDialString);
454    }
455
456    @Override
457    public Connection dial(String dialString, UUSInfo uusInfo, int videoState)
458            throws CallStateException {
459        throw new CallStateException("Sending UUS information NOT supported in CDMA!");
460    }
461
462    @Override
463    public List<? extends MmiCode>
464    getPendingMmiCodes() {
465        return mPendingMmis;
466    }
467
468    @Override
469    public void registerForSuppServiceNotification(
470            Handler h, int what, Object obj) {
471        Rlog.e(LOG_TAG, "method registerForSuppServiceNotification is NOT supported in CDMA!");
472    }
473
474    @Override
475    public CdmaCall getBackgroundCall() {
476        return mCT.mBackgroundCall;
477    }
478
479    @Override
480    public boolean handleInCallMmiCommands(String dialString) {
481        Rlog.e(LOG_TAG, "method handleInCallMmiCommands is NOT supported in CDMA!");
482        return false;
483    }
484
485    boolean isInCall() {
486        CdmaCall.State foregroundCallState = getForegroundCall().getState();
487        CdmaCall.State backgroundCallState = getBackgroundCall().getState();
488        CdmaCall.State ringingCallState = getRingingCall().getState();
489
490        return (foregroundCallState.isAlive() || backgroundCallState.isAlive() || ringingCallState
491                .isAlive());
492    }
493
494    @Override
495    public void unregisterForSuppServiceNotification(Handler h) {
496        Rlog.e(LOG_TAG, "method unregisterForSuppServiceNotification is NOT supported in CDMA!");
497    }
498
499    @Override
500    public void
501    acceptCall(int videoState) throws CallStateException {
502        ImsPhone imsPhone = mImsPhone;
503        if ( imsPhone != null && imsPhone.getRingingCall().isRinging() ) {
504            imsPhone.acceptCall(videoState);
505        } else {
506            mCT.acceptCall();
507        }
508    }
509
510    @Override
511    public void
512    rejectCall() throws CallStateException {
513        mCT.rejectCall();
514    }
515
516    @Override
517    public void
518    switchHoldingAndActive() throws CallStateException {
519        mCT.switchWaitingOrHoldingAndActive();
520    }
521
522    @Override
523    public String getIccSerialNumber() {
524        IccRecords r = mIccRecords.get();
525        if (r == null) {
526            // to get ICCID form SIMRecords because it is on MF.
527            r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP);
528        }
529        return (r != null) ? r.getIccId() : null;
530    }
531
532    @Override
533    public String getLine1Number() {
534        return mSST.getMdnNumber();
535    }
536
537    @Override
538    public String getCdmaPrlVersion(){
539        return mSST.getPrlVersion();
540    }
541
542    @Override
543    public String getCdmaMin() {
544        return mSST.getCdmaMin();
545    }
546
547    @Override
548    public boolean isMinInfoReady() {
549        return mSST.isMinInfoReady();
550    }
551
552    @Override
553    public void getCallWaiting(Message onComplete) {
554        mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
555    }
556
557    @Override
558    public void
559    setRadioPower(boolean power) {
560        mSST.setRadioPower(power);
561    }
562
563    @Override
564    public String getEsn() {
565        return mEsn;
566    }
567
568    @Override
569    public String getMeid() {
570        return mMeid;
571    }
572
573    @Override
574    public String getNai() {
575        IccRecords r = mIccRecords.get();
576        return (r != null) ? r.getNAI() : null;
577    }
578
579    //returns MEID or ESN in CDMA
580    @Override
581    public String getDeviceId() {
582        String id = getMeid();
583        if ((id == null) || id.matches("^0*$")) {
584            Rlog.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN");
585            id = getEsn();
586        }
587        return id;
588    }
589
590    @Override
591    public String getDeviceSvn() {
592        Rlog.d(LOG_TAG, "getDeviceSvn(): return 0");
593        return "0";
594    }
595
596    @Override
597    public String getSubscriberId() {
598        return mSST.getImsi();
599    }
600
601    @Override
602    public String getGroupIdLevel1() {
603        Rlog.e(LOG_TAG, "GID1 is not available in CDMA");
604        return null;
605    }
606
607    @Override
608    public String getImei() {
609        Rlog.e(LOG_TAG, "getImei() called for CDMAPhone");
610        return mImei;
611    }
612
613    @Override
614    public boolean canConference() {
615        if (mImsPhone != null && mImsPhone.canConference()) {
616            return true;
617        }
618        Rlog.e(LOG_TAG, "canConference: not possible in CDMA");
619        return false;
620    }
621
622    @Override
623    public CellLocation getCellLocation() {
624        CdmaCellLocation loc = mSST.mCellLoc;
625
626        int mode = Settings.Secure.getInt(getContext().getContentResolver(),
627                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
628        if (mode == Settings.Secure.LOCATION_MODE_OFF) {
629            // clear lat/long values for location privacy
630            CdmaCellLocation privateLoc = new CdmaCellLocation();
631            privateLoc.setCellLocationData(loc.getBaseStationId(),
632                    CdmaCellLocation.INVALID_LAT_LONG,
633                    CdmaCellLocation.INVALID_LAT_LONG,
634                    loc.getSystemId(), loc.getNetworkId());
635            loc = privateLoc;
636        }
637        return loc;
638    }
639
640    @Override
641    public CdmaCall getForegroundCall() {
642        return mCT.mForegroundCall;
643    }
644
645    @Override
646    public void setOnPostDialCharacter(Handler h, int what, Object obj) {
647        mPostDialHandler = new Registrant(h, what, obj);
648    }
649
650    @Override
651    public boolean handlePinMmi(String dialString) {
652        CdmaMmiCode mmi = CdmaMmiCode.newFromDialString(dialString, this, mUiccApplication.get());
653
654        if (mmi == null) {
655            Rlog.e(LOG_TAG, "Mmi is NULL!");
656            return false;
657        } else if (mmi.isPinPukCommand()) {
658            mPendingMmis.add(mmi);
659            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
660            mmi.processCode();
661            return true;
662        }
663        Rlog.e(LOG_TAG, "Unrecognized mmi!");
664        return false;
665    }
666
667    /**
668     * Removes the given MMI from the pending list and notifies registrants that
669     * it is complete.
670     *
671     * @param mmi MMI that is done
672     */
673    void onMMIDone(CdmaMmiCode mmi) {
674        /*
675         * Only notify complete if it's on the pending list. Otherwise, it's
676         * already been handled (eg, previously canceled).
677         */
678        if (mPendingMmis.remove(mmi)) {
679            mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
680        }
681    }
682
683    @Override
684    public void setLine1Number(String alphaTag, String number, Message onComplete) {
685        Rlog.e(LOG_TAG, "setLine1Number: not possible in CDMA");
686    }
687
688    @Override
689    public void setCallWaiting(boolean enable, Message onComplete) {
690        Rlog.e(LOG_TAG, "method setCallWaiting is NOT supported in CDMA!");
691    }
692
693    @Override
694    public void updateServiceLocation() {
695        mSST.enableSingleLocationUpdate();
696    }
697
698    @Override
699    public void setDataRoamingEnabled(boolean enable) {
700        mDcTracker.setDataOnRoamingEnabled(enable);
701    }
702
703    @Override
704    public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
705        mCi.registerForCdmaOtaProvision(h, what, obj);
706    }
707
708    @Override
709    public void unregisterForCdmaOtaStatusChange(Handler h) {
710        mCi.unregisterForCdmaOtaProvision(h);
711    }
712
713    @Override
714    public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
715        mSST.registerForSubscriptionInfoReady(h, what, obj);
716    }
717
718    @Override
719    public void unregisterForSubscriptionInfoReady(Handler h) {
720        mSST.unregisterForSubscriptionInfoReady(h);
721    }
722
723    @Override
724    public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
725        mEcmExitRespRegistrant = new Registrant (h, what, obj);
726    }
727
728    @Override
729    public void unsetOnEcbModeExitResponse(Handler h) {
730        mEcmExitRespRegistrant.clear();
731    }
732
733    @Override
734    public void registerForCallWaiting(Handler h, int what, Object obj) {
735        mCT.registerForCallWaiting(h, what, obj);
736    }
737
738    @Override
739    public void unregisterForCallWaiting(Handler h) {
740        mCT.unregisterForCallWaiting(h);
741    }
742
743    @Override
744    public void
745    getNeighboringCids(Message response) {
746        /*
747         * This is currently not implemented.  At least as of June
748         * 2009, there is no neighbor cell information available for
749         * CDMA because some party is resisting making this
750         * information readily available.  Consequently, calling this
751         * function can have no useful effect.  This situation may
752         * (and hopefully will) change in the future.
753         */
754        if (response != null) {
755            CommandException ce = new CommandException(
756                    CommandException.Error.REQUEST_NOT_SUPPORTED);
757            AsyncResult.forMessage(response).exception = ce;
758            response.sendToTarget();
759        }
760    }
761
762    @Override
763    public PhoneConstants.DataState getDataConnectionState(String apnType) {
764        PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED;
765
766        if (mSST == null) {
767             // Radio Technology Change is ongoning, dispose() and removeReferences() have
768             // already been called
769
770             ret = PhoneConstants.DataState.DISCONNECTED;
771        } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
772            // If we're out of service, open TCP sockets may still work
773            // but no data will flow
774            ret = PhoneConstants.DataState.DISCONNECTED;
775        } else if (mDcTracker.isApnTypeEnabled(apnType) == false ||
776                mDcTracker.isApnTypeActive(apnType) == false) {
777            ret = PhoneConstants.DataState.DISCONNECTED;
778        } else {
779            switch (mDcTracker.getState(apnType)) {
780                case RETRYING:
781                case FAILED:
782                case IDLE:
783                    ret = PhoneConstants.DataState.DISCONNECTED;
784                break;
785
786                case CONNECTED:
787                case DISCONNECTING:
788                    if ( mCT.mState != PhoneConstants.State.IDLE
789                            && !mSST.isConcurrentVoiceAndDataAllowed()) {
790                        ret = PhoneConstants.DataState.SUSPENDED;
791                    } else {
792                        ret = PhoneConstants.DataState.CONNECTED;
793                    }
794                break;
795
796                case CONNECTING:
797                case SCANNING:
798                    ret = PhoneConstants.DataState.CONNECTING;
799                break;
800            }
801        }
802
803        log("getDataConnectionState apnType=" + apnType + " ret=" + ret);
804        return ret;
805    }
806
807    @Override
808    public void sendUssdResponse(String ussdMessge) {
809        Rlog.e(LOG_TAG, "sendUssdResponse: not possible in CDMA");
810    }
811
812    @Override
813    public void sendDtmf(char c) {
814        if (!PhoneNumberUtils.is12Key(c)) {
815            Rlog.e(LOG_TAG,
816                    "sendDtmf called with invalid character '" + c + "'");
817        } else {
818            if (mCT.mState ==  PhoneConstants.State.OFFHOOK) {
819                mCi.sendDtmf(c, null);
820            }
821        }
822    }
823
824    @Override
825    public void startDtmf(char c) {
826        if (!PhoneNumberUtils.is12Key(c)) {
827            Rlog.e(LOG_TAG,
828                    "startDtmf called with invalid character '" + c + "'");
829        } else {
830            mCi.startDtmf(c, null);
831        }
832    }
833
834    @Override
835    public void stopDtmf() {
836        mCi.stopDtmf(null);
837    }
838
839    @Override
840    public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
841        boolean check = true;
842        for (int itr = 0;itr < dtmfString.length(); itr++) {
843            if (!PhoneNumberUtils.is12Key(dtmfString.charAt(itr))) {
844                Rlog.e(LOG_TAG,
845                        "sendDtmf called with invalid character '" + dtmfString.charAt(itr)+ "'");
846                check = false;
847                break;
848            }
849        }
850        if ((mCT.mState ==  PhoneConstants.State.OFFHOOK)&&(check)) {
851            mCi.sendBurstDtmf(dtmfString, on, off, onComplete);
852        }
853     }
854
855    @Override
856    public void getAvailableNetworks(Message response) {
857        Rlog.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA");
858    }
859
860    @Override
861    public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) {
862        Rlog.e(LOG_TAG, "setOutgoingCallerIdDisplay: not possible in CDMA");
863    }
864
865    @Override
866    public void enableLocationUpdates() {
867        mSST.enableLocationUpdates();
868    }
869
870    @Override
871    public void disableLocationUpdates() {
872        mSST.disableLocationUpdates();
873    }
874
875    @Override
876    public void getDataCallList(Message response) {
877        mCi.getDataCallList(response);
878    }
879
880    @Override
881    public boolean getDataRoamingEnabled() {
882        return mDcTracker.getDataOnRoamingEnabled();
883    }
884
885    @Override
886    public void setDataEnabled(boolean enable) {
887        mDcTracker.setDataEnabled(enable);
888    }
889
890    @Override
891    public boolean getDataEnabled() {
892        return mDcTracker.getDataEnabled();
893    }
894
895    @Override
896    public void setVoiceMailNumber(String alphaTag,
897                                   String voiceMailNumber,
898                                   Message onComplete) {
899        Message resp;
900        mVmNumber = voiceMailNumber;
901        resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
902        IccRecords r = mIccRecords.get();
903        if (r != null) {
904            r.setVoiceMailNumber(alphaTag, mVmNumber, resp);
905        }
906    }
907
908    @Override
909    public String getVoiceMailNumber() {
910        String number = null;
911        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
912        number = sp.getString(VM_NUMBER_CDMA + getPhoneId(), null);
913        if (TextUtils.isEmpty(number)) {
914            String[] listArray = getContext().getResources()
915                .getStringArray(com.android.internal.R.array.config_default_vm_number);
916            if (listArray != null && listArray.length > 0) {
917                for (int i=0; i<listArray.length; i++) {
918                    if (!TextUtils.isEmpty(listArray[i])) {
919                        String[] defaultVMNumberArray = listArray[i].split(";");
920                        if (defaultVMNumberArray != null && defaultVMNumberArray.length > 0) {
921                            if (defaultVMNumberArray.length == 1) {
922                                number = defaultVMNumberArray[0];
923                            } else if (defaultVMNumberArray.length == 2 &&
924                                    !TextUtils.isEmpty(defaultVMNumberArray[1]) &&
925                                    defaultVMNumberArray[1].equalsIgnoreCase(getGroupIdLevel1())) {
926                                number = defaultVMNumberArray[0];
927                                break;
928                            }
929                        }
930                    }
931                }
932            }
933        }
934        if (TextUtils.isEmpty(number)) {
935            // Read platform settings for dynamic voicemail number
936            if (getContext().getResources().getBoolean(com.android.internal
937                    .R.bool.config_telephony_use_own_number_for_voicemail)) {
938                number = getLine1Number();
939            } else {
940                number = "*86";
941            }
942        }
943        return number;
944    }
945
946    // pending voice mail count updated after phone creation
947    private void updateVoiceMail() {
948        setVoiceMessageCount(getStoredVoiceMessageCount());
949    }
950
951    @Override
952    public String getVoiceMailAlphaTag() {
953        // TODO: Where can we get this value has to be clarified with QC.
954        String ret = "";//TODO: Remove = "", if we know where to get this value.
955
956        //ret = mSIMRecords.getVoiceMailAlphaTag();
957
958        if (ret == null || ret.length() == 0) {
959            return mContext.getText(
960                com.android.internal.R.string.defaultVoiceMailAlphaTag).toString();
961        }
962
963        return ret;
964    }
965
966    @Override
967    public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
968        Rlog.e(LOG_TAG, "getCallForwardingOption: not possible in CDMA");
969    }
970
971    @Override
972    public void setCallForwardingOption(int commandInterfaceCFAction,
973            int commandInterfaceCFReason,
974            String dialingNumber,
975            int timerSeconds,
976            Message onComplete) {
977        Rlog.e(LOG_TAG, "setCallForwardingOption: not possible in CDMA");
978    }
979
980    @Override
981    public void
982    getOutgoingCallerIdDisplay(Message onComplete) {
983        Rlog.e(LOG_TAG, "getOutgoingCallerIdDisplay: not possible in CDMA");
984    }
985
986    @Override
987    public boolean
988    getCallForwardingIndicator() {
989        Rlog.e(LOG_TAG, "getCallForwardingIndicator: not possible in CDMA");
990        return false;
991    }
992
993    @Override
994    public void explicitCallTransfer() {
995        Rlog.e(LOG_TAG, "explicitCallTransfer: not possible in CDMA");
996    }
997
998    @Override
999    public String getLine1AlphaTag() {
1000        Rlog.e(LOG_TAG, "getLine1AlphaTag: not possible in CDMA");
1001        return null;
1002    }
1003
1004    /**
1005     * Notify any interested party of a Phone state change
1006     * {@link com.android.internal.telephony.PhoneConstants.State}
1007     */
1008    /*package*/ void notifyPhoneStateChanged() {
1009        mNotifier.notifyPhoneState(this);
1010    }
1011
1012    /**
1013     * Notify registrants of a change in the call state. This notifies changes in
1014     * {@link com.android.internal.telephony.Call.State}. Use this when changes
1015     * in the precise call state are needed, else use notifyPhoneStateChanged.
1016     */
1017    /*package*/ void notifyPreciseCallStateChanged() {
1018        /* we'd love it if this was package-scoped*/
1019        super.notifyPreciseCallStateChangedP();
1020    }
1021
1022     void notifyServiceStateChanged(ServiceState ss) {
1023         super.notifyServiceStateChangedP(ss);
1024     }
1025
1026     void notifyLocationChanged() {
1027         mNotifier.notifyCellLocation(this);
1028     }
1029
1030    public void notifyNewRingingConnection(Connection c) {
1031        super.notifyNewRingingConnectionP(c);
1032    }
1033
1034    /*package*/ void notifyDisconnect(Connection cn) {
1035        mDisconnectRegistrants.notifyResult(cn);
1036
1037        mNotifier.notifyDisconnectCause(cn.getDisconnectCause(), cn.getPreciseDisconnectCause());
1038    }
1039
1040    void notifyUnknownConnection(Connection connection) {
1041        mUnknownConnectionRegistrants.notifyResult(connection);
1042    }
1043
1044    @Override
1045    public boolean isInEmergencyCall() {
1046        return mCT.isInEmergencyCall();
1047    }
1048
1049    @Override
1050    public boolean isInEcm() {
1051        return mIsPhoneInEcmState;
1052    }
1053
1054    void sendEmergencyCallbackModeChange(){
1055        //Send an Intent
1056        Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
1057        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
1058        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
1059        ActivityManagerNative.broadcastStickyIntent(intent,null,UserHandle.USER_ALL);
1060        if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange");
1061    }
1062
1063    @Override
1064    public void exitEmergencyCallbackMode() {
1065        if (mWakeLock.isHeld()) {
1066            mWakeLock.release();
1067        }
1068        // Send a message which will invoke handleExitEmergencyCallbackMode
1069        mCi.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
1070    }
1071
1072    private void handleEnterEmergencyCallbackMode(Message msg) {
1073        if (DBG) {
1074            Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
1075                    + mIsPhoneInEcmState);
1076        }
1077        // if phone is not in Ecm mode, and it's changed to Ecm mode
1078        if (mIsPhoneInEcmState == false) {
1079            mIsPhoneInEcmState = true;
1080            // notify change
1081            sendEmergencyCallbackModeChange();
1082            setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true");
1083
1084            // Post this runnable so we will automatically exit
1085            // if no one invokes exitEmergencyCallbackMode() directly.
1086            long delayInMillis = SystemProperties.getLong(
1087                    TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
1088            postDelayed(mExitEcmRunnable, delayInMillis);
1089            // We don't want to go to sleep while in Ecm
1090            mWakeLock.acquire();
1091        }
1092    }
1093
1094    private void handleExitEmergencyCallbackMode(Message msg) {
1095        AsyncResult ar = (AsyncResult)msg.obj;
1096        if (DBG) {
1097            Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , mIsPhoneInEcmState "
1098                    + ar.exception + mIsPhoneInEcmState);
1099        }
1100        // Remove pending exit Ecm runnable, if any
1101        removeCallbacks(mExitEcmRunnable);
1102
1103        if (mEcmExitRespRegistrant != null) {
1104            mEcmExitRespRegistrant.notifyRegistrant(ar);
1105        }
1106        // if exiting ecm success
1107        if (ar.exception == null) {
1108            if (mIsPhoneInEcmState) {
1109                mIsPhoneInEcmState = false;
1110                setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false");
1111            }
1112            // send an Intent
1113            sendEmergencyCallbackModeChange();
1114            // Re-initiate data connection
1115            mDcTracker.setInternalDataEnabled(true);
1116        }
1117    }
1118
1119    /**
1120     * Handle to cancel or restart Ecm timer in emergency call back mode
1121     * if action is CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled;
1122     * otherwise, restart Ecm timer and notify apps the timer is restarted.
1123     */
1124    void handleTimerInEmergencyCallbackMode(int action) {
1125        switch(action) {
1126        case CANCEL_ECM_TIMER:
1127            removeCallbacks(mExitEcmRunnable);
1128            mEcmTimerResetRegistrants.notifyResult(Boolean.TRUE);
1129            break;
1130        case RESTART_ECM_TIMER:
1131            long delayInMillis = SystemProperties.getLong(
1132                    TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
1133            postDelayed(mExitEcmRunnable, delayInMillis);
1134            mEcmTimerResetRegistrants.notifyResult(Boolean.FALSE);
1135            break;
1136        default:
1137            Rlog.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action);
1138        }
1139    }
1140
1141    public void notifyEcbmTimerReset(Boolean flag) {
1142        mEcmTimerResetRegistrants.notifyResult(flag);
1143    }
1144
1145    /**
1146     * Registration point for Ecm timer reset
1147     * @param h handler to notify
1148     * @param what User-defined message code
1149     * @param obj placed in Message.obj
1150     */
1151    @Override
1152    public void registerForEcmTimerReset(Handler h, int what, Object obj) {
1153        mEcmTimerResetRegistrants.addUnique(h, what, obj);
1154    }
1155
1156    @Override
1157    public void unregisterForEcmTimerReset(Handler h) {
1158        mEcmTimerResetRegistrants.remove(h);
1159    }
1160
1161    @Override
1162    public void handleMessage(Message msg) {
1163        AsyncResult ar;
1164        Message     onComplete;
1165
1166        // messages to be handled whether or not the phone is being destroyed
1167        // should only include messages which are being re-directed and do not use
1168        // resources of the phone being destroyed
1169        switch (msg.what) {
1170            // handle the select network completion callbacks.
1171            case EVENT_SET_NETWORK_MANUAL_COMPLETE:
1172            case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE:
1173                super.handleMessage(msg);
1174                return;
1175        }
1176
1177        if (!mIsTheCurrentActivePhone) {
1178            Rlog.e(LOG_TAG, "Received message " + msg +
1179                    "[" + msg.what + "] while being destroyed. Ignoring.");
1180            return;
1181        }
1182        switch(msg.what) {
1183            case EVENT_RADIO_AVAILABLE: {
1184                mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
1185
1186                mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
1187                mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
1188            }
1189            break;
1190
1191            case EVENT_GET_BASEBAND_VERSION_DONE:{
1192                ar = (AsyncResult)msg.obj;
1193
1194                if (ar.exception != null) {
1195                    break;
1196                }
1197
1198                if (DBG) Rlog.d(LOG_TAG, "Baseband version: " + ar.result);
1199                setSystemProperty(TelephonyProperties.PROPERTY_BASEBAND_VERSION, (String)ar.result);
1200            }
1201            break;
1202
1203            case EVENT_GET_DEVICE_IDENTITY_DONE:{
1204                ar = (AsyncResult)msg.obj;
1205
1206                if (ar.exception != null) {
1207                    break;
1208                }
1209                String[] respId = (String[])ar.result;
1210                mImei = respId[0];
1211                mImeiSv = respId[1];
1212                mEsn  =  respId[2];
1213                mMeid =  respId[3];
1214            }
1215            break;
1216
1217            case EVENT_EMERGENCY_CALLBACK_MODE_ENTER:{
1218                handleEnterEmergencyCallbackMode(msg);
1219            }
1220            break;
1221
1222            case  EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE:{
1223                handleExitEmergencyCallbackMode(msg);
1224            }
1225            break;
1226
1227            case EVENT_RUIM_RECORDS_LOADED:{
1228                Rlog.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received");
1229                updateCurrentCarrierInProvider();
1230                // Notify voicemails.
1231                log("notifyMessageWaitingChanged");
1232                mNotifier.notifyMessageWaitingChanged(this);
1233                updateVoiceMail();
1234            }
1235            break;
1236
1237            case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:{
1238                Rlog.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received");
1239                ImsPhone imsPhone = mImsPhone;
1240                if (imsPhone != null) {
1241                    imsPhone.getServiceState().setStateOff();
1242                }
1243            }
1244            break;
1245
1246            case EVENT_RADIO_ON:{
1247                Rlog.d(LOG_TAG, "Event EVENT_RADIO_ON Received");
1248                handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
1249            }
1250            break;
1251
1252            case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:{
1253                Rlog.d(LOG_TAG, "EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED");
1254                handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
1255            }
1256            break;
1257
1258            case EVENT_SSN:{
1259                Rlog.d(LOG_TAG, "Event EVENT_SSN Received");
1260            }
1261            break;
1262
1263            case EVENT_REGISTERED_TO_NETWORK:{
1264                Rlog.d(LOG_TAG, "Event EVENT_REGISTERED_TO_NETWORK Received");
1265            }
1266            break;
1267
1268            case EVENT_NV_READY:{
1269                Rlog.d(LOG_TAG, "Event EVENT_NV_READY Received");
1270                prepareEri();
1271                // Notify voicemails.
1272                log("notifyMessageWaitingChanged");
1273                mNotifier.notifyMessageWaitingChanged(this);
1274                updateVoiceMail();
1275            }
1276            break;
1277
1278            case EVENT_SET_VM_NUMBER_DONE:{
1279                ar = (AsyncResult)msg.obj;
1280                if (IccException.class.isInstance(ar.exception)) {
1281                    storeVoiceMailNumber(mVmNumber);
1282                    ar.exception = null;
1283                }
1284                onComplete = (Message) ar.userObj;
1285                if (onComplete != null) {
1286                    AsyncResult.forMessage(onComplete, ar.result, ar.exception);
1287                    onComplete.sendToTarget();
1288                }
1289            }
1290            break;
1291
1292            default:{
1293                super.handleMessage(msg);
1294            }
1295        }
1296    }
1297
1298    protected UiccCardApplication getUiccCardApplication() {
1299        return  mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP2);
1300    }
1301
1302    @Override
1303    protected void onUpdateIccAvailability() {
1304        if (mUiccController == null ) {
1305            return;
1306        }
1307
1308        UiccCardApplication newUiccApplication = getUiccCardApplication();
1309
1310        if (newUiccApplication == null) {
1311            log("can't find 3GPP2 application; trying APP_FAM_3GPP");
1312            newUiccApplication =
1313                    mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP);
1314        }
1315
1316        UiccCardApplication app = mUiccApplication.get();
1317        if (app != newUiccApplication) {
1318            if (app != null) {
1319                log("Removing stale icc objects.");
1320                if (mIccRecords.get() != null) {
1321                    unregisterForRuimRecordEvents();
1322                }
1323                mIccRecords.set(null);
1324                mUiccApplication.set(null);
1325            }
1326            if (newUiccApplication != null) {
1327                log("New Uicc application found");
1328                mUiccApplication.set(newUiccApplication);
1329                mIccRecords.set(newUiccApplication.getIccRecords());
1330                registerForRuimRecordEvents();
1331            }
1332        }
1333    }
1334
1335    /**
1336     * Handles the call to get the subscription source
1337     *
1338     * @param newSubscriptionSource holds the new CDMA subscription source value
1339     */
1340    private void handleCdmaSubscriptionSource(int newSubscriptionSource) {
1341        if (newSubscriptionSource != mCdmaSubscriptionSource) {
1342             mCdmaSubscriptionSource = newSubscriptionSource;
1343             if (newSubscriptionSource == CDMA_SUBSCRIPTION_NV) {
1344                 // NV is ready when subscription source is NV
1345                 sendMessage(obtainMessage(EVENT_NV_READY));
1346             }
1347        }
1348    }
1349
1350    /**
1351     * Retrieves the PhoneSubInfo of the CDMAPhone
1352     */
1353    @Override
1354    public PhoneSubInfo getPhoneSubInfo() {
1355        return mSubInfo;
1356    }
1357
1358    /**
1359     * Retrieves the IccPhoneBookInterfaceManager of the CDMAPhone
1360     */
1361    @Override
1362    public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager() {
1363        return mRuimPhoneBookInterfaceManager;
1364    }
1365
1366    public void registerForEriFileLoaded(Handler h, int what, Object obj) {
1367        Registrant r = new Registrant (h, what, obj);
1368        mEriFileLoadedRegistrants.add(r);
1369    }
1370
1371    public void unregisterForEriFileLoaded(Handler h) {
1372        mEriFileLoadedRegistrants.remove(h);
1373    }
1374
1375    // override for allowing access from other classes of this package
1376    /**
1377     * {@inheritDoc}
1378     */
1379    @Override
1380    public void setSystemProperty(String property, String value) {
1381        super.setSystemProperty(property, value);
1382    }
1383
1384    // override for allowing access from other classes of this package
1385    /**
1386     * {@inheritDoc}
1387     */
1388    @Override
1389    public String getSystemProperty(String property, String defValue) {
1390        return super.getSystemProperty(property, defValue);
1391    }
1392
1393    /**
1394     * Activate or deactivate cell broadcast SMS.
1395     *
1396     * @param activate 0 = activate, 1 = deactivate
1397     * @param response Callback message is empty on completion
1398     */
1399    @Override
1400    public void activateCellBroadcastSms(int activate, Message response) {
1401        Rlog.e(LOG_TAG, "[CDMAPhone] activateCellBroadcastSms() is obsolete; use SmsManager");
1402        response.sendToTarget();
1403    }
1404
1405    /**
1406     * Query the current configuration of cdma cell broadcast SMS.
1407     *
1408     * @param response Callback message is empty on completion
1409     */
1410    @Override
1411    public void getCellBroadcastSmsConfig(Message response) {
1412        Rlog.e(LOG_TAG, "[CDMAPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager");
1413        response.sendToTarget();
1414    }
1415
1416    /**
1417     * Configure cdma cell broadcast SMS.
1418     *
1419     * @param response Callback message is empty on completion
1420     */
1421    @Override
1422    public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
1423        Rlog.e(LOG_TAG, "[CDMAPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager");
1424        response.sendToTarget();
1425    }
1426
1427    /**
1428     * Returns true if OTA Service Provisioning needs to be performed.
1429     */
1430    @Override
1431    public boolean needsOtaServiceProvisioning() {
1432        return mSST.getOtasp() != ServiceStateTracker.OTASP_NOT_NEEDED;
1433    }
1434
1435    private static final String IS683A_FEATURE_CODE = "*228";
1436    private static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4;
1437    private static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2;
1438    private static final int IS683A_SYS_SEL_CODE_OFFSET = 4;
1439
1440    private static final int IS683_CONST_800MHZ_A_BAND = 0;
1441    private static final int IS683_CONST_800MHZ_B_BAND = 1;
1442    private static final int IS683_CONST_1900MHZ_A_BLOCK = 2;
1443    private static final int IS683_CONST_1900MHZ_B_BLOCK = 3;
1444    private static final int IS683_CONST_1900MHZ_C_BLOCK = 4;
1445    private static final int IS683_CONST_1900MHZ_D_BLOCK = 5;
1446    private static final int IS683_CONST_1900MHZ_E_BLOCK = 6;
1447    private static final int IS683_CONST_1900MHZ_F_BLOCK = 7;
1448    private static final int INVALID_SYSTEM_SELECTION_CODE = -1;
1449
1450    private static boolean isIs683OtaSpDialStr(String dialStr) {
1451        int sysSelCodeInt;
1452        boolean isOtaspDialString = false;
1453        int dialStrLen = dialStr.length();
1454
1455        if (dialStrLen == IS683A_FEATURE_CODE_NUM_DIGITS) {
1456            if (dialStr.equals(IS683A_FEATURE_CODE)) {
1457                isOtaspDialString = true;
1458            }
1459        } else {
1460            sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr);
1461            switch (sysSelCodeInt) {
1462                case IS683_CONST_800MHZ_A_BAND:
1463                case IS683_CONST_800MHZ_B_BAND:
1464                case IS683_CONST_1900MHZ_A_BLOCK:
1465                case IS683_CONST_1900MHZ_B_BLOCK:
1466                case IS683_CONST_1900MHZ_C_BLOCK:
1467                case IS683_CONST_1900MHZ_D_BLOCK:
1468                case IS683_CONST_1900MHZ_E_BLOCK:
1469                case IS683_CONST_1900MHZ_F_BLOCK:
1470                    isOtaspDialString = true;
1471                    break;
1472                default:
1473                    break;
1474            }
1475        }
1476        return isOtaspDialString;
1477    }
1478    /**
1479     * This function extracts the system selection code from the dial string.
1480     */
1481    private static int extractSelCodeFromOtaSpNum(String dialStr) {
1482        int dialStrLen = dialStr.length();
1483        int sysSelCodeInt = INVALID_SYSTEM_SELECTION_CODE;
1484
1485        if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE,
1486                                   0, IS683A_FEATURE_CODE_NUM_DIGITS)) &&
1487            (dialStrLen >= (IS683A_FEATURE_CODE_NUM_DIGITS +
1488                            IS683A_SYS_SEL_CODE_NUM_DIGITS))) {
1489                // Since we checked the condition above, the system selection code
1490                // extracted from dialStr will not cause any exception
1491                sysSelCodeInt = Integer.parseInt (
1492                                dialStr.substring (IS683A_FEATURE_CODE_NUM_DIGITS,
1493                                IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS));
1494        }
1495        if (DBG) Rlog.d(LOG_TAG, "extractSelCodeFromOtaSpNum " + sysSelCodeInt);
1496        return sysSelCodeInt;
1497    }
1498
1499    /**
1500     * This function checks if the system selection code extracted from
1501     * the dial string "sysSelCodeInt' is the system selection code specified
1502     * in the carrier ota sp number schema "sch".
1503     */
1504    private static boolean
1505    checkOtaSpNumBasedOnSysSelCode (int sysSelCodeInt, String sch[]) {
1506        boolean isOtaSpNum = false;
1507        try {
1508            // Get how many number of system selection code ranges
1509            int selRc = Integer.parseInt(sch[1]);
1510            for (int i = 0; i < selRc; i++) {
1511                if (!TextUtils.isEmpty(sch[i+2]) && !TextUtils.isEmpty(sch[i+3])) {
1512                    int selMin = Integer.parseInt(sch[i+2]);
1513                    int selMax = Integer.parseInt(sch[i+3]);
1514                    // Check if the selection code extracted from the dial string falls
1515                    // within any of the range pairs specified in the schema.
1516                    if ((sysSelCodeInt >= selMin) && (sysSelCodeInt <= selMax)) {
1517                        isOtaSpNum = true;
1518                        break;
1519                    }
1520                }
1521            }
1522        } catch (NumberFormatException ex) {
1523            // If the carrier ota sp number schema is not correct, we still allow dial
1524            // and only log the error:
1525            Rlog.e(LOG_TAG, "checkOtaSpNumBasedOnSysSelCode, error", ex);
1526        }
1527        return isOtaSpNum;
1528    }
1529
1530    // Define the pattern/format for carrier specified OTASP number schema.
1531    // It separates by comma and/or whitespace.
1532    private static Pattern pOtaSpNumSchema = Pattern.compile("[,\\s]+");
1533
1534    /**
1535     * The following function checks if a dial string is a carrier specified
1536     * OTASP number or not by checking against the OTASP number schema stored
1537     * in PROPERTY_OTASP_NUM_SCHEMA.
1538     *
1539     * Currently, there are 2 schemas for carriers to specify the OTASP number:
1540     * 1) Use system selection code:
1541     *    The schema is:
1542     *    SELC,the # of code pairs,min1,max1,min2,max2,...
1543     *    e.g "SELC,3,10,20,30,40,60,70" indicates that there are 3 pairs of
1544     *    selection codes, and they are {10,20}, {30,40} and {60,70} respectively.
1545     *
1546     * 2) Use feature code:
1547     *    The schema is:
1548     *    "FC,length of feature code,feature code".
1549     *     e.g "FC,2,*2" indicates that the length of the feature code is 2,
1550     *     and the code itself is "*2".
1551     */
1552    private boolean isCarrierOtaSpNum(String dialStr) {
1553        boolean isOtaSpNum = false;
1554        int sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr);
1555        if (sysSelCodeInt == INVALID_SYSTEM_SELECTION_CODE) {
1556            return isOtaSpNum;
1557        }
1558        // mCarrierOtaSpNumSchema is retrieved from PROPERTY_OTASP_NUM_SCHEMA:
1559        if (!TextUtils.isEmpty(mCarrierOtaSpNumSchema)) {
1560            Matcher m = pOtaSpNumSchema.matcher(mCarrierOtaSpNumSchema);
1561            if (DBG) {
1562                Rlog.d(LOG_TAG, "isCarrierOtaSpNum,schema" + mCarrierOtaSpNumSchema);
1563            }
1564
1565            if (m.find()) {
1566                String sch[] = pOtaSpNumSchema.split(mCarrierOtaSpNumSchema);
1567                // If carrier uses system selection code mechanism
1568                if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("SELC")) {
1569                    if (sysSelCodeInt!=INVALID_SYSTEM_SELECTION_CODE) {
1570                        isOtaSpNum=checkOtaSpNumBasedOnSysSelCode(sysSelCodeInt,sch);
1571                    } else {
1572                        if (DBG) {
1573                            Rlog.d(LOG_TAG, "isCarrierOtaSpNum,sysSelCodeInt is invalid");
1574                        }
1575                    }
1576                } else if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("FC")) {
1577                    int fcLen =  Integer.parseInt(sch[1]);
1578                    String fc = sch[2];
1579                    if (dialStr.regionMatches(0,fc,0,fcLen)) {
1580                        isOtaSpNum = true;
1581                    } else {
1582                        if (DBG) Rlog.d(LOG_TAG, "isCarrierOtaSpNum,not otasp number");
1583                    }
1584                } else {
1585                    if (DBG) {
1586                        Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema not supported" + sch[0]);
1587                    }
1588                }
1589            } else {
1590                if (DBG) {
1591                    Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern not right" +
1592                          mCarrierOtaSpNumSchema);
1593                }
1594            }
1595        } else {
1596            if (DBG) Rlog.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern empty");
1597        }
1598        return isOtaSpNum;
1599    }
1600
1601    /**
1602     * isOTASPNumber: checks a given number against the IS-683A OTASP dial string and carrier
1603     * OTASP dial string.
1604     *
1605     * @param dialStr the number to look up.
1606     * @return true if the number is in IS-683A OTASP dial string or carrier OTASP dial string
1607     */
1608    @Override
1609    public  boolean isOtaSpNumber(String dialStr){
1610        boolean isOtaSpNum = false;
1611        String dialableStr = PhoneNumberUtils.extractNetworkPortionAlt(dialStr);
1612        if (dialableStr != null) {
1613            isOtaSpNum = isIs683OtaSpDialStr(dialableStr);
1614            if (isOtaSpNum == false) {
1615                isOtaSpNum = isCarrierOtaSpNum(dialableStr);
1616            }
1617        }
1618        if (DBG) Rlog.d(LOG_TAG, "isOtaSpNumber " + isOtaSpNum);
1619        return isOtaSpNum;
1620    }
1621
1622    @Override
1623    public int getCdmaEriIconIndex() {
1624        return getServiceState().getCdmaEriIconIndex();
1625    }
1626
1627    /**
1628     * Returns the CDMA ERI icon mode,
1629     * 0 - ON
1630     * 1 - FLASHING
1631     */
1632    @Override
1633    public int getCdmaEriIconMode() {
1634        return getServiceState().getCdmaEriIconMode();
1635    }
1636
1637    /**
1638     * Returns the CDMA ERI text,
1639     */
1640    @Override
1641    public String getCdmaEriText() {
1642        int roamInd = getServiceState().getCdmaRoamingIndicator();
1643        int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator();
1644        return mEriManager.getCdmaEriText(roamInd, defRoamInd);
1645    }
1646
1647    /**
1648     * Store the voicemail number in preferences
1649     */
1650    private void storeVoiceMailNumber(String number) {
1651        // Update the preference value of voicemail number
1652        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
1653        SharedPreferences.Editor editor = sp.edit();
1654        editor.putString(VM_NUMBER_CDMA + getPhoneId(), number);
1655        editor.apply();
1656    }
1657
1658    /**
1659     * Sets PROPERTY_ICC_OPERATOR_ISO_COUNTRY property
1660     *
1661     */
1662    protected void setIsoCountryProperty(String operatorNumeric) {
1663        if (TextUtils.isEmpty(operatorNumeric)) {
1664            log("setIsoCountryProperty: clear 'gsm.sim.operator.iso-country'");
1665            setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
1666        } else {
1667            String iso = "";
1668            try {
1669                iso = MccTable.countryCodeForMcc(Integer.parseInt(
1670                        operatorNumeric.substring(0,3)));
1671            } catch (NumberFormatException ex) {
1672                loge("setIsoCountryProperty: countryCodeForMcc error", ex);
1673            } catch (StringIndexOutOfBoundsException ex) {
1674                loge("setIsoCountryProperty: countryCodeForMcc error", ex);
1675            }
1676
1677            log("setIsoCountryProperty: set 'gsm.sim.operator.iso-country' to iso=" + iso);
1678            setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, iso);
1679        }
1680    }
1681
1682    /**
1683     * Sets the "current" field in the telephony provider according to the
1684     * build-time operator numeric property
1685     *
1686     * @return true for success; false otherwise.
1687     */
1688    boolean updateCurrentCarrierInProvider(String operatorNumeric) {
1689        log("CDMAPhone: updateCurrentCarrierInProvider called");
1690        if (!TextUtils.isEmpty(operatorNumeric)) {
1691            try {
1692                Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
1693                ContentValues map = new ContentValues();
1694                map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
1695                log("updateCurrentCarrierInProvider from system: numeric=" + operatorNumeric);
1696                getContext().getContentResolver().insert(uri, map);
1697
1698                // Updates MCC MNC device configuration information
1699                log("update mccmnc=" + operatorNumeric);
1700                MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
1701
1702                return true;
1703            } catch (SQLException e) {
1704                Rlog.e(LOG_TAG, "Can't store current operator", e);
1705            }
1706        }
1707        return false;
1708    }
1709
1710    /**
1711     * Sets the "current" field in the telephony provider according to the SIM's operator.
1712     * Implemented in {@link CDMALTEPhone} for CDMA/LTE devices.
1713     *
1714     * @return true for success; false otherwise.
1715     */
1716    boolean updateCurrentCarrierInProvider() {
1717        return true;
1718    }
1719
1720    public void prepareEri() {
1721        if (mEriManager == null) {
1722            Rlog.e(LOG_TAG, "PrepareEri: Trying to access stale objects");
1723            return;
1724        }
1725        mEriManager.loadEriFile();
1726        if(mEriManager.isEriFileLoaded()) {
1727            // when the ERI file is loaded
1728            log("ERI read, notify registrants");
1729            mEriFileLoadedRegistrants.notifyRegistrants();
1730        }
1731    }
1732
1733    public boolean isEriFileLoaded() {
1734        return mEriManager.isEriFileLoaded();
1735    }
1736
1737    protected void registerForRuimRecordEvents() {
1738        IccRecords r = mIccRecords.get();
1739        if (r == null) {
1740            return;
1741        }
1742        r.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
1743    }
1744
1745    protected void unregisterForRuimRecordEvents() {
1746        IccRecords r = mIccRecords.get();
1747        if (r == null) {
1748            return;
1749        }
1750        r.unregisterForRecordsLoaded(this);
1751    }
1752
1753     /**
1754     * Sets the SIM voice message count
1755     * @param line Subscriber Profile Number, one-based. Only '1' is supported
1756     * @param countWaiting The number of messages waiting, if known. Use
1757     *                     -1 to indicate that an unknown number of
1758     *                      messages are waiting
1759     * This is a wrapper function for setVoiceMessageCount
1760     */
1761    @Override
1762    public void setVoiceMessageWaiting(int line, int countWaiting) {
1763        setVoiceMessageCount(countWaiting);
1764    }
1765
1766    protected void log(String s) {
1767        if (DBG)
1768            Rlog.d(LOG_TAG, s);
1769    }
1770
1771    protected void loge(String s, Exception e) {
1772        if (DBG)
1773            Rlog.e(LOG_TAG, s, e);
1774    }
1775
1776    @Override
1777    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1778        pw.println("CDMAPhone extends:");
1779        super.dump(fd, pw, args);
1780        pw.println(" mVmNumber=" + mVmNumber);
1781        pw.println(" mCT=" + mCT);
1782        pw.println(" mSST=" + mSST);
1783        pw.println(" mCdmaSSM=" + mCdmaSSM);
1784        pw.println(" mPendingMmis=" + mPendingMmis);
1785        pw.println(" mRuimPhoneBookInterfaceManager=" + mRuimPhoneBookInterfaceManager);
1786        pw.println(" mCdmaSubscriptionSource=" + mCdmaSubscriptionSource);
1787        pw.println(" mSubInfo=" + mSubInfo);
1788        pw.println(" mEriManager=" + mEriManager);
1789        pw.println(" mWakeLock=" + mWakeLock);
1790        pw.println(" mIsPhoneInEcmState=" + mIsPhoneInEcmState);
1791        if (VDBG) pw.println(" mImei=" + mImei);
1792        if (VDBG) pw.println(" mImeiSv=" + mImeiSv);
1793        if (VDBG) pw.println(" mEsn=" + mEsn);
1794        if (VDBG) pw.println(" mMeid=" + mMeid);
1795        pw.println(" mCarrierOtaSpNumSchema=" + mCarrierOtaSpNumSchema);
1796        pw.println(" getCdmaEriIconIndex()=" + getCdmaEriIconIndex());
1797        pw.println(" getCdmaEriIconMode()=" + getCdmaEriIconMode());
1798        pw.println(" getCdmaEriText()=" + getCdmaEriText());
1799        pw.println(" isMinInfoReady()=" + isMinInfoReady());
1800        pw.println(" isCspPlmnEnabled()=" + isCspPlmnEnabled());
1801    }
1802
1803    @Override
1804    public boolean setOperatorBrandOverride(String brand) {
1805        if (mUiccController == null) {
1806            return false;
1807        }
1808
1809        UiccCard card = mUiccController.getUiccCard();
1810        if (card == null) {
1811            return false;
1812        }
1813
1814        boolean status = card.setOperatorBrandOverride(brand);
1815
1816        // Refresh.
1817        if (status) {
1818            IccRecords iccRecords = mIccRecords.get();
1819            if (iccRecords != null) {
1820                SystemProperties.set(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA,
1821                        iccRecords.getServiceProviderName());
1822            }
1823            if (mSST != null) {
1824                mSST.pollState();
1825            }
1826        }
1827        return status;
1828    }
1829}
1830