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