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