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