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