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