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