GsmServiceStateTracker.java revision 3beec5e255c514dc419e5cc3021d178b3a416d40
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.gsm;
18
19import android.app.AlarmManager;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.ContentResolver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.res.Resources;
29import android.database.ContentObserver;
30import android.os.AsyncResult;
31import android.os.Build;
32import android.os.Handler;
33import android.os.Message;
34import android.os.PowerManager;
35import android.os.SystemClock;
36import android.os.SystemProperties;
37import android.os.UserHandle;
38import android.provider.Settings;
39import android.provider.Settings.SettingNotFoundException;
40import android.telephony.CellIdentityGsm;
41import android.telephony.CellIdentityLte;
42import android.telephony.CellIdentityWcdma;
43import android.telephony.CellInfo;
44import android.telephony.CellInfoGsm;
45import android.telephony.CellInfoLte;
46import android.telephony.CellInfoWcdma;
47import android.telephony.CellLocation;
48import android.telephony.Rlog;
49import android.telephony.ServiceState;
50import android.telephony.SignalStrength;
51import android.telephony.gsm.GsmCellLocation;
52import android.telephony.TelephonyManager;
53import android.text.TextUtils;
54import android.util.EventLog;
55import android.util.TimeUtils;
56
57import com.android.internal.telephony.CommandException;
58import com.android.internal.telephony.CommandsInterface;
59import com.android.internal.telephony.EventLogTags;
60import com.android.internal.telephony.MccTable;
61import com.android.internal.telephony.PhoneConstants;
62import com.android.internal.telephony.ProxyController;
63import com.android.internal.telephony.Phone;
64import com.android.internal.telephony.PhoneFactory;
65import com.android.internal.telephony.RILConstants;
66import com.android.internal.telephony.RestrictedState;
67import com.android.internal.telephony.ServiceStateTracker;
68import android.telephony.SubscriptionManager;
69import com.android.internal.telephony.TelephonyIntents;
70import com.android.internal.telephony.TelephonyProperties;
71import com.android.internal.telephony.dataconnection.DcTrackerBase;
72import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
73import com.android.internal.telephony.uicc.IccRecords;
74import com.android.internal.telephony.uicc.SIMRecords;
75import com.android.internal.telephony.uicc.UiccCardApplication;
76import com.android.internal.telephony.uicc.UiccController;
77
78import java.io.FileDescriptor;
79import java.io.PrintWriter;
80import java.util.ArrayList;
81import java.util.Arrays;
82import java.util.Calendar;
83import java.util.Date;
84import java.util.List;
85import java.util.TimeZone;
86
87/**
88 * {@hide}
89 */
90final class GsmServiceStateTracker extends ServiceStateTracker {
91    static final String LOG_TAG = "GsmSST";
92    static final boolean VDBG = false;
93    //CAF_MSIM make it private ??
94    private static final int EVENT_ALL_DATA_DISCONNECTED = 1001;
95    private GSMPhone mPhone;
96    GsmCellLocation mCellLoc;
97    GsmCellLocation mNewCellLoc;
98    int mPreferredNetworkType;
99
100    private int mMaxDataCalls = 1;
101    private int mNewMaxDataCalls = 1;
102    private int mReasonDataDenied = -1;
103    private int mNewReasonDataDenied = -1;
104
105    /**
106     * GSM roaming status solely based on TS 27.007 7.2 CREG. Only used by
107     * handlePollStateResult to store CREG roaming result.
108     */
109    private boolean mGsmRoaming = false;
110
111    /**
112     * Data roaming status solely based on TS 27.007 10.1.19 CGREG. Only used by
113     * handlePollStateResult to store CGREG roaming result.
114     */
115    private boolean mDataRoaming = false;
116
117    /**
118     * Mark when service state is in emergency call only mode
119     */
120    private boolean mEmergencyOnly = false;
121
122    /**
123     * Sometimes we get the NITZ time before we know what country we
124     * are in. Keep the time zone information from the NITZ string so
125     * we can fix the time zone once know the country.
126     */
127    private boolean mNeedFixZoneAfterNitz = false;
128    private int mZoneOffset;
129    private boolean mZoneDst;
130    private long mZoneTime;
131    private boolean mGotCountryCode = false;
132    private ContentResolver mCr;
133
134    /** Boolean is true is setTimeFromNITZString was called */
135    private boolean mNitzUpdatedTime = false;
136
137    String mSavedTimeZone;
138    long mSavedTime;
139    long mSavedAtTime;
140
141    /** Started the recheck process after finding gprs should registered but not. */
142    private boolean mStartedGprsRegCheck = false;
143
144    /** Already sent the event-log for no gprs register. */
145    private boolean mReportedGprsNoReg = false;
146
147    /**
148     * The Notification object given to the NotificationManager.
149     */
150    private Notification mNotification;
151
152    /** Wake lock used while setting time of day. */
153    private PowerManager.WakeLock mWakeLock;
154    private static final String WAKELOCK_TAG = "ServiceStateTracker";
155
156    /** Keep track of SPN display rules, so we only broadcast intent if something changes. */
157    private String mCurSpn = null;
158    private String mCurPlmn = null;
159    private boolean mCurShowPlmn = false;
160    private boolean mCurShowSpn = false;
161
162    /** Notification type. */
163    static final int PS_ENABLED = 1001;            // Access Control blocks data service
164    static final int PS_DISABLED = 1002;           // Access Control enables data service
165    static final int CS_ENABLED = 1003;            // Access Control blocks all voice/sms service
166    static final int CS_DISABLED = 1004;           // Access Control enables all voice/sms service
167    static final int CS_NORMAL_ENABLED = 1005;     // Access Control blocks normal voice/sms service
168    static final int CS_EMERGENCY_ENABLED = 1006;  // Access Control blocks emergency call service
169
170    /** Notification id. */
171    static final int PS_NOTIFICATION = 888;  // Id to update and cancel PS restricted
172    static final int CS_NOTIFICATION = 999;  // Id to update and cancel CS restricted
173
174    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
175        @Override
176        public void onReceive(Context context, Intent intent) {
177            if (!mPhone.mIsTheCurrentActivePhone) {
178                Rlog.e(LOG_TAG, "Received Intent " + intent +
179                        " while being destroyed. Ignoring.");
180                return;
181            }
182
183            if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
184                // update emergency string whenever locale changed
185                updateSpnDisplay();
186            } else if (intent.getAction().equals(ACTION_RADIO_OFF)) {
187                mAlarmSwitch = false;
188                DcTrackerBase dcTracker = mPhone.mDcTracker;
189                powerOffRadioSafely(dcTracker);
190            }
191        }
192    };
193
194    private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
195        @Override
196        public void onChange(boolean selfChange) {
197            Rlog.i("GsmServiceStateTracker", "Auto time state changed");
198            revertToNitzTime();
199        }
200    };
201
202    private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
203        @Override
204        public void onChange(boolean selfChange) {
205            Rlog.i("GsmServiceStateTracker", "Auto time zone state changed");
206            revertToNitzTimeZone();
207        }
208    };
209
210    public GsmServiceStateTracker(GSMPhone phone) {
211        super(phone, phone.mCi, new CellInfoGsm());
212
213        mPhone = phone;
214        mCellLoc = new GsmCellLocation();
215        mNewCellLoc = new GsmCellLocation();
216
217        PowerManager powerManager =
218                (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
219        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
220
221        mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
222        mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
223
224        mCi.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
225        mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
226        mCi.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
227
228        // system setting property AIRPLANE_MODE_ON is set in Settings.
229        int airplaneMode = Settings.Global.getInt(
230                phone.getContext().getContentResolver(),
231                Settings.Global.AIRPLANE_MODE_ON, 0);
232        mDesiredPowerState = ! (airplaneMode > 0);
233
234        mCr = phone.getContext().getContentResolver();
235        mCr.registerContentObserver(
236                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
237                mAutoTimeObserver);
238        mCr.registerContentObserver(
239                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
240                mAutoTimeZoneObserver);
241
242        setSignalStrengthDefaultValues();
243
244        // Monitor locale change
245        IntentFilter filter = new IntentFilter();
246        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
247        phone.getContext().registerReceiver(mIntentReceiver, filter);
248
249        filter = new IntentFilter();
250        Context context = phone.getContext();
251        filter.addAction(ACTION_RADIO_OFF);
252        context.registerReceiver(mIntentReceiver, filter);
253
254        // Gsm doesn't support OTASP so its not needed
255        phone.notifyOtaspChanged(OTASP_NOT_NEEDED);
256    }
257
258    @Override
259    public void dispose() {
260        checkCorrectThread();
261        log("ServiceStateTracker dispose");
262
263        // Unregister for all events.
264        mCi.unregisterForAvailable(this);
265        mCi.unregisterForRadioStateChanged(this);
266        mCi.unregisterForVoiceNetworkStateChanged(this);
267        if (mUiccApplcation != null) {mUiccApplcation.unregisterForReady(this);}
268        if (mIccRecords != null) {mIccRecords.unregisterForRecordsLoaded(this);}
269        mCi.unSetOnRestrictedStateChanged(this);
270        mCi.unSetOnNITZTime(this);
271        mCr.unregisterContentObserver(mAutoTimeObserver);
272        mCr.unregisterContentObserver(mAutoTimeZoneObserver);
273        mPhone.getContext().unregisterReceiver(mIntentReceiver);
274        super.dispose();
275    }
276
277    @Override
278    protected void finalize() {
279        if(DBG) log("finalize");
280    }
281
282    @Override
283    protected Phone getPhone() {
284        return mPhone;
285    }
286
287    @Override
288    public void handleMessage (Message msg) {
289        AsyncResult ar;
290        int[] ints;
291        String[] strings;
292        Message message;
293
294        if (!mPhone.mIsTheCurrentActivePhone) {
295            Rlog.e(LOG_TAG, "Received message " + msg +
296                    "[" + msg.what + "] while being destroyed. Ignoring.");
297            return;
298        }
299        switch (msg.what) {
300            case EVENT_RADIO_AVAILABLE:
301                //this is unnecessary
302                //setPowerStateToDesired();
303                break;
304
305            case EVENT_SIM_READY:
306                // Set the network type, in case the radio does not restore it.
307                int networkType = PhoneFactory.calculatePreferredNetworkType(mPhone.getContext());
308                mCi.setPreferredNetworkType(networkType, null);
309
310                boolean skipRestoringSelection = mPhone.getContext().getResources().getBoolean(
311                        com.android.internal.R.bool.skip_restoring_network_selection);
312
313                if (!skipRestoringSelection) {
314                    // restore the previous network selection.
315                    mPhone.restoreSavedNetworkSelection(null);
316                }
317                pollState();
318                // Signal strength polling stops when radio is off
319                queueNextSignalStrengthPoll();
320                break;
321
322            case EVENT_RADIO_STATE_CHANGED:
323                // This will do nothing in the radio not
324                // available case
325                setPowerStateToDesired();
326                pollState();
327                break;
328
329            case EVENT_NETWORK_STATE_CHANGED:
330                pollState();
331                break;
332
333            case EVENT_GET_SIGNAL_STRENGTH:
334                // This callback is called when signal strength is polled
335                // all by itself
336
337                if (!(mCi.getRadioState().isOn())) {
338                    // Polling will continue when radio turns back on
339                    return;
340                }
341                ar = (AsyncResult) msg.obj;
342                onSignalStrengthResult(ar, true);
343                queueNextSignalStrengthPoll();
344
345                break;
346
347            case EVENT_GET_LOC_DONE:
348                ar = (AsyncResult) msg.obj;
349
350                if (ar.exception == null) {
351                    String states[] = (String[])ar.result;
352                    int lac = -1;
353                    int cid = -1;
354                    if (states.length >= 3) {
355                        try {
356                            if (states[1] != null && states[1].length() > 0) {
357                                lac = Integer.parseInt(states[1], 16);
358                            }
359                            if (states[2] != null && states[2].length() > 0) {
360                                cid = Integer.parseInt(states[2], 16);
361                            }
362                        } catch (NumberFormatException ex) {
363                            Rlog.w(LOG_TAG, "error parsing location: " + ex);
364                        }
365                    }
366                    mCellLoc.setLacAndCid(lac, cid);
367                    mPhone.notifyLocationChanged();
368                }
369
370                // Release any temporary cell lock, which could have been
371                // acquired to allow a single-shot location update.
372                disableSingleLocationUpdate();
373                break;
374
375            case EVENT_POLL_STATE_REGISTRATION:
376            case EVENT_POLL_STATE_GPRS:
377            case EVENT_POLL_STATE_OPERATOR:
378            case EVENT_POLL_STATE_NETWORK_SELECTION_MODE:
379                ar = (AsyncResult) msg.obj;
380
381                handlePollStateResult(msg.what, ar);
382                break;
383
384            case EVENT_POLL_SIGNAL_STRENGTH:
385                // Just poll signal strength...not part of pollState()
386
387                mCi.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
388                break;
389
390            case EVENT_NITZ_TIME:
391                ar = (AsyncResult) msg.obj;
392
393                String nitzString = (String)((Object[])ar.result)[0];
394                long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
395
396                setTimeFromNITZString(nitzString, nitzReceiveTime);
397                break;
398
399            case EVENT_SIGNAL_STRENGTH_UPDATE:
400                // This is a notification from
401                // CommandsInterface.setOnSignalStrengthUpdate
402
403                ar = (AsyncResult) msg.obj;
404
405                // The radio is telling us about signal strength changes
406                // we don't have to ask it
407                mDontPollSignalStrength = true;
408
409                onSignalStrengthResult(ar, true);
410                break;
411
412            case EVENT_SIM_RECORDS_LOADED:
413                updateSpnDisplay();
414                break;
415
416            case EVENT_LOCATION_UPDATES_ENABLED:
417                ar = (AsyncResult) msg.obj;
418
419                if (ar.exception == null) {
420                    mCi.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
421                }
422                break;
423
424            case EVENT_SET_PREFERRED_NETWORK_TYPE:
425                ar = (AsyncResult) msg.obj;
426                // Don't care the result, only use for dereg network (COPS=2)
427                message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj);
428                mCi.setPreferredNetworkType(mPreferredNetworkType, message);
429                break;
430
431            case EVENT_RESET_PREFERRED_NETWORK_TYPE:
432                ar = (AsyncResult) msg.obj;
433                if (ar.userObj != null) {
434                    AsyncResult.forMessage(((Message) ar.userObj)).exception
435                            = ar.exception;
436                    ((Message) ar.userObj).sendToTarget();
437                }
438                break;
439
440            case EVENT_GET_PREFERRED_NETWORK_TYPE:
441                ar = (AsyncResult) msg.obj;
442
443                if (ar.exception == null) {
444                    mPreferredNetworkType = ((int[])ar.result)[0];
445                } else {
446                    mPreferredNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
447                }
448
449                message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj);
450                int toggledNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
451
452                mCi.setPreferredNetworkType(toggledNetworkType, message);
453                break;
454
455            case EVENT_CHECK_REPORT_GPRS:
456                if (mSS != null && !isGprsConsistent(mSS.getDataRegState(), mSS.getVoiceRegState())) {
457
458                    // Can't register data service while voice service is ok
459                    // i.e. CREG is ok while CGREG is not
460                    // possible a network or baseband side error
461                    GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
462                    EventLog.writeEvent(EventLogTags.DATA_NETWORK_REGISTRATION_FAIL,
463                            mSS.getOperatorNumeric(), loc != null ? loc.getCid() : -1);
464                    mReportedGprsNoReg = true;
465                }
466                mStartedGprsRegCheck = false;
467                break;
468
469            case EVENT_RESTRICTED_STATE_CHANGED:
470                // This is a notification from
471                // CommandsInterface.setOnRestrictedStateChanged
472
473                if (DBG) log("EVENT_RESTRICTED_STATE_CHANGED");
474
475                ar = (AsyncResult) msg.obj;
476
477                onRestrictedStateChanged(ar);
478                break;
479
480            case EVENT_ALL_DATA_DISCONNECTED:
481                long dds = SubscriptionManager.getDefaultDataSubId();
482                ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
483                synchronized(this) {
484                    if (mPendingRadioPowerOffAfterDataOff) {
485                        if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
486                        hangupAndPowerOff();
487                        mPendingRadioPowerOffAfterDataOff = false;
488                    } else {
489                        log("EVENT_ALL_DATA_DISCONNECTED is stale");
490                    }
491                }
492                break;
493
494            case EVENT_CHANGE_IMS_STATE:
495                if (DBG) log("EVENT_CHANGE_IMS_STATE:");
496
497                setPowerStateToDesired();
498                break;
499
500            default:
501                super.handleMessage(msg);
502            break;
503        }
504    }
505
506    @Override
507    protected void setPowerStateToDesired() {
508
509        if (DBG) {
510            log("mDesiredPowerState = " + mDesiredPowerState);
511            log("getRadioState = " + mCi.getRadioState());
512            log("mPowerOffDelayNeed = " + mPowerOffDelayNeed);
513            log("mAlarmSwitch = " + mAlarmSwitch);
514        }
515
516        if (mAlarmSwitch) {
517            if(DBG) log("mAlarmSwitch == true");
518            Context context = mPhone.getContext();
519            AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
520            am.cancel(mRadioOffIntent);
521            mAlarmSwitch = false;
522        }
523
524        // If we want it on and it's off, turn it on
525        if (mDesiredPowerState
526                && mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
527            mCi.setRadioPower(true, null);
528        } else if (!mDesiredPowerState && mCi.getRadioState().isOn()) {
529            // If it's on and available and we want it off gracefully
530            if (mPowerOffDelayNeed) {
531                if (mImsRegistrationOnOff && !mAlarmSwitch) {
532                    if(DBG) log("mImsRegistrationOnOff == true");
533                    Context context = mPhone.getContext();
534                    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
535
536                    Intent intent = new Intent(ACTION_RADIO_OFF);
537                    mRadioOffIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
538
539                    mAlarmSwitch = true;
540                    if (DBG) log("Alarm setting");
541                    am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
542                            SystemClock.elapsedRealtime() + 3000, mRadioOffIntent);
543                } else {
544                    DcTrackerBase dcTracker = mPhone.mDcTracker;
545                    powerOffRadioSafely(dcTracker);
546                }
547            } else {
548                DcTrackerBase dcTracker = mPhone.mDcTracker;
549                powerOffRadioSafely(dcTracker);
550            }
551        }
552    }
553
554    @Override
555    protected void hangupAndPowerOff() {
556        // hang up all active voice calls
557        if (mPhone.isInCall()) {
558            mPhone.mCT.mRingingCall.hangupIfAlive();
559            mPhone.mCT.mBackgroundCall.hangupIfAlive();
560            mPhone.mCT.mForegroundCall.hangupIfAlive();
561        }
562
563        mCi.setRadioPower(false, null);
564    }
565
566    @Override
567    protected void updateSpnDisplay() {
568        // The values of plmn/showPlmn change in different scenarios.
569        // 1) No service but emergency call allowed -> expected
570        //    to show "Emergency call only"
571        //    EXTRA_SHOW_PLMN = true
572        //    EXTRA_PLMN = "Emergency call only"
573
574        // 2) No service at all --> expected to show "No service"
575        //    EXTRA_SHOW_PLMN = true
576        //    EXTRA_PLMN = "No service"
577
578        // 3) Normal operation in either home or roaming service
579        //    EXTRA_SHOW_PLMN = depending on IccRecords rule
580        //    EXTRA_PLMN = plmn
581
582        // 4) No service due to power off, aka airplane mode
583        //    EXTRA_SHOW_PLMN = false
584        //    EXTRA_PLMN = null
585
586        IccRecords iccRecords = mIccRecords;
587        String plmn = null;
588        boolean showPlmn = false;
589        int rule = (iccRecords != null) ? iccRecords.getDisplayRule(mSS.getOperatorNumeric()) : 0;
590        if (mSS.getVoiceRegState() == ServiceState.STATE_OUT_OF_SERVICE
591                || mSS.getVoiceRegState() == ServiceState.STATE_EMERGENCY_ONLY) {
592            showPlmn = true;
593            if (mEmergencyOnly) {
594                // No service but emergency call allowed
595                plmn = Resources.getSystem().
596                        getText(com.android.internal.R.string.emergency_calls_only).toString();
597            } else {
598                // No service at all
599                plmn = Resources.getSystem().
600                        getText(com.android.internal.R.string.lockscreen_carrier_default).toString();
601            }
602            if (DBG) log("updateSpnDisplay: radio is on but out " +
603                    "of service, set plmn='" + plmn + "'");
604        } else if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
605            // In either home or roaming service
606            plmn = mSS.getOperatorAlphaLong();
607            showPlmn = !TextUtils.isEmpty(plmn) &&
608                    ((rule & SIMRecords.SPN_RULE_SHOW_PLMN)
609                            == SIMRecords.SPN_RULE_SHOW_PLMN);
610        } else {
611            // Power off state, such as airplane mode
612            if (DBG) log("updateSpnDisplay: radio is off w/ showPlmn="
613                    + showPlmn + " plmn=" + plmn);
614        }
615
616        // The value of spn/showSpn are same in different scenarios.
617        //    EXTRA_SHOW_SPN = depending on IccRecords rule
618        //    EXTRA_SPN = spn
619        String spn = (iccRecords != null) ? iccRecords.getServiceProviderName() : "";
620        boolean showSpn = !TextUtils.isEmpty(spn)
621                && ((rule & SIMRecords.SPN_RULE_SHOW_SPN)
622                        == SIMRecords.SPN_RULE_SHOW_SPN);
623
624        // Update SPN_STRINGS_UPDATED_ACTION IFF any value changes
625        if (showPlmn != mCurShowPlmn
626                || showSpn != mCurShowSpn
627                || !TextUtils.equals(spn, mCurSpn)
628                || !TextUtils.equals(plmn, mCurPlmn)) {
629            if (DBG) {
630                log(String.format("updateSpnDisplay: changed" +
631                        " sending intent rule=" + rule +
632                        " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'",
633                        showPlmn, plmn, showSpn, spn));
634            }
635            Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
636            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
637            intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, showSpn);
638            intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
639            intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
640            intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
641            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
642            mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
643        }
644
645        mCurShowSpn = showSpn;
646        mCurShowPlmn = showPlmn;
647        mCurSpn = spn;
648        mCurPlmn = plmn;
649    }
650
651    /**
652     * Handle the result of one of the pollState()-related requests
653     */
654    @Override
655    protected void handlePollStateResult (int what, AsyncResult ar) {
656        int ints[];
657        String states[];
658
659        // Ignore stale requests from last poll
660        if (ar.userObj != mPollingContext) return;
661
662        if (ar.exception != null) {
663            CommandException.Error err=null;
664
665            if (ar.exception instanceof CommandException) {
666                err = ((CommandException)(ar.exception)).getCommandError();
667            }
668
669            if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
670                // Radio has crashed or turned off
671                cancelPollState();
672                return;
673            }
674
675            if (!mCi.getRadioState().isOn()) {
676                // Radio has crashed or turned off
677                cancelPollState();
678                return;
679            }
680
681            if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
682                loge("RIL implementation has returned an error where it must succeed" +
683                        ar.exception);
684            }
685        } else try {
686            switch (what) {
687                case EVENT_POLL_STATE_REGISTRATION: {
688                    states = (String[])ar.result;
689                    int lac = -1;
690                    int cid = -1;
691                    int type = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
692                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
693                    int reasonRegStateDenied = -1;
694                    int psc = -1;
695                    if (states.length > 0) {
696                        try {
697                            regState = Integer.parseInt(states[0]);
698                            if (states.length >= 3) {
699                                if (states[1] != null && states[1].length() > 0) {
700                                    lac = Integer.parseInt(states[1], 16);
701                                }
702                                if (states[2] != null && states[2].length() > 0) {
703                                    cid = Integer.parseInt(states[2], 16);
704                                }
705
706                                // states[3] (if present) is the current radio technology
707                                if (states.length >= 4 && states[3] != null) {
708                                    type = Integer.parseInt(states[3]);
709                                }
710                            }
711                            if (states.length > 14) {
712                                if (states[14] != null && states[14].length() > 0) {
713                                    psc = Integer.parseInt(states[14], 16);
714                                }
715                            }
716                        } catch (NumberFormatException ex) {
717                            loge("error parsing RegistrationState: " + ex);
718                        }
719                    }
720
721                    mGsmRoaming = regCodeIsRoaming(regState);
722                    mNewSS.setState(regCodeToServiceState(regState));
723                    mNewSS.setRilVoiceRadioTechnology(type);
724
725                    boolean isVoiceCapable = mPhoneBase.getContext().getResources()
726                            .getBoolean(com.android.internal.R.bool.config_voice_capable);
727                    if ((regState == ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED
728                         || regState == ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED
729                         || regState == ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED
730                         || regState == ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED)
731                         && isVoiceCapable) {
732                        mEmergencyOnly = true;
733                    } else {
734                        mEmergencyOnly = false;
735                    }
736
737                    // LAC and CID are -1 if not avail
738                    mNewCellLoc.setLacAndCid(lac, cid);
739                    mNewCellLoc.setPsc(psc);
740                    break;
741                }
742
743                case EVENT_POLL_STATE_GPRS: {
744                    states = (String[])ar.result;
745
746                    int type = 0;
747                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
748                    mNewReasonDataDenied = -1;
749                    mNewMaxDataCalls = 1;
750                    if (states.length > 0) {
751                        try {
752                            regState = Integer.parseInt(states[0]);
753
754                            // states[3] (if present) is the current radio technology
755                            if (states.length >= 4 && states[3] != null) {
756                                type = Integer.parseInt(states[3]);
757                            }
758                            if ((states.length >= 5 ) &&
759                                    (regState == ServiceState.RIL_REG_STATE_DENIED)) {
760                                mNewReasonDataDenied = Integer.parseInt(states[4]);
761                            }
762                            if (states.length >= 6) {
763                                mNewMaxDataCalls = Integer.parseInt(states[5]);
764                            }
765                        } catch (NumberFormatException ex) {
766                            loge("error parsing GprsRegistrationState: " + ex);
767                        }
768                    }
769                    int dataRegState = regCodeToServiceState(regState);
770                    mNewSS.setDataRegState(dataRegState);
771                    mDataRoaming = regCodeIsRoaming(regState);
772                    mNewSS.setRilDataRadioTechnology(type);
773                    if (DBG) {
774                        log("handlPollStateResultMessage: GsmSST setDataRegState=" + dataRegState
775                                + " regState=" + regState
776                                + " dataRadioTechnology=" + type);
777                    }
778                    break;
779                }
780
781                case EVENT_POLL_STATE_OPERATOR: {
782                    String opNames[] = (String[])ar.result;
783
784                    if (opNames != null && opNames.length >= 3) {
785                        String brandOverride = mUiccController.getUiccCard() != null ?
786                            mUiccController.getUiccCard().getOperatorBrandOverride() : null;
787                        if (brandOverride != null) {
788                            mNewSS.setOperatorName(brandOverride, brandOverride, opNames[2]);
789                        } else {
790                            mNewSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
791                        }
792                    }
793                    break;
794                }
795
796                case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: {
797                    ints = (int[])ar.result;
798                    mNewSS.setIsManualSelection(ints[0] == 1);
799                    break;
800                }
801            }
802
803        } catch (RuntimeException ex) {
804            loge("Exception while polling service state. Probably malformed RIL response." + ex);
805        }
806
807        mPollingContext[0]--;
808
809        if (mPollingContext[0] == 0) {
810            /**
811             * Since the roaming state of gsm service (from +CREG) and
812             * data service (from +CGREG) could be different, the new SS
813             * is set to roaming when either is true.
814             *
815             * There are exceptions for the above rule.
816             * The new SS is not set as roaming while gsm service reports
817             * roaming but indeed it is same operator.
818             * And the operator is considered non roaming.
819             *
820             * The test for the operators is to handle special roaming
821             * agreements and MVNO's.
822             */
823            boolean roaming = (mGsmRoaming || mDataRoaming);
824            if ((mGsmRoaming && isSameNamedOperators(mNewSS)
825                        && !isSameNamedOperatorConsideredRoaming(mNewSS))
826                    || isOperatorConsideredNonRoaming(mNewSS)) {
827                roaming = false;
828            }
829            mNewSS.setRoaming(roaming);
830            mNewSS.setEmergencyOnly(mEmergencyOnly);
831            pollStateDone();
832        }
833    }
834
835    private void setSignalStrengthDefaultValues() {
836        mSignalStrength = new SignalStrength(true);
837    }
838
839    /**
840     * A complete "service state" from our perspective is
841     * composed of a handful of separate requests to the radio.
842     *
843     * We make all of these requests at once, but then abandon them
844     * and start over again if the radio notifies us that some
845     * event has changed
846     */
847    @Override
848    public void pollState() {
849        mPollingContext = new int[1];
850        mPollingContext[0] = 0;
851
852        switch (mCi.getRadioState()) {
853            case RADIO_UNAVAILABLE:
854                mNewSS.setStateOutOfService();
855                mNewCellLoc.setStateInvalid();
856                setSignalStrengthDefaultValues();
857                mGotCountryCode = false;
858                mNitzUpdatedTime = false;
859                pollStateDone();
860            break;
861
862            case RADIO_OFF:
863                mNewSS.setStateOff();
864                mNewCellLoc.setStateInvalid();
865                setSignalStrengthDefaultValues();
866                mGotCountryCode = false;
867                mNitzUpdatedTime = false;
868                pollStateDone();
869            break;
870
871            default:
872                // Issue all poll-related commands at once
873                // then count down the responses, which
874                // are allowed to arrive out-of-order
875
876                mPollingContext[0]++;
877                mCi.getOperator(
878                    obtainMessage(
879                        EVENT_POLL_STATE_OPERATOR, mPollingContext));
880
881                mPollingContext[0]++;
882                mCi.getDataRegistrationState(
883                    obtainMessage(
884                        EVENT_POLL_STATE_GPRS, mPollingContext));
885
886                mPollingContext[0]++;
887                mCi.getVoiceRegistrationState(
888                    obtainMessage(
889                        EVENT_POLL_STATE_REGISTRATION, mPollingContext));
890
891                mPollingContext[0]++;
892                mCi.getNetworkSelectionMode(
893                    obtainMessage(
894                        EVENT_POLL_STATE_NETWORK_SELECTION_MODE, mPollingContext));
895            break;
896        }
897    }
898
899    private void pollStateDone() {
900        if (DBG) {
901            log("Poll ServiceState done: " +
902                " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]" +
903                " oldMaxDataCalls=" + mMaxDataCalls +
904                " mNewMaxDataCalls=" + mNewMaxDataCalls +
905                " oldReasonDataDenied=" + mReasonDataDenied +
906                " mNewReasonDataDenied=" + mNewReasonDataDenied);
907        }
908
909        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
910            mNewSS.setRoaming(true);
911        }
912
913        useDataRegStateForDataOnlyDevices();
914
915        boolean hasRegistered =
916            mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
917            && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
918
919        boolean hasDeregistered =
920            mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
921            && mNewSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE;
922
923        boolean hasGprsAttached =
924                mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
925                && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
926
927        boolean hasGprsDetached =
928                mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
929                && mNewSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
930
931        boolean hasDataRegStateChanged =
932                mSS.getDataRegState() != mNewSS.getDataRegState();
933
934        boolean hasVoiceRegStateChanged =
935                mSS.getVoiceRegState() != mNewSS.getVoiceRegState();
936
937        boolean hasRilVoiceRadioTechnologyChanged =
938                mSS.getRilVoiceRadioTechnology() != mNewSS.getRilVoiceRadioTechnology();
939
940        boolean hasRilDataRadioTechnologyChanged =
941                mSS.getRilDataRadioTechnology() != mNewSS.getRilDataRadioTechnology();
942
943        boolean hasChanged = !mNewSS.equals(mSS);
944
945        boolean hasRoamingOn = !mSS.getRoaming() && mNewSS.getRoaming();
946
947        boolean hasRoamingOff = mSS.getRoaming() && !mNewSS.getRoaming();
948
949        boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
950
951        // Add an event log when connection state changes
952        if (hasVoiceRegStateChanged || hasDataRegStateChanged) {
953            EventLog.writeEvent(EventLogTags.GSM_SERVICE_STATE_CHANGE,
954                mSS.getVoiceRegState(), mSS.getDataRegState(),
955                mNewSS.getVoiceRegState(), mNewSS.getDataRegState());
956        }
957
958        // Add an event log when network type switched
959        // TODO: we may add filtering to reduce the event logged,
960        // i.e. check preferred network setting, only switch to 2G, etc
961        if (hasRilVoiceRadioTechnologyChanged) {
962            int cid = -1;
963            GsmCellLocation loc = mNewCellLoc;
964            if (loc != null) cid = loc.getCid();
965            // NOTE: this code was previously located after mSS and mNewSS are swapped, so
966            // existing logs were incorrectly using the new state for "network_from"
967            // and STATE_OUT_OF_SERVICE for "network_to". To avoid confusion, use a new log tag
968            // to record the correct states.
969            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED_NEW, cid,
970                    mSS.getRilVoiceRadioTechnology(),
971                    mNewSS.getRilVoiceRadioTechnology());
972            if (DBG) {
973                log("RAT switched "
974                        + ServiceState.rilRadioTechnologyToString(mSS.getRilVoiceRadioTechnology())
975                        + " -> "
976                        + ServiceState.rilRadioTechnologyToString(
977                                mNewSS.getRilVoiceRadioTechnology()) + " at cell " + cid);
978            }
979        }
980
981        // swap mSS and mNewSS to put new state in mSS
982        ServiceState tss = mSS;
983        mSS = mNewSS;
984        mNewSS = tss;
985        // clean slate for next time
986        mNewSS.setStateOutOfService();
987
988        // swap mCellLoc and mNewCellLoc to put new state in mCellLoc
989        GsmCellLocation tcl = mCellLoc;
990        mCellLoc = mNewCellLoc;
991        mNewCellLoc = tcl;
992
993        mReasonDataDenied = mNewReasonDataDenied;
994        mMaxDataCalls = mNewMaxDataCalls;
995
996        if (hasRilDataRadioTechnologyChanged) {
997            mPhone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
998                    ServiceState.rilRadioTechnologyToString(mSS.getRilVoiceRadioTechnology()));
999        }
1000
1001        if (hasRegistered) {
1002            mNetworkAttachedRegistrants.notifyRegistrants();
1003
1004            if (DBG) {
1005                log("pollStateDone: registering current mNitzUpdatedTime=" +
1006                        mNitzUpdatedTime + " changing to false");
1007            }
1008            mNitzUpdatedTime = false;
1009        }
1010
1011        if (hasChanged) {
1012            String operatorNumeric;
1013
1014            updateSpnDisplay();
1015
1016            mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
1017                mSS.getOperatorAlphaLong());
1018
1019            String prevOperatorNumeric =
1020                    SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
1021            operatorNumeric = mSS.getOperatorNumeric();
1022            mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
1023            updateCarrierMccMncConfiguration(operatorNumeric,
1024                    prevOperatorNumeric, mPhone.getContext());
1025            if (operatorNumeric == null) {
1026                if (DBG) log("operatorNumeric is null");
1027                mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
1028                mGotCountryCode = false;
1029                mNitzUpdatedTime = false;
1030            } else {
1031                String iso = "";
1032                String mcc = "";
1033                try{
1034                    mcc = operatorNumeric.substring(0, 3);
1035                    iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
1036                } catch ( NumberFormatException ex){
1037                    loge("pollStateDone: countryCodeForMcc error" + ex);
1038                } catch ( StringIndexOutOfBoundsException ex) {
1039                    loge("pollStateDone: countryCodeForMcc error" + ex);
1040                }
1041
1042                mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
1043                mGotCountryCode = true;
1044
1045                TimeZone zone = null;
1046
1047                if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso) &&
1048                        getAutoTimeZone()) {
1049
1050                    // Test both paths if ignore nitz is true
1051                    boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
1052                                TelephonyProperties.PROPERTY_IGNORE_NITZ, false) &&
1053                                    ((SystemClock.uptimeMillis() & 1) == 0);
1054
1055                    ArrayList<TimeZone> uniqueZones = TimeUtils.getTimeZonesWithUniqueOffsets(iso);
1056                    if ((uniqueZones.size() == 1) || testOneUniqueOffsetPath) {
1057                        zone = uniqueZones.get(0);
1058                        if (DBG) {
1059                           log("pollStateDone: no nitz but one TZ for iso-cc=" + iso +
1060                                   " with zone.getID=" + zone.getID() +
1061                                   " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
1062                        }
1063                        setAndBroadcastNetworkSetTimeZone(zone.getID());
1064                    } else {
1065                        if (DBG) {
1066                            log("pollStateDone: there are " + uniqueZones.size() +
1067                                " unique offsets for iso-cc='" + iso +
1068                                " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath +
1069                                "', do nothing");
1070                        }
1071                    }
1072                }
1073
1074                if (shouldFixTimeZoneNow(mPhone, operatorNumeric, prevOperatorNumeric,
1075                        mNeedFixZoneAfterNitz)) {
1076                    // If the offset is (0, false) and the timezone property
1077                    // is set, use the timezone property rather than
1078                    // GMT.
1079                    String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
1080                    if (DBG) {
1081                        log("pollStateDone: fix time zone zoneName='" + zoneName +
1082                            "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
1083                            " iso-cc='" + iso +
1084                            "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
1085                    }
1086
1087                    // "(mZoneOffset == 0) && (mZoneDst == false) &&
1088                    //  (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)"
1089                    // means that we received a NITZ string telling
1090                    // it is in GMT+0 w/ DST time zone
1091                    // BUT iso tells is NOT, e.g, a wrong NITZ reporting
1092                    // local time w/ 0 offset.
1093                    if ((mZoneOffset == 0) && (mZoneDst == false) &&
1094                        (zoneName != null) && (zoneName.length() > 0) &&
1095                        (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) {
1096                        zone = TimeZone.getDefault();
1097                        if (mNeedFixZoneAfterNitz) {
1098                            // For wrong NITZ reporting local time w/ 0 offset,
1099                            // need adjust time to reflect default timezone setting
1100                            long ctm = System.currentTimeMillis();
1101                            long tzOffset = zone.getOffset(ctm);
1102                            if (DBG) {
1103                                log("pollStateDone: tzOffset=" + tzOffset + " ltod=" +
1104                                        TimeUtils.logTimeOfDay(ctm));
1105                            }
1106                            if (getAutoTime()) {
1107                                long adj = ctm - tzOffset;
1108                                if (DBG) log("pollStateDone: adj ltod=" +
1109                                        TimeUtils.logTimeOfDay(adj));
1110                                setAndBroadcastNetworkSetTime(adj);
1111                            } else {
1112                                // Adjust the saved NITZ time to account for tzOffset.
1113                                mSavedTime = mSavedTime - tzOffset;
1114                            }
1115                        }
1116                        if (DBG) log("pollStateDone: using default TimeZone");
1117                    } else if (iso.equals("")){
1118                        // Country code not found.  This is likely a test network.
1119                        // Get a TimeZone based only on the NITZ parameters (best guess).
1120                        zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
1121                        if (DBG) log("pollStateDone: using NITZ TimeZone");
1122                    } else {
1123                        zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, iso);
1124                        if (DBG) log("pollStateDone: using getTimeZone(off, dst, time, iso)");
1125                    }
1126
1127                    mNeedFixZoneAfterNitz = false;
1128
1129                    if (zone != null) {
1130                        log("pollStateDone: zone != null zone.getID=" + zone.getID());
1131                        if (getAutoTimeZone()) {
1132                            setAndBroadcastNetworkSetTimeZone(zone.getID());
1133                        }
1134                        saveNitzTimeZone(zone.getID());
1135                    } else {
1136                        log("pollStateDone: zone == null");
1137                    }
1138                }
1139            }
1140
1141            mPhone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
1142                mSS.getRoaming() ? "true" : "false");
1143
1144            mPhone.notifyServiceStateChanged(mSS);
1145        }
1146
1147        if (hasGprsAttached) {
1148            mAttachedRegistrants.notifyRegistrants();
1149        }
1150
1151        if (hasGprsDetached) {
1152            mDetachedRegistrants.notifyRegistrants();
1153        }
1154
1155        if (hasDataRegStateChanged || hasRilDataRadioTechnologyChanged) {
1156            notifyDataRegStateRilRadioTechnologyChanged();
1157            mPhone.notifyDataConnection(null);
1158        }
1159
1160        if (hasRoamingOn) {
1161            mRoamingOnRegistrants.notifyRegistrants();
1162        }
1163
1164        if (hasRoamingOff) {
1165            mRoamingOffRegistrants.notifyRegistrants();
1166        }
1167
1168        if (hasLocationChanged) {
1169            mPhone.notifyLocationChanged();
1170        }
1171
1172        if (! isGprsConsistent(mSS.getDataRegState(), mSS.getVoiceRegState())) {
1173            if (!mStartedGprsRegCheck && !mReportedGprsNoReg) {
1174                mStartedGprsRegCheck = true;
1175
1176                int check_period = Settings.Global.getInt(
1177                        mPhone.getContext().getContentResolver(),
1178                        Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
1179                        DEFAULT_GPRS_CHECK_PERIOD_MILLIS);
1180                sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS),
1181                        check_period);
1182            }
1183        } else {
1184            mReportedGprsNoReg = false;
1185        }
1186        // TODO: Add GsmCellIdenity updating, see CdmaLteServiceStateTracker.
1187    }
1188
1189    /**
1190     * Check if GPRS got registered while voice is registered.
1191     *
1192     * @param dataRegState i.e. CGREG in GSM
1193     * @param voiceRegState i.e. CREG in GSM
1194     * @return false if device only register to voice but not gprs
1195     */
1196    private boolean isGprsConsistent(int dataRegState, int voiceRegState) {
1197        return !((voiceRegState == ServiceState.STATE_IN_SERVICE) &&
1198                (dataRegState != ServiceState.STATE_IN_SERVICE));
1199    }
1200
1201    /**
1202     * Returns a TimeZone object based only on parameters from the NITZ string.
1203     */
1204    private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
1205        TimeZone guess = findTimeZone(offset, dst, when);
1206        if (guess == null) {
1207            // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
1208            guess = findTimeZone(offset, !dst, when);
1209        }
1210        if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
1211        return guess;
1212    }
1213
1214    private TimeZone findTimeZone(int offset, boolean dst, long when) {
1215        int rawOffset = offset;
1216        if (dst) {
1217            rawOffset -= 3600000;
1218        }
1219        String[] zones = TimeZone.getAvailableIDs(rawOffset);
1220        TimeZone guess = null;
1221        Date d = new Date(when);
1222        for (String zone : zones) {
1223            TimeZone tz = TimeZone.getTimeZone(zone);
1224            if (tz.getOffset(when) == offset &&
1225                tz.inDaylightTime(d) == dst) {
1226                guess = tz;
1227                break;
1228            }
1229        }
1230
1231        return guess;
1232    }
1233
1234    private void queueNextSignalStrengthPoll() {
1235        if (mDontPollSignalStrength) {
1236            // The radio is telling us about signal strength changes
1237            // we don't have to ask it
1238            return;
1239        }
1240
1241        Message msg;
1242
1243        msg = obtainMessage();
1244        msg.what = EVENT_POLL_SIGNAL_STRENGTH;
1245
1246        long nextTime;
1247
1248        // TODO Don't poll signal strength if screen is off
1249        sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
1250    }
1251
1252    /**
1253     * Set restricted state based on the OnRestrictedStateChanged notification
1254     * If any voice or packet restricted state changes, trigger a UI
1255     * notification and notify registrants when sim is ready.
1256     *
1257     * @param ar an int value of RIL_RESTRICTED_STATE_*
1258     */
1259    private void onRestrictedStateChanged(AsyncResult ar) {
1260        RestrictedState newRs = new RestrictedState();
1261
1262        if (DBG) log("onRestrictedStateChanged: E rs "+ mRestrictedState);
1263
1264        if (ar.exception == null) {
1265            int[] ints = (int[])ar.result;
1266            int state = ints[0];
1267
1268            newRs.setCsEmergencyRestricted(
1269                    ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) ||
1270                    ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
1271            //ignore the normal call and data restricted state before SIM READY
1272            if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY) {
1273                newRs.setCsNormalRestricted(
1274                        ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) ||
1275                        ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
1276                newRs.setPsRestricted(
1277                        (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0);
1278            }
1279
1280            if (DBG) log("onRestrictedStateChanged: new rs "+ newRs);
1281
1282            if (!mRestrictedState.isPsRestricted() && newRs.isPsRestricted()) {
1283                mPsRestrictEnabledRegistrants.notifyRegistrants();
1284                setNotification(PS_ENABLED);
1285            } else if (mRestrictedState.isPsRestricted() && !newRs.isPsRestricted()) {
1286                mPsRestrictDisabledRegistrants.notifyRegistrants();
1287                setNotification(PS_DISABLED);
1288            }
1289
1290            /**
1291             * There are two kind of cs restriction, normal and emergency. So
1292             * there are 4 x 4 combinations in current and new restricted states
1293             * and we only need to notify when state is changed.
1294             */
1295            if (mRestrictedState.isCsRestricted()) {
1296                if (!newRs.isCsRestricted()) {
1297                    // remove all restriction
1298                    setNotification(CS_DISABLED);
1299                } else if (!newRs.isCsNormalRestricted()) {
1300                    // remove normal restriction
1301                    setNotification(CS_EMERGENCY_ENABLED);
1302                } else if (!newRs.isCsEmergencyRestricted()) {
1303                    // remove emergency restriction
1304                    setNotification(CS_NORMAL_ENABLED);
1305                }
1306            } else if (mRestrictedState.isCsEmergencyRestricted() &&
1307                    !mRestrictedState.isCsNormalRestricted()) {
1308                if (!newRs.isCsRestricted()) {
1309                    // remove all restriction
1310                    setNotification(CS_DISABLED);
1311                } else if (newRs.isCsRestricted()) {
1312                    // enable all restriction
1313                    setNotification(CS_ENABLED);
1314                } else if (newRs.isCsNormalRestricted()) {
1315                    // remove emergency restriction and enable normal restriction
1316                    setNotification(CS_NORMAL_ENABLED);
1317                }
1318            } else if (!mRestrictedState.isCsEmergencyRestricted() &&
1319                    mRestrictedState.isCsNormalRestricted()) {
1320                if (!newRs.isCsRestricted()) {
1321                    // remove all restriction
1322                    setNotification(CS_DISABLED);
1323                } else if (newRs.isCsRestricted()) {
1324                    // enable all restriction
1325                    setNotification(CS_ENABLED);
1326                } else if (newRs.isCsEmergencyRestricted()) {
1327                    // remove normal restriction and enable emergency restriction
1328                    setNotification(CS_EMERGENCY_ENABLED);
1329                }
1330            } else {
1331                if (newRs.isCsRestricted()) {
1332                    // enable all restriction
1333                    setNotification(CS_ENABLED);
1334                } else if (newRs.isCsEmergencyRestricted()) {
1335                    // enable emergency restriction
1336                    setNotification(CS_EMERGENCY_ENABLED);
1337                } else if (newRs.isCsNormalRestricted()) {
1338                    // enable normal restriction
1339                    setNotification(CS_NORMAL_ENABLED);
1340                }
1341            }
1342
1343            mRestrictedState = newRs;
1344        }
1345        log("onRestrictedStateChanged: X rs "+ mRestrictedState);
1346    }
1347
1348    /** code is registration state 0-5 from TS 27.007 7.2 */
1349    private int regCodeToServiceState(int code) {
1350        switch (code) {
1351            case 0:
1352            case 2: // 2 is "searching"
1353            case 3: // 3 is "registration denied"
1354            case 4: // 4 is "unknown" no vaild in current baseband
1355            case 10:// same as 0, but indicates that emergency call is possible.
1356            case 12:// same as 2, but indicates that emergency call is possible.
1357            case 13:// same as 3, but indicates that emergency call is possible.
1358            case 14:// same as 4, but indicates that emergency call is possible.
1359                return ServiceState.STATE_OUT_OF_SERVICE;
1360
1361            case 1:
1362                return ServiceState.STATE_IN_SERVICE;
1363
1364            case 5:
1365                // in service, roam
1366                return ServiceState.STATE_IN_SERVICE;
1367
1368            default:
1369                loge("regCodeToServiceState: unexpected service state " + code);
1370                return ServiceState.STATE_OUT_OF_SERVICE;
1371        }
1372    }
1373
1374
1375    /**
1376     * code is registration state 0-5 from TS 27.007 7.2
1377     * returns true if registered roam, false otherwise
1378     */
1379    private boolean regCodeIsRoaming (int code) {
1380        return ServiceState.RIL_REG_STATE_ROAMING == code;
1381    }
1382
1383    /**
1384     * Set roaming state if operator mcc is the same as sim mcc
1385     * and ons is different from spn
1386     *
1387     * @param s ServiceState hold current ons
1388     * @return true if same operator
1389     */
1390    private boolean isSameNamedOperators(ServiceState s) {
1391        String spn = getSystemProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
1392
1393        String onsl = s.getOperatorAlphaLong();
1394        String onss = s.getOperatorAlphaShort();
1395
1396        boolean equalsOnsl = onsl != null && spn.equals(onsl);
1397        boolean equalsOnss = onss != null && spn.equals(onss);
1398
1399        return currentMccEqualsSimMcc(s) && (equalsOnsl || equalsOnss);
1400    }
1401
1402    /**
1403     * Compare SIM MCC with Operator MCC
1404     *
1405     * @param s ServiceState hold current ons
1406     * @return true if both are same
1407     */
1408    private boolean currentMccEqualsSimMcc(ServiceState s) {
1409        String simNumeric = getSystemProperty(
1410                TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
1411        String operatorNumeric = s.getOperatorNumeric();
1412        boolean equalsMcc = true;
1413
1414        try {
1415            equalsMcc = simNumeric.substring(0, 3).
1416                    equals(operatorNumeric.substring(0, 3));
1417        } catch (Exception e){
1418        }
1419        return equalsMcc;
1420    }
1421
1422    /**
1423     * Do not set roaming state in case of oprators considered non-roaming.
1424     *
1425     + Can use mcc or mcc+mnc as item of config_operatorConsideredNonRoaming.
1426     * For example, 302 or 21407. If mcc or mcc+mnc match with operator,
1427     * don't set roaming state.
1428     *
1429     * @param s ServiceState hold current ons
1430     * @return false for roaming state set
1431     */
1432    private boolean isOperatorConsideredNonRoaming(ServiceState s) {
1433        String operatorNumeric = s.getOperatorNumeric();
1434        String[] numericArray = mPhone.getContext().getResources().getStringArray(
1435                    com.android.internal.R.array.config_operatorConsideredNonRoaming);
1436
1437        if (numericArray.length == 0 || operatorNumeric == null) {
1438            return false;
1439        }
1440
1441        for (String numeric : numericArray) {
1442            if (operatorNumeric.startsWith(numeric)) {
1443                return true;
1444            }
1445        }
1446        return false;
1447    }
1448
1449    private boolean isSameNamedOperatorConsideredRoaming(ServiceState s) {
1450        String operatorNumeric = s.getOperatorNumeric();
1451        String[] numericArray = mPhone.getContext().getResources().getStringArray(
1452                    com.android.internal.R.array.config_sameNamedOperatorConsideredRoaming);
1453
1454        if (numericArray.length == 0 || operatorNumeric == null) {
1455            return false;
1456        }
1457
1458        for (String numeric : numericArray) {
1459            if (operatorNumeric.startsWith(numeric)) {
1460                return true;
1461            }
1462        }
1463        return false;
1464    }
1465
1466    /**
1467     * @return The current GPRS state. IN_SERVICE is the same as "attached"
1468     * and OUT_OF_SERVICE is the same as detached.
1469     */
1470    @Override
1471    public int getCurrentDataConnectionState() {
1472        return mSS.getDataRegState();
1473    }
1474
1475    /**
1476     * @return true if phone is camping on a technology (eg UMTS)
1477     * that could support voice and data simultaneously.
1478     */
1479    @Override
1480    public boolean isConcurrentVoiceAndDataAllowed() {
1481        return (mSS.getRilVoiceRadioTechnology() >= ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
1482    }
1483
1484    /**
1485     * @return the current cell location information. Prefer Gsm location
1486     * information if available otherwise return LTE location information
1487     */
1488    public CellLocation getCellLocation() {
1489        if ((mCellLoc.getLac() >= 0) && (mCellLoc.getCid() >= 0)) {
1490            if (DBG) log("getCellLocation(): X good mCellLoc=" + mCellLoc);
1491            return mCellLoc;
1492        } else {
1493            List<CellInfo> result = getAllCellInfo();
1494            if (result != null) {
1495                // A hack to allow tunneling of LTE information via GsmCellLocation
1496                // so that older Network Location Providers can return some information
1497                // on LTE only networks, see bug 9228974.
1498                //
1499                // We'll search the return CellInfo array preferring GSM/WCDMA
1500                // data, but if there is none we'll tunnel the first LTE information
1501                // in the list.
1502                //
1503                // The tunnel'd LTE information is returned as follows:
1504                //   LAC = TAC field
1505                //   CID = CI field
1506                //   PSC = 0.
1507                GsmCellLocation cellLocOther = new GsmCellLocation();
1508                for (CellInfo ci : result) {
1509                    if (ci instanceof CellInfoGsm) {
1510                        CellInfoGsm cellInfoGsm = (CellInfoGsm)ci;
1511                        CellIdentityGsm cellIdentityGsm = cellInfoGsm.getCellIdentity();
1512                        cellLocOther.setLacAndCid(cellIdentityGsm.getLac(),
1513                                cellIdentityGsm.getCid());
1514                        cellLocOther.setPsc(cellIdentityGsm.getPsc());
1515                        if (DBG) log("getCellLocation(): X ret GSM info=" + cellLocOther);
1516                        return cellLocOther;
1517                    } else if (ci instanceof CellInfoWcdma) {
1518                        CellInfoWcdma cellInfoWcdma = (CellInfoWcdma)ci;
1519                        CellIdentityWcdma cellIdentityWcdma = cellInfoWcdma.getCellIdentity();
1520                        cellLocOther.setLacAndCid(cellIdentityWcdma.getLac(),
1521                                cellIdentityWcdma.getCid());
1522                        cellLocOther.setPsc(cellIdentityWcdma.getPsc());
1523                        if (DBG) log("getCellLocation(): X ret WCDMA info=" + cellLocOther);
1524                        return cellLocOther;
1525                    } else if ((ci instanceof CellInfoLte) &&
1526                            ((cellLocOther.getLac() < 0) || (cellLocOther.getCid() < 0))) {
1527                        // We'll return the first good LTE info we get if there is no better answer
1528                        CellInfoLte cellInfoLte = (CellInfoLte)ci;
1529                        CellIdentityLte cellIdentityLte = cellInfoLte.getCellIdentity();
1530                        if ((cellIdentityLte.getTac() != Integer.MAX_VALUE)
1531                                && (cellIdentityLte.getCi() != Integer.MAX_VALUE)) {
1532                            cellLocOther.setLacAndCid(cellIdentityLte.getTac(),
1533                                    cellIdentityLte.getCi());
1534                            cellLocOther.setPsc(0);
1535                            if (DBG) {
1536                                log("getCellLocation(): possible LTE cellLocOther=" + cellLocOther);
1537                            }
1538                        }
1539                    }
1540                }
1541                if (DBG) {
1542                    log("getCellLocation(): X ret best answer cellLocOther=" + cellLocOther);
1543                }
1544                return cellLocOther;
1545            } else {
1546                if (DBG) {
1547                    log("getCellLocation(): X empty mCellLoc and CellInfo mCellLoc=" + mCellLoc);
1548                }
1549                return mCellLoc;
1550            }
1551        }
1552    }
1553
1554    /**
1555     * nitzReceiveTime is time_t that the NITZ time was posted
1556     */
1557    private void setTimeFromNITZString (String nitz, long nitzReceiveTime) {
1558        // "yy/mm/dd,hh:mm:ss(+/-)tz"
1559        // tz is in number of quarter-hours
1560
1561        long start = SystemClock.elapsedRealtime();
1562        if (DBG) {log("NITZ: " + nitz + "," + nitzReceiveTime +
1563                        " start=" + start + " delay=" + (start - nitzReceiveTime));
1564        }
1565
1566        try {
1567            /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
1568             * offset as well (which we won't worry about until later) */
1569            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
1570
1571            c.clear();
1572            c.set(Calendar.DST_OFFSET, 0);
1573
1574            String[] nitzSubs = nitz.split("[/:,+-]");
1575
1576            int year = 2000 + Integer.parseInt(nitzSubs[0]);
1577            c.set(Calendar.YEAR, year);
1578
1579            // month is 0 based!
1580            int month = Integer.parseInt(nitzSubs[1]) - 1;
1581            c.set(Calendar.MONTH, month);
1582
1583            int date = Integer.parseInt(nitzSubs[2]);
1584            c.set(Calendar.DATE, date);
1585
1586            int hour = Integer.parseInt(nitzSubs[3]);
1587            c.set(Calendar.HOUR, hour);
1588
1589            int minute = Integer.parseInt(nitzSubs[4]);
1590            c.set(Calendar.MINUTE, minute);
1591
1592            int second = Integer.parseInt(nitzSubs[5]);
1593            c.set(Calendar.SECOND, second);
1594
1595            boolean sign = (nitz.indexOf('-') == -1);
1596
1597            int tzOffset = Integer.parseInt(nitzSubs[6]);
1598
1599            int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7])
1600                                              : 0;
1601
1602            // The zone offset received from NITZ is for current local time,
1603            // so DST correction is already applied.  Don't add it again.
1604            //
1605            // tzOffset += dst * 4;
1606            //
1607            // We could unapply it if we wanted the raw offset.
1608
1609            tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000;
1610
1611            TimeZone    zone = null;
1612
1613            // As a special extension, the Android emulator appends the name of
1614            // the host computer's timezone to the nitz string. this is zoneinfo
1615            // timezone name of the form Area!Location or Area!Location!SubLocation
1616            // so we need to convert the ! into /
1617            if (nitzSubs.length >= 9) {
1618                String  tzname = nitzSubs[8].replace('!','/');
1619                zone = TimeZone.getTimeZone( tzname );
1620            }
1621
1622            String iso = getSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
1623
1624            if (zone == null) {
1625
1626                if (mGotCountryCode) {
1627                    if (iso != null && iso.length() > 0) {
1628                        zone = TimeUtils.getTimeZone(tzOffset, dst != 0,
1629                                c.getTimeInMillis(),
1630                                iso);
1631                    } else {
1632                        // We don't have a valid iso country code.  This is
1633                        // most likely because we're on a test network that's
1634                        // using a bogus MCC (eg, "001"), so get a TimeZone
1635                        // based only on the NITZ parameters.
1636                        zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());
1637                    }
1638                }
1639            }
1640
1641            if ((zone == null) || (mZoneOffset != tzOffset) || (mZoneDst != (dst != 0))){
1642                // We got the time before the country or the zone has changed
1643                // so we don't know how to identify the DST rules yet.  Save
1644                // the information and hope to fix it up later.
1645
1646                mNeedFixZoneAfterNitz = true;
1647                mZoneOffset  = tzOffset;
1648                mZoneDst     = dst != 0;
1649                mZoneTime    = c.getTimeInMillis();
1650            }
1651
1652            if (zone != null) {
1653                if (getAutoTimeZone()) {
1654                    setAndBroadcastNetworkSetTimeZone(zone.getID());
1655                }
1656                saveNitzTimeZone(zone.getID());
1657            }
1658
1659            String ignore = SystemProperties.get("gsm.ignore-nitz");
1660            if (ignore != null && ignore.equals("yes")) {
1661                log("NITZ: Not setting clock because gsm.ignore-nitz is set");
1662                return;
1663            }
1664
1665            try {
1666                mWakeLock.acquire();
1667
1668                if (getAutoTime()) {
1669                    long millisSinceNitzReceived
1670                            = SystemClock.elapsedRealtime() - nitzReceiveTime;
1671
1672                    if (millisSinceNitzReceived < 0) {
1673                        // Sanity check: something is wrong
1674                        if (DBG) {
1675                            log("NITZ: not setting time, clock has rolled "
1676                                            + "backwards since NITZ time was received, "
1677                                            + nitz);
1678                        }
1679                        return;
1680                    }
1681
1682                    if (millisSinceNitzReceived > Integer.MAX_VALUE) {
1683                        // If the time is this far off, something is wrong > 24 days!
1684                        if (DBG) {
1685                            log("NITZ: not setting time, processing has taken "
1686                                        + (millisSinceNitzReceived / (1000 * 60 * 60 * 24))
1687                                        + " days");
1688                        }
1689                        return;
1690                    }
1691
1692                    // Note: with range checks above, cast to int is safe
1693                    c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
1694
1695                    if (DBG) {
1696                        log("NITZ: Setting time of day to " + c.getTime()
1697                            + " NITZ receive delay(ms): " + millisSinceNitzReceived
1698                            + " gained(ms): "
1699                            + (c.getTimeInMillis() - System.currentTimeMillis())
1700                            + " from " + nitz);
1701                    }
1702
1703                    setAndBroadcastNetworkSetTime(c.getTimeInMillis());
1704                    Rlog.i(LOG_TAG, "NITZ: after Setting time of day");
1705                }
1706                SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis()));
1707                saveNitzTime(c.getTimeInMillis());
1708                if (VDBG) {
1709                    long end = SystemClock.elapsedRealtime();
1710                    log("NITZ: end=" + end + " dur=" + (end - start));
1711                }
1712                mNitzUpdatedTime = true;
1713            } finally {
1714                mWakeLock.release();
1715            }
1716        } catch (RuntimeException ex) {
1717            loge("NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
1718        }
1719    }
1720
1721    private boolean getAutoTime() {
1722        try {
1723            return Settings.Global.getInt(mPhone.getContext().getContentResolver(),
1724                    Settings.Global.AUTO_TIME) > 0;
1725        } catch (SettingNotFoundException snfe) {
1726            return true;
1727        }
1728    }
1729
1730    private boolean getAutoTimeZone() {
1731        try {
1732            return Settings.Global.getInt(mPhone.getContext().getContentResolver(),
1733                    Settings.Global.AUTO_TIME_ZONE) > 0;
1734        } catch (SettingNotFoundException snfe) {
1735            return true;
1736        }
1737    }
1738
1739    private void saveNitzTimeZone(String zoneId) {
1740        mSavedTimeZone = zoneId;
1741    }
1742
1743    private void saveNitzTime(long time) {
1744        mSavedTime = time;
1745        mSavedAtTime = SystemClock.elapsedRealtime();
1746    }
1747
1748    /**
1749     * Set the timezone and send out a sticky broadcast so the system can
1750     * determine if the timezone was set by the carrier.
1751     *
1752     * @param zoneId timezone set by carrier
1753     */
1754    private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
1755        if (DBG) log("setAndBroadcastNetworkSetTimeZone: setTimeZone=" + zoneId);
1756        AlarmManager alarm =
1757            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1758        alarm.setTimeZone(zoneId);
1759        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
1760        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1761        intent.putExtra("time-zone", zoneId);
1762        mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1763        if (DBG) {
1764            log("setAndBroadcastNetworkSetTimeZone: call alarm.setTimeZone and broadcast zoneId=" +
1765                zoneId);
1766        }
1767    }
1768
1769    /**
1770     * Set the time and Send out a sticky broadcast so the system can determine
1771     * if the time was set by the carrier.
1772     *
1773     * @param time time set by network
1774     */
1775    private void setAndBroadcastNetworkSetTime(long time) {
1776        if (DBG) log("setAndBroadcastNetworkSetTime: time=" + time + "ms");
1777        SystemClock.setCurrentTimeMillis(time);
1778        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
1779        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
1780        intent.putExtra("time", time);
1781        mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1782    }
1783
1784    private void revertToNitzTime() {
1785        if (Settings.Global.getInt(mPhone.getContext().getContentResolver(),
1786                Settings.Global.AUTO_TIME, 0) == 0) {
1787            return;
1788        }
1789        if (DBG) {
1790            log("Reverting to NITZ Time: mSavedTime=" + mSavedTime
1791                + " mSavedAtTime=" + mSavedAtTime);
1792        }
1793        if (mSavedTime != 0 && mSavedAtTime != 0) {
1794            setAndBroadcastNetworkSetTime(mSavedTime
1795                    + (SystemClock.elapsedRealtime() - mSavedAtTime));
1796        }
1797    }
1798
1799    private void revertToNitzTimeZone() {
1800        if (Settings.Global.getInt(mPhone.getContext().getContentResolver(),
1801                Settings.Global.AUTO_TIME_ZONE, 0) == 0) {
1802            return;
1803        }
1804        if (DBG) log("Reverting to NITZ TimeZone: tz='" + mSavedTimeZone);
1805        if (mSavedTimeZone != null) {
1806            setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
1807        }
1808    }
1809
1810    /**
1811     * Post a notification to NotificationManager for restricted state
1812     *
1813     * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE
1814     */
1815    private void setNotification(int notifyType) {
1816
1817        if (DBG) log("setNotification: create notification " + notifyType);
1818        Context context = mPhone.getContext();
1819
1820        mNotification = new Notification();
1821        mNotification.when = System.currentTimeMillis();
1822        mNotification.flags = Notification.FLAG_AUTO_CANCEL;
1823        mNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
1824        Intent intent = new Intent();
1825        mNotification.contentIntent = PendingIntent
1826        .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
1827
1828        CharSequence details = "";
1829        CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle);
1830        int notificationId = CS_NOTIFICATION;
1831
1832        switch (notifyType) {
1833        case PS_ENABLED:
1834            notificationId = PS_NOTIFICATION;
1835            details = context.getText(com.android.internal.R.string.RestrictedOnData);
1836            break;
1837        case PS_DISABLED:
1838            notificationId = PS_NOTIFICATION;
1839            break;
1840        case CS_ENABLED:
1841            details = context.getText(com.android.internal.R.string.RestrictedOnAllVoice);
1842            break;
1843        case CS_NORMAL_ENABLED:
1844            details = context.getText(com.android.internal.R.string.RestrictedOnNormal);
1845            break;
1846        case CS_EMERGENCY_ENABLED:
1847            details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);
1848            break;
1849        case CS_DISABLED:
1850            // do nothing and cancel the notification later
1851            break;
1852        }
1853
1854        if (DBG) log("setNotification: put notification " + title + " / " +details);
1855        mNotification.tickerText = title;
1856        mNotification.setLatestEventInfo(context, title, details,
1857                mNotification.contentIntent);
1858
1859        NotificationManager notificationManager = (NotificationManager)
1860            context.getSystemService(Context.NOTIFICATION_SERVICE);
1861
1862        if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) {
1863            // cancel previous post notification
1864            notificationManager.cancel(notificationId);
1865        } else {
1866            // update restricted state notification
1867            notificationManager.notify(notificationId, mNotification);
1868        }
1869    }
1870
1871    private UiccCardApplication getUiccCardApplication() {
1872            return  mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
1873                    UiccController.APP_FAM_3GPP);
1874    }
1875
1876    @Override
1877    protected void onUpdateIccAvailability() {
1878        if (mUiccController == null ) {
1879            return;
1880        }
1881
1882        UiccCardApplication newUiccApplication = getUiccCardApplication();
1883
1884        if (mUiccApplcation != newUiccApplication) {
1885            if (mUiccApplcation != null) {
1886                log("Removing stale icc objects.");
1887                mUiccApplcation.unregisterForReady(this);
1888                if (mIccRecords != null) {
1889                    mIccRecords.unregisterForRecordsLoaded(this);
1890                }
1891                mIccRecords = null;
1892                mUiccApplcation = null;
1893            }
1894            if (newUiccApplication != null) {
1895                log("New card found");
1896                mUiccApplcation = newUiccApplication;
1897                mIccRecords = mUiccApplcation.getIccRecords();
1898                mUiccApplcation.registerForReady(this, EVENT_SIM_READY, null);
1899                if (mIccRecords != null) {
1900                    mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
1901                }
1902            }
1903        }
1904    }
1905    @Override
1906    protected void log(String s) {
1907        Rlog.d(LOG_TAG, "[GsmSST] " + s);
1908    }
1909
1910    @Override
1911    protected void loge(String s) {
1912        Rlog.e(LOG_TAG, "[GsmSST] " + s);
1913    }
1914
1915    @Override
1916    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1917        pw.println("GsmServiceStateTracker extends:");
1918        super.dump(fd, pw, args);
1919        pw.println(" mPhone=" + mPhone);
1920        pw.println(" mSS=" + mSS);
1921        pw.println(" mNewSS=" + mNewSS);
1922        pw.println(" mCellLoc=" + mCellLoc);
1923        pw.println(" mNewCellLoc=" + mNewCellLoc);
1924        pw.println(" mPreferredNetworkType=" + mPreferredNetworkType);
1925        pw.println(" mMaxDataCalls=" + mMaxDataCalls);
1926        pw.println(" mNewMaxDataCalls=" + mNewMaxDataCalls);
1927        pw.println(" mReasonDataDenied=" + mReasonDataDenied);
1928        pw.println(" mNewReasonDataDenied=" + mNewReasonDataDenied);
1929        pw.println(" mGsmRoaming=" + mGsmRoaming);
1930        pw.println(" mDataRoaming=" + mDataRoaming);
1931        pw.println(" mEmergencyOnly=" + mEmergencyOnly);
1932        pw.println(" mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz);
1933        pw.println(" mZoneOffset=" + mZoneOffset);
1934        pw.println(" mZoneDst=" + mZoneDst);
1935        pw.println(" mZoneTime=" + mZoneTime);
1936        pw.println(" mGotCountryCode=" + mGotCountryCode);
1937        pw.println(" mNitzUpdatedTime=" + mNitzUpdatedTime);
1938        pw.println(" mSavedTimeZone=" + mSavedTimeZone);
1939        pw.println(" mSavedTime=" + mSavedTime);
1940        pw.println(" mSavedAtTime=" + mSavedAtTime);
1941        pw.println(" mStartedGprsRegCheck=" + mStartedGprsRegCheck);
1942        pw.println(" mReportedGprsNoReg=" + mReportedGprsNoReg);
1943        pw.println(" mNotification=" + mNotification);
1944        pw.println(" mWakeLock=" + mWakeLock);
1945        pw.println(" mCurSpn=" + mCurSpn);
1946        pw.println(" mCurShowSpn=" + mCurShowSpn);
1947        pw.println(" mCurPlmn=" + mCurPlmn);
1948        pw.println(" mCurShowPlmn=" + mCurShowPlmn);
1949    }
1950
1951
1952    /**
1953     * Clean up existing voice and data connection then turn off radio power.
1954     *
1955     * Hang up the existing voice calls to decrease call drop rate.
1956     */
1957    @Override
1958    public void powerOffRadioSafely(DcTrackerBase dcTracker) {
1959        synchronized (this) {
1960            if (!mPendingRadioPowerOffAfterDataOff) {
1961                long dds = SubscriptionManager.getDefaultDataSubId();
1962                // To minimize race conditions we call cleanUpAllConnections on
1963                // both if else paths instead of before this isDisconnected test.
1964                if (dcTracker.isDisconnected()
1965                        && (dds == mPhone.getSubId()
1966                            || (dds != mPhone.getSubId()
1967                                && ProxyController.getInstance().isDataDisconnected(dds)))) {
1968                    // To minimize race conditions we do this after isDisconnected
1969                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
1970                    if (DBG) log("Data disconnected, turn off radio right away.");
1971                    hangupAndPowerOff();
1972                } else {
1973                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
1974                    if (dds != mPhone.getSubId()
1975                            && !ProxyController.getInstance().isDataDisconnected(dds)) {
1976                        if (DBG) log("Data is active on DDS.  Wait for all data disconnect");
1977                        // Data is not disconnected on DDS. Wait for the data disconnect complete
1978                        // before sending the RADIO_POWER off.
1979                        ProxyController.getInstance().registerForAllDataDisconnected(dds, this,
1980                                EVENT_ALL_DATA_DISCONNECTED, null);
1981                        mPendingRadioPowerOffAfterDataOff = true;
1982                    }
1983                    Message msg = Message.obtain(this);
1984                    msg.what = EVENT_SET_RADIO_POWER_OFF;
1985                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
1986                    if (sendMessageDelayed(msg, 30000)) {
1987                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
1988                        mPendingRadioPowerOffAfterDataOff = true;
1989                    } else {
1990                        log("Cannot send delayed Msg, turn off radio right away.");
1991                        hangupAndPowerOff();
1992                        mPendingRadioPowerOffAfterDataOff = false;
1993                    }
1994                }
1995            }
1996        }
1997
1998    }
1999
2000    public void setImsRegistrationState(boolean registered){
2001        if (mImsRegistrationOnOff && !registered) {
2002            if (mAlarmSwitch) {
2003                mImsRegistrationOnOff = registered;
2004
2005                Context context = mPhone.getContext();
2006                AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2007                am.cancel(mRadioOffIntent);
2008                mAlarmSwitch = false;
2009
2010                sendMessage(obtainMessage(EVENT_CHANGE_IMS_STATE));
2011                return;
2012            }
2013        }
2014        mImsRegistrationOnOff = registered;
2015    }
2016}
2017