GsmServiceStateTracker.java revision a8467dd0c524787104b1ccdddc5e8af10ba729ed
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                log("EVENT_SIM_RECORDS_LOADED: what=" + msg.what);
414
415                updatePhoneObject();
416                updateSpnDisplay();
417                break;
418
419            case EVENT_LOCATION_UPDATES_ENABLED:
420                ar = (AsyncResult) msg.obj;
421
422                if (ar.exception == null) {
423                    mCi.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
424                }
425                break;
426
427            case EVENT_SET_PREFERRED_NETWORK_TYPE:
428                ar = (AsyncResult) msg.obj;
429                // Don't care the result, only use for dereg network (COPS=2)
430                message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj);
431                mCi.setPreferredNetworkType(mPreferredNetworkType, message);
432                break;
433
434            case EVENT_RESET_PREFERRED_NETWORK_TYPE:
435                ar = (AsyncResult) msg.obj;
436                if (ar.userObj != null) {
437                    AsyncResult.forMessage(((Message) ar.userObj)).exception
438                            = ar.exception;
439                    ((Message) ar.userObj).sendToTarget();
440                }
441                break;
442
443            case EVENT_GET_PREFERRED_NETWORK_TYPE:
444                ar = (AsyncResult) msg.obj;
445
446                if (ar.exception == null) {
447                    mPreferredNetworkType = ((int[])ar.result)[0];
448                } else {
449                    mPreferredNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
450                }
451
452                message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj);
453                int toggledNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
454
455                mCi.setPreferredNetworkType(toggledNetworkType, message);
456                break;
457
458            case EVENT_CHECK_REPORT_GPRS:
459                if (mSS != null && !isGprsConsistent(mSS.getDataRegState(), mSS.getVoiceRegState())) {
460
461                    // Can't register data service while voice service is ok
462                    // i.e. CREG is ok while CGREG is not
463                    // possible a network or baseband side error
464                    GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
465                    EventLog.writeEvent(EventLogTags.DATA_NETWORK_REGISTRATION_FAIL,
466                            mSS.getOperatorNumeric(), loc != null ? loc.getCid() : -1);
467                    mReportedGprsNoReg = true;
468                }
469                mStartedGprsRegCheck = false;
470                break;
471
472            case EVENT_RESTRICTED_STATE_CHANGED:
473                // This is a notification from
474                // CommandsInterface.setOnRestrictedStateChanged
475
476                if (DBG) log("EVENT_RESTRICTED_STATE_CHANGED");
477
478                ar = (AsyncResult) msg.obj;
479
480                onRestrictedStateChanged(ar);
481                break;
482
483            case EVENT_ALL_DATA_DISCONNECTED:
484                long dds = SubscriptionManager.getDefaultDataSubId();
485                ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
486                synchronized(this) {
487                    if (mPendingRadioPowerOffAfterDataOff) {
488                        if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
489                        hangupAndPowerOff();
490                        mPendingRadioPowerOffAfterDataOff = false;
491                    } else {
492                        log("EVENT_ALL_DATA_DISCONNECTED is stale");
493                    }
494                }
495                break;
496
497            case EVENT_CHANGE_IMS_STATE:
498                if (DBG) log("EVENT_CHANGE_IMS_STATE:");
499
500                setPowerStateToDesired();
501                break;
502
503            default:
504                super.handleMessage(msg);
505            break;
506        }
507    }
508
509    @Override
510    protected void setPowerStateToDesired() {
511
512        if (DBG) {
513            log("mDesiredPowerState = " + mDesiredPowerState);
514            log("getRadioState = " + mCi.getRadioState());
515            log("mPowerOffDelayNeed = " + mPowerOffDelayNeed);
516            log("mAlarmSwitch = " + mAlarmSwitch);
517        }
518
519        if (mAlarmSwitch) {
520            if(DBG) log("mAlarmSwitch == true");
521            Context context = mPhone.getContext();
522            AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
523            am.cancel(mRadioOffIntent);
524            mAlarmSwitch = false;
525        }
526
527        // If we want it on and it's off, turn it on
528        if (mDesiredPowerState
529                && mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
530            mCi.setRadioPower(true, null);
531        } else if (!mDesiredPowerState && mCi.getRadioState().isOn()) {
532            // If it's on and available and we want it off gracefully
533            if (mPowerOffDelayNeed) {
534                if (mImsRegistrationOnOff && !mAlarmSwitch) {
535                    if(DBG) log("mImsRegistrationOnOff == true");
536                    Context context = mPhone.getContext();
537                    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
538
539                    Intent intent = new Intent(ACTION_RADIO_OFF);
540                    mRadioOffIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
541
542                    mAlarmSwitch = true;
543                    if (DBG) log("Alarm setting");
544                    am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
545                            SystemClock.elapsedRealtime() + 3000, mRadioOffIntent);
546                } else {
547                    DcTrackerBase dcTracker = mPhone.mDcTracker;
548                    powerOffRadioSafely(dcTracker);
549                }
550            } else {
551                DcTrackerBase dcTracker = mPhone.mDcTracker;
552                powerOffRadioSafely(dcTracker);
553            }
554        }
555    }
556
557    @Override
558    protected void hangupAndPowerOff() {
559        // hang up all active voice calls
560        if (mPhone.isInCall()) {
561            mPhone.mCT.mRingingCall.hangupIfAlive();
562            mPhone.mCT.mBackgroundCall.hangupIfAlive();
563            mPhone.mCT.mForegroundCall.hangupIfAlive();
564        }
565
566        mCi.setRadioPower(false, null);
567    }
568
569    @Override
570    protected void updateSpnDisplay() {
571        // The values of plmn/showPlmn change in different scenarios.
572        // 1) No service but emergency call allowed -> expected
573        //    to show "Emergency call only"
574        //    EXTRA_SHOW_PLMN = true
575        //    EXTRA_PLMN = "Emergency call only"
576
577        // 2) No service at all --> expected to show "No service"
578        //    EXTRA_SHOW_PLMN = true
579        //    EXTRA_PLMN = "No service"
580
581        // 3) Normal operation in either home or roaming service
582        //    EXTRA_SHOW_PLMN = depending on IccRecords rule
583        //    EXTRA_PLMN = plmn
584
585        // 4) No service due to power off, aka airplane mode
586        //    EXTRA_SHOW_PLMN = false
587        //    EXTRA_PLMN = null
588
589        IccRecords iccRecords = mIccRecords;
590        String plmn = null;
591        boolean showPlmn = false;
592        int rule = (iccRecords != null) ? iccRecords.getDisplayRule(mSS.getOperatorNumeric()) : 0;
593        if (mSS.getVoiceRegState() == ServiceState.STATE_OUT_OF_SERVICE
594                || mSS.getVoiceRegState() == ServiceState.STATE_EMERGENCY_ONLY) {
595            showPlmn = true;
596            if (mEmergencyOnly) {
597                // No service but emergency call allowed
598                plmn = Resources.getSystem().
599                        getText(com.android.internal.R.string.emergency_calls_only).toString();
600            } else {
601                // No service at all
602                plmn = Resources.getSystem().
603                        getText(com.android.internal.R.string.lockscreen_carrier_default).toString();
604            }
605            if (DBG) log("updateSpnDisplay: radio is on but out " +
606                    "of service, set plmn='" + plmn + "'");
607        } else if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
608            // In either home or roaming service
609            plmn = mSS.getOperatorAlphaLong();
610            showPlmn = !TextUtils.isEmpty(plmn) &&
611                    ((rule & SIMRecords.SPN_RULE_SHOW_PLMN)
612                            == SIMRecords.SPN_RULE_SHOW_PLMN);
613        } else {
614            // Power off state, such as airplane mode
615            if (DBG) log("updateSpnDisplay: radio is off w/ showPlmn="
616                    + showPlmn + " plmn=" + plmn);
617        }
618
619        // The value of spn/showSpn are same in different scenarios.
620        //    EXTRA_SHOW_SPN = depending on IccRecords rule
621        //    EXTRA_SPN = spn
622        String spn = (iccRecords != null) ? iccRecords.getServiceProviderName() : "";
623        boolean showSpn = !TextUtils.isEmpty(spn)
624                && ((rule & SIMRecords.SPN_RULE_SHOW_SPN)
625                        == SIMRecords.SPN_RULE_SHOW_SPN);
626
627        // Update SPN_STRINGS_UPDATED_ACTION IFF any value changes
628        if (showPlmn != mCurShowPlmn
629                || showSpn != mCurShowSpn
630                || !TextUtils.equals(spn, mCurSpn)
631                || !TextUtils.equals(plmn, mCurPlmn)) {
632            if (DBG) {
633                log(String.format("updateSpnDisplay: changed" +
634                        " sending intent rule=" + rule +
635                        " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'",
636                        showPlmn, plmn, showSpn, spn));
637            }
638            Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
639            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
640            intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, showSpn);
641            intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
642            intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
643            intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
644            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
645            mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
646        }
647
648        mCurShowSpn = showSpn;
649        mCurShowPlmn = showPlmn;
650        mCurSpn = spn;
651        mCurPlmn = plmn;
652    }
653
654    /**
655     * Handle the result of one of the pollState()-related requests
656     */
657    @Override
658    protected void handlePollStateResult (int what, AsyncResult ar) {
659        int ints[];
660        String states[];
661
662        // Ignore stale requests from last poll
663        if (ar.userObj != mPollingContext) return;
664
665        if (ar.exception != null) {
666            CommandException.Error err=null;
667
668            if (ar.exception instanceof CommandException) {
669                err = ((CommandException)(ar.exception)).getCommandError();
670            }
671
672            if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
673                // Radio has crashed or turned off
674                cancelPollState();
675                return;
676            }
677
678            if (!mCi.getRadioState().isOn()) {
679                // Radio has crashed or turned off
680                cancelPollState();
681                return;
682            }
683
684            if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
685                loge("RIL implementation has returned an error where it must succeed" +
686                        ar.exception);
687            }
688        } else try {
689            switch (what) {
690                case EVENT_POLL_STATE_REGISTRATION: {
691                    states = (String[])ar.result;
692                    int lac = -1;
693                    int cid = -1;
694                    int type = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
695                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
696                    int reasonRegStateDenied = -1;
697                    int psc = -1;
698                    if (states.length > 0) {
699                        try {
700                            regState = Integer.parseInt(states[0]);
701                            if (states.length >= 3) {
702                                if (states[1] != null && states[1].length() > 0) {
703                                    lac = Integer.parseInt(states[1], 16);
704                                }
705                                if (states[2] != null && states[2].length() > 0) {
706                                    cid = Integer.parseInt(states[2], 16);
707                                }
708
709                                // states[3] (if present) is the current radio technology
710                                if (states.length >= 4 && states[3] != null) {
711                                    type = Integer.parseInt(states[3]);
712                                }
713                            }
714                            if (states.length > 14) {
715                                if (states[14] != null && states[14].length() > 0) {
716                                    psc = Integer.parseInt(states[14], 16);
717                                }
718                            }
719                        } catch (NumberFormatException ex) {
720                            loge("error parsing RegistrationState: " + ex);
721                        }
722                    }
723
724                    mGsmRoaming = regCodeIsRoaming(regState);
725                    mNewSS.setState(regCodeToServiceState(regState));
726                    mNewSS.setRilVoiceRadioTechnology(type);
727
728                    boolean isVoiceCapable = mPhoneBase.getContext().getResources()
729                            .getBoolean(com.android.internal.R.bool.config_voice_capable);
730                    if ((regState == ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED
731                         || regState == ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED
732                         || regState == ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED
733                         || regState == ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED)
734                         && isVoiceCapable) {
735                        mEmergencyOnly = true;
736                    } else {
737                        mEmergencyOnly = false;
738                    }
739
740                    // LAC and CID are -1 if not avail
741                    mNewCellLoc.setLacAndCid(lac, cid);
742                    mNewCellLoc.setPsc(psc);
743                    break;
744                }
745
746                case EVENT_POLL_STATE_GPRS: {
747                    states = (String[])ar.result;
748
749                    int type = 0;
750                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
751                    mNewReasonDataDenied = -1;
752                    mNewMaxDataCalls = 1;
753                    if (states.length > 0) {
754                        try {
755                            regState = Integer.parseInt(states[0]);
756
757                            // states[3] (if present) is the current radio technology
758                            if (states.length >= 4 && states[3] != null) {
759                                type = Integer.parseInt(states[3]);
760                            }
761                            if ((states.length >= 5 ) &&
762                                    (regState == ServiceState.RIL_REG_STATE_DENIED)) {
763                                mNewReasonDataDenied = Integer.parseInt(states[4]);
764                            }
765                            if (states.length >= 6) {
766                                mNewMaxDataCalls = Integer.parseInt(states[5]);
767                            }
768                        } catch (NumberFormatException ex) {
769                            loge("error parsing GprsRegistrationState: " + ex);
770                        }
771                    }
772                    int dataRegState = regCodeToServiceState(regState);
773                    mNewSS.setDataRegState(dataRegState);
774                    mDataRoaming = regCodeIsRoaming(regState);
775                    mNewSS.setRilDataRadioTechnology(type);
776                    if (DBG) {
777                        log("handlPollStateResultMessage: GsmSST setDataRegState=" + dataRegState
778                                + " regState=" + regState
779                                + " dataRadioTechnology=" + type);
780                    }
781                    break;
782                }
783
784                case EVENT_POLL_STATE_OPERATOR: {
785                    String opNames[] = (String[])ar.result;
786
787                    if (opNames != null && opNames.length >= 3) {
788                         mNewSS.setOperatorName (opNames[0], opNames[1], opNames[2]);
789                    }
790                    break;
791                }
792
793                case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: {
794                    ints = (int[])ar.result;
795                    mNewSS.setIsManualSelection(ints[0] == 1);
796                    break;
797                }
798            }
799
800        } catch (RuntimeException ex) {
801            loge("Exception while polling service state. Probably malformed RIL response." + ex);
802        }
803
804        mPollingContext[0]--;
805
806        if (mPollingContext[0] == 0) {
807            /**
808             * Since the roaming state of gsm service (from +CREG) and
809             * data service (from +CGREG) could be different, the new SS
810             * is set to roaming when either is true.
811             *
812             * There are exceptions for the above rule.
813             * The new SS is not set as roaming while gsm service reports
814             * roaming but indeed it is same operator.
815             * And the operator is considered non roaming.
816             *
817             * The test for the operators is to handle special roaming
818             * agreements and MVNO's.
819             */
820            boolean roaming = (mGsmRoaming || mDataRoaming);
821            if ((mGsmRoaming && isSameNamedOperators(mNewSS)
822                        && !isSameNamedOperatorConsideredRoaming(mNewSS))
823                    || isOperatorConsideredNonRoaming(mNewSS)) {
824                roaming = false;
825            }
826            mNewSS.setRoaming(roaming);
827            mNewSS.setEmergencyOnly(mEmergencyOnly);
828            pollStateDone();
829        }
830    }
831
832    private void setSignalStrengthDefaultValues() {
833        mSignalStrength = new SignalStrength(true);
834    }
835
836    /**
837     * A complete "service state" from our perspective is
838     * composed of a handful of separate requests to the radio.
839     *
840     * We make all of these requests at once, but then abandon them
841     * and start over again if the radio notifies us that some
842     * event has changed
843     */
844    private void pollState() {
845        mPollingContext = new int[1];
846        mPollingContext[0] = 0;
847
848        switch (mCi.getRadioState()) {
849            case RADIO_UNAVAILABLE:
850                mNewSS.setStateOutOfService();
851                mNewCellLoc.setStateInvalid();
852                setSignalStrengthDefaultValues();
853                mGotCountryCode = false;
854                mNitzUpdatedTime = false;
855                pollStateDone();
856            break;
857
858            case RADIO_OFF:
859                mNewSS.setStateOff();
860                mNewCellLoc.setStateInvalid();
861                setSignalStrengthDefaultValues();
862                mGotCountryCode = false;
863                mNitzUpdatedTime = false;
864                pollStateDone();
865            break;
866
867            default:
868                // Issue all poll-related commands at once
869                // then count down the responses, which
870                // are allowed to arrive out-of-order
871
872                mPollingContext[0]++;
873                mCi.getOperator(
874                    obtainMessage(
875                        EVENT_POLL_STATE_OPERATOR, mPollingContext));
876
877                mPollingContext[0]++;
878                mCi.getDataRegistrationState(
879                    obtainMessage(
880                        EVENT_POLL_STATE_GPRS, mPollingContext));
881
882                mPollingContext[0]++;
883                mCi.getVoiceRegistrationState(
884                    obtainMessage(
885                        EVENT_POLL_STATE_REGISTRATION, mPollingContext));
886
887                mPollingContext[0]++;
888                mCi.getNetworkSelectionMode(
889                    obtainMessage(
890                        EVENT_POLL_STATE_NETWORK_SELECTION_MODE, mPollingContext));
891            break;
892        }
893    }
894
895    private void pollStateDone() {
896        if (DBG) {
897            log("Poll ServiceState done: " +
898                " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]" +
899                " oldMaxDataCalls=" + mMaxDataCalls +
900                " mNewMaxDataCalls=" + mNewMaxDataCalls +
901                " oldReasonDataDenied=" + mReasonDataDenied +
902                " mNewReasonDataDenied=" + mNewReasonDataDenied);
903        }
904
905        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
906            mNewSS.setRoaming(true);
907        }
908
909        useDataRegStateForDataOnlyDevices();
910
911        boolean hasRegistered =
912            mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
913            && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
914
915        boolean hasDeregistered =
916            mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
917            && mNewSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE;
918
919        boolean hasGprsAttached =
920                mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
921                && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
922
923        boolean hasGprsDetached =
924                mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
925                && mNewSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
926
927        boolean hasDataRegStateChanged =
928                mSS.getDataRegState() != mNewSS.getDataRegState();
929
930        boolean hasVoiceRegStateChanged =
931                mSS.getVoiceRegState() != mNewSS.getVoiceRegState();
932
933        boolean hasRilVoiceRadioTechnologyChanged =
934                mSS.getRilVoiceRadioTechnology() != mNewSS.getRilVoiceRadioTechnology();
935
936        boolean hasRilDataRadioTechnologyChanged =
937                mSS.getRilDataRadioTechnology() != mNewSS.getRilDataRadioTechnology();
938
939        boolean hasChanged = !mNewSS.equals(mSS);
940
941        boolean hasRoamingOn = !mSS.getRoaming() && mNewSS.getRoaming();
942
943        boolean hasRoamingOff = mSS.getRoaming() && !mNewSS.getRoaming();
944
945        boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
946
947        // Add an event log when connection state changes
948        if (hasVoiceRegStateChanged || hasDataRegStateChanged) {
949            EventLog.writeEvent(EventLogTags.GSM_SERVICE_STATE_CHANGE,
950                mSS.getVoiceRegState(), mSS.getDataRegState(),
951                mNewSS.getVoiceRegState(), mNewSS.getDataRegState());
952        }
953
954        // Add an event log when network type switched
955        // TODO: we may add filtering to reduce the event logged,
956        // i.e. check preferred network setting, only switch to 2G, etc
957        if (hasRilVoiceRadioTechnologyChanged) {
958            int cid = -1;
959            GsmCellLocation loc = mNewCellLoc;
960            if (loc != null) cid = loc.getCid();
961            // NOTE: this code was previously located after mSS and mNewSS are swapped, so
962            // existing logs were incorrectly using the new state for "network_from"
963            // and STATE_OUT_OF_SERVICE for "network_to". To avoid confusion, use a new log tag
964            // to record the correct states.
965            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED_NEW, cid,
966                    mSS.getRilVoiceRadioTechnology(),
967                    mNewSS.getRilVoiceRadioTechnology());
968            if (DBG) {
969                log("RAT switched "
970                        + ServiceState.rilRadioTechnologyToString(mSS.getRilVoiceRadioTechnology())
971                        + " -> "
972                        + ServiceState.rilRadioTechnologyToString(
973                                mNewSS.getRilVoiceRadioTechnology()) + " at cell " + cid);
974            }
975        }
976
977        // swap mSS and mNewSS to put new state in mSS
978        ServiceState tss = mSS;
979        mSS = mNewSS;
980        mNewSS = tss;
981        // clean slate for next time
982        mNewSS.setStateOutOfService();
983
984        // swap mCellLoc and mNewCellLoc to put new state in mCellLoc
985        GsmCellLocation tcl = mCellLoc;
986        mCellLoc = mNewCellLoc;
987        mNewCellLoc = tcl;
988
989        mReasonDataDenied = mNewReasonDataDenied;
990        mMaxDataCalls = mNewMaxDataCalls;
991
992        if (hasRilVoiceRadioTechnologyChanged) {
993            updatePhoneObject();
994        }
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