GsmServiceStateTracker.java revision 99c2e1d6749cfad2a8ca94a47857d8c3bfc09454
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.IccRecords;
27import com.android.internal.telephony.MccTable;
28import com.android.internal.telephony.Phone;
29import com.android.internal.telephony.RestrictedState;
30import com.android.internal.telephony.RILConstants;
31import com.android.internal.telephony.ServiceStateTracker;
32import com.android.internal.telephony.TelephonyIntents;
33import com.android.internal.telephony.TelephonyProperties;
34import com.android.internal.telephony.UiccCard;
35import com.android.internal.telephony.UiccCardApplication;
36import com.android.internal.telephony.IccCardApplicationStatus.AppState;
37import com.android.internal.telephony.uicc.UiccController;
38
39import android.app.AlarmManager;
40import android.app.Notification;
41import android.app.NotificationManager;
42import android.app.PendingIntent;
43import android.content.BroadcastReceiver;
44import android.content.ContentResolver;
45import android.content.Context;
46import android.content.Intent;
47import android.content.IntentFilter;
48import android.content.res.Resources;
49import android.database.ContentObserver;
50import android.os.AsyncResult;
51import android.os.Handler;
52import android.os.Message;
53import android.os.PowerManager;
54import android.os.Registrant;
55import android.os.RegistrantList;
56import android.os.SystemClock;
57import android.os.SystemProperties;
58import android.os.UserHandle;
59import android.provider.Settings;
60import android.provider.Settings.SettingNotFoundException;
61import android.telephony.CellInfo;
62import android.telephony.CellInfoGsm;
63import android.telephony.ServiceState;
64import android.telephony.SignalStrength;
65import android.telephony.gsm.GsmCellLocation;
66import android.text.TextUtils;
67import android.util.EventLog;
68import android.telephony.Rlog;
69import android.util.TimeUtils;
70
71import java.io.FileDescriptor;
72import java.io.PrintWriter;
73import java.util.ArrayList;
74import java.util.Arrays;
75import java.util.Calendar;
76import java.util.Collection;
77import java.util.Date;
78import java.util.HashSet;
79import java.util.TimeZone;
80
81/**
82 * {@hide}
83 */
84final class GsmServiceStateTracker extends ServiceStateTracker {
85    static final String LOG_TAG = "GSM";
86    static final boolean DBG = true;
87
88    GSMPhone phone;
89    GsmCellLocation cellLoc;
90    GsmCellLocation newCellLoc;
91    int mPreferredNetworkType;
92
93    private int mMaxDataCalls = 1;
94    private int mNewMaxDataCalls = 1;
95    private int mReasonDataDenied = -1;
96    private int mNewReasonDataDenied = -1;
97
98    /**
99     * GSM roaming status solely based on TS 27.007 7.2 CREG. Only used by
100     * handlePollStateResult to store CREG roaming result.
101     */
102    private boolean mGsmRoaming = false;
103
104    /**
105     * Data roaming status solely based on TS 27.007 10.1.19 CGREG. Only used by
106     * handlePollStateResult to store CGREG roaming result.
107     */
108    private boolean mDataRoaming = false;
109
110    /**
111     * Mark when service state is in emergency call only mode
112     */
113    private boolean mEmergencyOnly = false;
114
115    /**
116     * Sometimes we get the NITZ time before we know what country we
117     * are in. Keep the time zone information from the NITZ string so
118     * we can fix the time zone once know the country.
119     */
120    private boolean mNeedFixZoneAfterNitz = false;
121    private int mZoneOffset;
122    private boolean mZoneDst;
123    private long mZoneTime;
124    private boolean mGotCountryCode = false;
125    private ContentResolver cr;
126
127    /** Boolean is true is setTimeFromNITZString was called */
128    private boolean mNitzUpdatedTime = false;
129
130    String mSavedTimeZone;
131    long mSavedTime;
132    long mSavedAtTime;
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 boolean curShowPlmn = false;
153    private boolean curShowSpn = false;
154
155
156    /** waiting period before recheck gprs and voice registration. */
157    static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
158
159    /** Notification type. */
160    static final int PS_ENABLED = 1001;            // Access Control blocks data service
161    static final int PS_DISABLED = 1002;           // Access Control enables data service
162    static final int CS_ENABLED = 1003;            // Access Control blocks all voice/sms service
163    static final int CS_DISABLED = 1004;           // Access Control enables all voice/sms service
164    static final int CS_NORMAL_ENABLED = 1005;     // Access Control blocks normal voice/sms service
165    static final int CS_EMERGENCY_ENABLED = 1006;  // Access Control blocks emergency call service
166
167    /** Notification id. */
168    static final int PS_NOTIFICATION = 888;  // Id to update and cancel PS restricted
169    static final int CS_NOTIFICATION = 999;  // Id to update and cancel CS restricted
170
171    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
172        @Override
173        public void onReceive(Context context, Intent intent) {
174            if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
175                // update emergency string whenever locale changed
176                updateSpnDisplay();
177            }
178        }
179    };
180
181    private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
182        @Override
183        public void onChange(boolean selfChange) {
184            Rlog.i("GsmServiceStateTracker", "Auto time state changed");
185            revertToNitzTime();
186        }
187    };
188
189    private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {
190        @Override
191        public void onChange(boolean selfChange) {
192            Rlog.i("GsmServiceStateTracker", "Auto time zone state changed");
193            revertToNitzTimeZone();
194        }
195    };
196
197    public GsmServiceStateTracker(GSMPhone phone) {
198        super(phone, phone.mCM, new CellInfoGsm());
199
200        this.phone = phone;
201        cellLoc = new GsmCellLocation();
202        newCellLoc = new GsmCellLocation();
203
204        PowerManager powerManager =
205                (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE);
206        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
207
208        cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
209        cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
210
211        cm.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
212        cm.setOnNITZTime(this, EVENT_NITZ_TIME, null);
213        cm.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
214
215        // system setting property AIRPLANE_MODE_ON is set in Settings.
216        int airplaneMode = Settings.Global.getInt(
217                phone.getContext().getContentResolver(),
218                Settings.Global.AIRPLANE_MODE_ON, 0);
219        mDesiredPowerState = ! (airplaneMode > 0);
220
221        cr = phone.getContext().getContentResolver();
222        cr.registerContentObserver(
223                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
224                mAutoTimeObserver);
225        cr.registerContentObserver(
226                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
227                mAutoTimeZoneObserver);
228
229        setSignalStrengthDefaultValues();
230
231        // Monitor locale change
232        IntentFilter filter = new IntentFilter();
233        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
234        phone.getContext().registerReceiver(mIntentReceiver, filter);
235
236        // Gsm doesn't support OTASP so its not needed
237        phone.notifyOtaspChanged(OTASP_NOT_NEEDED);
238    }
239
240    @Override
241    public void dispose() {
242        checkCorrectThread();
243        // Unregister for all events.
244        cm.unregisterForAvailable(this);
245        cm.unregisterForRadioStateChanged(this);
246        cm.unregisterForVoiceNetworkStateChanged(this);
247        if (mUiccApplcation != null) {mUiccApplcation.unregisterForReady(this);}
248        if (mIccRecords != null) {mIccRecords.unregisterForRecordsLoaded(this);}
249        cm.unSetOnRestrictedStateChanged(this);
250        cm.unSetOnNITZTime(this);
251        cr.unregisterContentObserver(this.mAutoTimeObserver);
252        cr.unregisterContentObserver(this.mAutoTimeZoneObserver);
253        phone.getContext().unregisterReceiver(mIntentReceiver);
254        super.dispose();
255    }
256
257    protected void finalize() {
258        if(DBG) log("finalize");
259    }
260
261    @Override
262    protected Phone getPhone() {
263        return phone;
264    }
265
266    public void handleMessage (Message msg) {
267        AsyncResult ar;
268        int[] ints;
269        String[] strings;
270        Message message;
271
272        if (!phone.mIsTheCurrentActivePhone) {
273            Rlog.e(LOG_TAG, "Received message " + msg +
274                    "[" + msg.what + "] while being destroyed. Ignoring.");
275            return;
276        }
277        switch (msg.what) {
278            case EVENT_RADIO_AVAILABLE:
279                //this is unnecessary
280                //setPowerStateToDesired();
281                break;
282
283            case EVENT_SIM_READY:
284                // Set the network type, in case the radio does not restore it.
285                cm.setCurrentPreferredNetworkType();
286
287                boolean skipRestoringSelection = phone.getContext().getResources().getBoolean(
288                        com.android.internal.R.bool.skip_restoring_network_selection);
289
290                if (!skipRestoringSelection) {
291                    // restore the previous network selection.
292                    phone.restoreSavedNetworkSelection(null);
293                }
294                pollState();
295                // Signal strength polling stops when radio is off
296                queueNextSignalStrengthPoll();
297                break;
298
299            case EVENT_RADIO_STATE_CHANGED:
300                // This will do nothing in the radio not
301                // available case
302                setPowerStateToDesired();
303                pollState();
304                break;
305
306            case EVENT_NETWORK_STATE_CHANGED:
307                pollState();
308                break;
309
310            case EVENT_GET_SIGNAL_STRENGTH:
311                // This callback is called when signal strength is polled
312                // all by itself
313
314                if (!(cm.getRadioState().isOn())) {
315                    // Polling will continue when radio turns back on
316                    return;
317                }
318                ar = (AsyncResult) msg.obj;
319                onSignalStrengthResult(ar, true);
320                queueNextSignalStrengthPoll();
321
322                break;
323
324            case EVENT_GET_LOC_DONE:
325                ar = (AsyncResult) msg.obj;
326
327                if (ar.exception == null) {
328                    String states[] = (String[])ar.result;
329                    int lac = -1;
330                    int cid = -1;
331                    if (states.length >= 3) {
332                        try {
333                            if (states[1] != null && states[1].length() > 0) {
334                                lac = Integer.parseInt(states[1], 16);
335                            }
336                            if (states[2] != null && states[2].length() > 0) {
337                                cid = Integer.parseInt(states[2], 16);
338                            }
339                        } catch (NumberFormatException ex) {
340                            Rlog.w(LOG_TAG, "error parsing location: " + ex);
341                        }
342                    }
343                    cellLoc.setLacAndCid(lac, cid);
344                    phone.notifyLocationChanged();
345                }
346
347                // Release any temporary cell lock, which could have been
348                // acquired to allow a single-shot location update.
349                disableSingleLocationUpdate();
350                break;
351
352            case EVENT_POLL_STATE_REGISTRATION:
353            case EVENT_POLL_STATE_GPRS:
354            case EVENT_POLL_STATE_OPERATOR:
355            case EVENT_POLL_STATE_NETWORK_SELECTION_MODE:
356                ar = (AsyncResult) msg.obj;
357
358                handlePollStateResult(msg.what, ar);
359                break;
360
361            case EVENT_POLL_SIGNAL_STRENGTH:
362                // Just poll signal strength...not part of pollState()
363
364                cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
365                break;
366
367            case EVENT_NITZ_TIME:
368                ar = (AsyncResult) msg.obj;
369
370                String nitzString = (String)((Object[])ar.result)[0];
371                long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue();
372
373                setTimeFromNITZString(nitzString, nitzReceiveTime);
374                break;
375
376            case EVENT_SIGNAL_STRENGTH_UPDATE:
377                // This is a notification from
378                // CommandsInterface.setOnSignalStrengthUpdate
379
380                ar = (AsyncResult) msg.obj;
381
382                // The radio is telling us about signal strength changes
383                // we don't have to ask it
384                dontPollSignalStrength = true;
385
386                onSignalStrengthResult(ar, true);
387                break;
388
389            case EVENT_SIM_RECORDS_LOADED:
390                updateSpnDisplay();
391                break;
392
393            case EVENT_LOCATION_UPDATES_ENABLED:
394                ar = (AsyncResult) msg.obj;
395
396                if (ar.exception == null) {
397                    cm.getVoiceRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
398                }
399                break;
400
401            case EVENT_SET_PREFERRED_NETWORK_TYPE:
402                ar = (AsyncResult) msg.obj;
403                // Don't care the result, only use for dereg network (COPS=2)
404                message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj);
405                cm.setPreferredNetworkType(mPreferredNetworkType, message);
406                break;
407
408            case EVENT_RESET_PREFERRED_NETWORK_TYPE:
409                ar = (AsyncResult) msg.obj;
410                if (ar.userObj != null) {
411                    AsyncResult.forMessage(((Message) ar.userObj)).exception
412                            = ar.exception;
413                    ((Message) ar.userObj).sendToTarget();
414                }
415                break;
416
417            case EVENT_GET_PREFERRED_NETWORK_TYPE:
418                ar = (AsyncResult) msg.obj;
419
420                if (ar.exception == null) {
421                    mPreferredNetworkType = ((int[])ar.result)[0];
422                } else {
423                    mPreferredNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
424                }
425
426                message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj);
427                int toggledNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
428
429                cm.setPreferredNetworkType(toggledNetworkType, message);
430                break;
431
432            case EVENT_CHECK_REPORT_GPRS:
433                if (ss != null && !isGprsConsistent(ss.getDataRegState(), ss.getVoiceRegState())) {
434
435                    // Can't register data service while voice service is ok
436                    // i.e. CREG is ok while CGREG is not
437                    // possible a network or baseband side error
438                    GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
439                    EventLog.writeEvent(EventLogTags.DATA_NETWORK_REGISTRATION_FAIL,
440                            ss.getOperatorNumeric(), loc != null ? loc.getCid() : -1);
441                    mReportedGprsNoReg = true;
442                }
443                mStartedGprsRegCheck = false;
444                break;
445
446            case EVENT_RESTRICTED_STATE_CHANGED:
447                // This is a notification from
448                // CommandsInterface.setOnRestrictedStateChanged
449
450                if (DBG) log("EVENT_RESTRICTED_STATE_CHANGED");
451
452                ar = (AsyncResult) msg.obj;
453
454                onRestrictedStateChanged(ar);
455                break;
456
457            default:
458                super.handleMessage(msg);
459            break;
460        }
461    }
462
463    protected void setPowerStateToDesired() {
464        // If we want it on and it's off, turn it on
465        if (mDesiredPowerState
466            && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
467            cm.setRadioPower(true, null);
468        } else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
469            // If it's on and available and we want it off gracefully
470            DataConnectionTracker dcTracker = phone.mDataConnectionTracker;
471            powerOffRadioSafely(dcTracker);
472        } // Otherwise, we're in the desired state
473    }
474
475    @Override
476    protected void hangupAndPowerOff() {
477        // hang up all active voice calls
478        if (phone.isInCall()) {
479            phone.mCT.ringingCall.hangupIfAlive();
480            phone.mCT.backgroundCall.hangupIfAlive();
481            phone.mCT.foregroundCall.hangupIfAlive();
482        }
483
484        cm.setRadioPower(false, null);
485    }
486
487    @Override
488    protected void updateSpnDisplay() {
489        // The values of plmn/showPlmn change in different scenarios.
490        // 1) No service but emergency call allowed -> expected
491        //    to show "Emergency call only"
492        //    EXTRA_SHOW_PLMN = true
493        //    EXTRA_PLMN = "Emergency call only"
494
495        // 2) No service at all --> expected to show "No service"
496        //    EXTRA_SHOW_PLMN = true
497        //    EXTRA_PLMN = "No service"
498
499        // 3) Normal operation in either home or roaming service
500        //    EXTRA_SHOW_PLMN = depending on IccRecords rule
501        //    EXTRA_PLMN = plmn
502
503        // 4) No service due to power off, aka airplane mode
504        //    EXTRA_SHOW_PLMN = false
505        //    EXTRA_PLMN = null
506
507        IccRecords iccRecords = mIccRecords;
508        String plmn = null;
509        boolean showPlmn = false;
510        int rule = (iccRecords != null) ? iccRecords.getDisplayRule(ss.getOperatorNumeric()) : 0;
511        if (ss.getVoiceRegState() == ServiceState.STATE_OUT_OF_SERVICE
512                || ss.getVoiceRegState() == ServiceState.STATE_EMERGENCY_ONLY) {
513            showPlmn = true;
514            if (mEmergencyOnly) {
515                // No service but emergency call allowed
516                plmn = Resources.getSystem().
517                        getText(com.android.internal.R.string.emergency_calls_only).toString();
518            } else {
519                // No service at all
520                plmn = Resources.getSystem().
521                        getText(com.android.internal.R.string.lockscreen_carrier_default).toString();
522            }
523            if (DBG) log("updateSpnDisplay: radio is on but out " +
524                    "of service, set plmn='" + plmn + "'");
525        } else if (ss.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
526            // In either home or roaming service
527            plmn = ss.getOperatorAlphaLong();
528            showPlmn = !TextUtils.isEmpty(plmn) &&
529                    ((rule & SIMRecords.SPN_RULE_SHOW_PLMN)
530                            == SIMRecords.SPN_RULE_SHOW_PLMN);
531        } else {
532            // Power off state, such as airplane mode
533            if (DBG) log("updateSpnDisplay: radio is off w/ showPlmn="
534                    + showPlmn + " plmn=" + plmn);
535        }
536
537        // The value of spn/showSpn are same in different scenarios.
538        //    EXTRA_SHOW_SPN = depending on IccRecords rule
539        //    EXTRA_SPN = spn
540        String spn = (iccRecords != null) ? iccRecords.getServiceProviderName() : "";
541        boolean showSpn = !TextUtils.isEmpty(spn)
542                && ((rule & SIMRecords.SPN_RULE_SHOW_SPN)
543                        == SIMRecords.SPN_RULE_SHOW_SPN);
544
545        // Update SPN_STRINGS_UPDATED_ACTION IFF any value changes
546        if (showPlmn != curShowPlmn
547                || showSpn != curShowSpn
548                || !TextUtils.equals(spn, curSpn)
549                || !TextUtils.equals(plmn, curPlmn)) {
550            if (DBG) {
551                log(String.format("updateSpnDisplay: changed" +
552                        " sending intent rule=" + rule +
553                        " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'",
554                        showPlmn, plmn, showSpn, spn));
555            }
556            Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
557            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
558            intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, showSpn);
559            intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
560            intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
561            intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
562            phone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
563        }
564
565        curShowSpn = showSpn;
566        curShowPlmn = showPlmn;
567        curSpn = spn;
568        curPlmn = plmn;
569    }
570
571    /**
572     * Handle the result of one of the pollState()-related requests
573     */
574    @Override
575    protected void handlePollStateResult (int what, AsyncResult ar) {
576        int ints[];
577        String states[];
578
579        // Ignore stale requests from last poll
580        if (ar.userObj != pollingContext) return;
581
582        if (ar.exception != null) {
583            CommandException.Error err=null;
584
585            if (ar.exception instanceof CommandException) {
586                err = ((CommandException)(ar.exception)).getCommandError();
587            }
588
589            if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
590                // Radio has crashed or turned off
591                cancelPollState();
592                return;
593            }
594
595            if (!cm.getRadioState().isOn()) {
596                // Radio has crashed or turned off
597                cancelPollState();
598                return;
599            }
600
601            if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
602                loge("RIL implementation has returned an error where it must succeed" +
603                        ar.exception);
604            }
605        } else try {
606            switch (what) {
607                case EVENT_POLL_STATE_REGISTRATION: {
608                    states = (String[])ar.result;
609                    int lac = -1;
610                    int cid = -1;
611                    int type = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
612                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
613                    int reasonRegStateDenied = -1;
614                    int psc = -1;
615                    if (states.length > 0) {
616                        try {
617                            regState = Integer.parseInt(states[0]);
618                            if (states.length >= 3) {
619                                if (states[1] != null && states[1].length() > 0) {
620                                    lac = Integer.parseInt(states[1], 16);
621                                }
622                                if (states[2] != null && states[2].length() > 0) {
623                                    cid = Integer.parseInt(states[2], 16);
624                                }
625
626                                // states[3] (if present) is the current radio technology
627                                if (states.length >= 4 && states[3] != null) {
628                                    type = Integer.parseInt(states[3]);
629                                }
630                            }
631                            if (states.length > 14) {
632                                if (states[14] != null && states[14].length() > 0) {
633                                    psc = Integer.parseInt(states[14], 16);
634                                }
635                            }
636                        } catch (NumberFormatException ex) {
637                            loge("error parsing RegistrationState: " + ex);
638                        }
639                    }
640
641                    mGsmRoaming = regCodeIsRoaming(regState);
642                    newSS.setState(regCodeToServiceState(regState));
643                    newSS.setRilVoiceRadioTechnology(type);
644
645                    if (regState == ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED
646                         || regState == ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED
647                         || regState == ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED
648                         || regState == ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED) {
649                        mEmergencyOnly = true;
650                    } else {
651                        mEmergencyOnly = false;
652                    }
653
654                    // LAC and CID are -1 if not avail
655                    newCellLoc.setLacAndCid(lac, cid);
656                    newCellLoc.setPsc(psc);
657                    break;
658                }
659
660                case EVENT_POLL_STATE_GPRS: {
661                    states = (String[])ar.result;
662
663                    int type = 0;
664                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
665                    mNewReasonDataDenied = -1;
666                    mNewMaxDataCalls = 1;
667                    if (states.length > 0) {
668                        try {
669                            regState = Integer.parseInt(states[0]);
670
671                            // states[3] (if present) is the current radio technology
672                            if (states.length >= 4 && states[3] != null) {
673                                type = Integer.parseInt(states[3]);
674                            }
675                            if ((states.length >= 5 ) &&
676                                    (regState == ServiceState.RIL_REG_STATE_DENIED)) {
677                                mNewReasonDataDenied = Integer.parseInt(states[4]);
678                            }
679                            if (states.length >= 6) {
680                                mNewMaxDataCalls = Integer.parseInt(states[5]);
681                            }
682                        } catch (NumberFormatException ex) {
683                            loge("error parsing GprsRegistrationState: " + ex);
684                        }
685                    }
686                    int dataRegState = regCodeToServiceState(regState);
687                    newSS.setDataRegState(dataRegState);
688                    mDataRoaming = regCodeIsRoaming(regState);
689                    newSS.setRilDataRadioTechnology(type);
690                    if (DBG) {
691                        log("handlPollStateResultMessage: GsmSST setDataRegState=" + dataRegState
692                                + " regState=" + regState
693                                + " dataRadioTechnology=" + type);
694                    }
695                    break;
696                }
697
698                case EVENT_POLL_STATE_OPERATOR: {
699                    String opNames[] = (String[])ar.result;
700
701                    if (opNames != null && opNames.length >= 3) {
702                         newSS.setOperatorName (opNames[0], opNames[1], opNames[2]);
703                    }
704                    break;
705                }
706
707                case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: {
708                    ints = (int[])ar.result;
709                    newSS.setIsManualSelection(ints[0] == 1);
710                    break;
711                }
712            }
713
714        } catch (RuntimeException ex) {
715            loge("Exception while polling service state. Probably malformed RIL response." + ex);
716        }
717
718        pollingContext[0]--;
719
720        if (pollingContext[0] == 0) {
721            /**
722             *  Since the roaming states of gsm service (from +CREG) and
723             *  data service (from +CGREG) could be different, the new SS
724             *  is set roaming while either one is roaming.
725             *
726             *  There is an exception for the above rule. The new SS is not set
727             *  as roaming while gsm service reports roaming but indeed it is
728             *  not roaming between operators.
729             */
730            boolean roaming = (mGsmRoaming || mDataRoaming);
731            if (mGsmRoaming && !isRoamingBetweenOperators(mGsmRoaming, newSS)) {
732                roaming = false;
733            }
734            newSS.setRoaming(roaming);
735            newSS.setEmergencyOnly(mEmergencyOnly);
736            pollStateDone();
737        }
738    }
739
740    private void setSignalStrengthDefaultValues() {
741        mSignalStrength = new SignalStrength(true);
742    }
743
744    /**
745     * A complete "service state" from our perspective is
746     * composed of a handful of separate requests to the radio.
747     *
748     * We make all of these requests at once, but then abandon them
749     * and start over again if the radio notifies us that some
750     * event has changed
751     */
752    private void pollState() {
753        pollingContext = new int[1];
754        pollingContext[0] = 0;
755
756        switch (cm.getRadioState()) {
757            case RADIO_UNAVAILABLE:
758                newSS.setStateOutOfService();
759                newCellLoc.setStateInvalid();
760                setSignalStrengthDefaultValues();
761                mGotCountryCode = false;
762                mNitzUpdatedTime = false;
763                pollStateDone();
764            break;
765
766            case RADIO_OFF:
767                newSS.setStateOff();
768                newCellLoc.setStateInvalid();
769                setSignalStrengthDefaultValues();
770                mGotCountryCode = false;
771                mNitzUpdatedTime = false;
772                pollStateDone();
773            break;
774
775            default:
776                // Issue all poll-related commands at once
777                // then count down the responses, which
778                // are allowed to arrive out-of-order
779
780                pollingContext[0]++;
781                cm.getOperator(
782                    obtainMessage(
783                        EVENT_POLL_STATE_OPERATOR, pollingContext));
784
785                pollingContext[0]++;
786                cm.getDataRegistrationState(
787                    obtainMessage(
788                        EVENT_POLL_STATE_GPRS, pollingContext));
789
790                pollingContext[0]++;
791                cm.getVoiceRegistrationState(
792                    obtainMessage(
793                        EVENT_POLL_STATE_REGISTRATION, pollingContext));
794
795                pollingContext[0]++;
796                cm.getNetworkSelectionMode(
797                    obtainMessage(
798                        EVENT_POLL_STATE_NETWORK_SELECTION_MODE, pollingContext));
799            break;
800        }
801    }
802
803    private void pollStateDone() {
804        if (DBG) {
805            log("Poll ServiceState done: " +
806                " oldSS=[" + ss + "] newSS=[" + newSS + "]" +
807                " oldMaxDataCalls=" + mMaxDataCalls +
808                " mNewMaxDataCalls=" + mNewMaxDataCalls +
809                " oldReasonDataDenied=" + mReasonDataDenied +
810                " mNewReasonDataDenied=" + mNewReasonDataDenied);
811        }
812
813        boolean hasRegistered =
814            ss.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
815            && newSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
816
817        boolean hasDeregistered =
818            ss.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
819            && newSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE;
820
821        boolean hasGprsAttached =
822                ss.getDataRegState() != ServiceState.STATE_IN_SERVICE
823                && newSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
824
825        boolean hasGprsDetached =
826                ss.getDataRegState() == ServiceState.STATE_IN_SERVICE
827                && newSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
828
829        boolean hasRilVoiceRadioTechnologyChanged =
830                ss.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology();
831
832        boolean hasChanged = !newSS.equals(ss);
833
834        boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming();
835
836        boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming();
837
838        boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
839
840        // Add an event log when connection state changes
841        if (ss.getVoiceRegState() != newSS.getVoiceRegState()
842                || ss.getDataRegState() != newSS.getDataRegState()) {
843            EventLog.writeEvent(EventLogTags.GSM_SERVICE_STATE_CHANGE,
844                ss.getVoiceRegState(), ss.getDataRegState(),
845                newSS.getVoiceRegState(), newSS.getDataRegState());
846        }
847
848        ServiceState tss;
849        tss = ss;
850        ss = newSS;
851        newSS = tss;
852        // clean slate for next time
853        newSS.setStateOutOfService();
854
855        GsmCellLocation tcl = cellLoc;
856        cellLoc = newCellLoc;
857        newCellLoc = tcl;
858
859        // Add an event log when network type switched
860        // TODO: we may add filtering to reduce the event logged,
861        // i.e. check preferred network setting, only switch to 2G, etc
862        if (hasRilVoiceRadioTechnologyChanged) {
863            int cid = -1;
864            GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
865            if (loc != null) cid = loc.getCid();
866            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED, cid, ss.getRilVoiceRadioTechnology(),
867                    newSS.getRilVoiceRadioTechnology());
868            if (DBG) {
869                log("RAT switched "
870                        + ServiceState.rilRadioTechnologyToString(ss.getRilVoiceRadioTechnology())
871                        + " -> "
872                        + ServiceState.rilRadioTechnologyToString(newSS.getRilVoiceRadioTechnology()) +
873                        " at cell " + cid);
874            }
875        }
876
877        mReasonDataDenied = mNewReasonDataDenied;
878        mMaxDataCalls = mNewMaxDataCalls;
879
880        newSS.setStateOutOfService(); // clean slate for next time
881
882        if (hasRilVoiceRadioTechnologyChanged) {
883            phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
884                    ServiceState.rilRadioTechnologyToString(ss.getRilVoiceRadioTechnology()));
885        }
886
887        if (hasRegistered) {
888            mNetworkAttachedRegistrants.notifyRegistrants();
889
890            if (DBG) {
891                log("pollStateDone: registering current mNitzUpdatedTime=" +
892                        mNitzUpdatedTime + " changing to false");
893            }
894            mNitzUpdatedTime = false;
895        }
896
897        if (hasChanged) {
898            String operatorNumeric;
899
900            updateSpnDisplay();
901
902            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
903                ss.getOperatorAlphaLong());
904
905            String prevOperatorNumeric =
906                    SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
907            operatorNumeric = ss.getOperatorNumeric();
908            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
909
910            if (operatorNumeric == null) {
911                if (DBG) log("operatorNumeric is null");
912                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
913                mGotCountryCode = false;
914                mNitzUpdatedTime = false;
915            } else {
916                String iso = "";
917                String mcc = "";
918                try{
919                    mcc = operatorNumeric.substring(0, 3);
920                    iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
921                } catch ( NumberFormatException ex){
922                    loge("pollStateDone: countryCodeForMcc error" + ex);
923                } catch ( StringIndexOutOfBoundsException ex) {
924                    loge("pollStateDone: countryCodeForMcc error" + ex);
925                }
926
927                phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
928                mGotCountryCode = true;
929
930                TimeZone zone = null;
931
932                if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso) &&
933                        getAutoTimeZone()) {
934
935                    // Test both paths if ignore nitz is true
936                    boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
937                                TelephonyProperties.PROPERTY_IGNORE_NITZ, false) &&
938                                    ((SystemClock.uptimeMillis() & 1) == 0);
939
940                    ArrayList<TimeZone> uniqueZones = TimeUtils.getTimeZonesWithUniqueOffsets(iso);
941                    if ((uniqueZones.size() == 1) || testOneUniqueOffsetPath) {
942                        zone = uniqueZones.get(0);
943                        if (DBG) {
944                           log("pollStateDone: no nitz but one TZ for iso-cc=" + iso +
945                                   " with zone.getID=" + zone.getID() +
946                                   " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
947                        }
948                        setAndBroadcastNetworkSetTimeZone(zone.getID());
949                    } else {
950                        if (DBG) {
951                            log("pollStateDone: there are " + uniqueZones.size() +
952                                " unique offsets for iso-cc='" + iso +
953                                " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath +
954                                "', do nothing");
955                        }
956                    }
957                }
958
959                if (shouldFixTimeZoneNow(phone, operatorNumeric, prevOperatorNumeric,
960                        mNeedFixZoneAfterNitz)) {
961                    // If the offset is (0, false) and the timezone property
962                    // is set, use the timezone property rather than
963                    // GMT.
964                    String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
965                    if (DBG) {
966                        log("pollStateDone: fix time zone zoneName='" + zoneName +
967                            "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
968                            " iso-cc='" + iso +
969                            "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
970                    }
971
972                    // "(mZoneOffset == 0) && (mZoneDst == false) &&
973                    //  (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)"
974                    // means that we received a NITZ string telling
975                    // it is in GMT+0 w/ DST time zone
976                    // BUT iso tells is NOT, e.g, a wrong NITZ reporting
977                    // local time w/ 0 offset.
978                    if ((mZoneOffset == 0) && (mZoneDst == false) &&
979                        (zoneName != null) && (zoneName.length() > 0) &&
980                        (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) {
981                        zone = TimeZone.getDefault();
982                        if (mNeedFixZoneAfterNitz) {
983                            // For wrong NITZ reporting local time w/ 0 offset,
984                            // need adjust time to reflect default timezone setting
985                            long ctm = System.currentTimeMillis();
986                            long tzOffset = zone.getOffset(ctm);
987                            if (DBG) {
988                                log("pollStateDone: tzOffset=" + tzOffset + " ltod=" +
989                                        TimeUtils.logTimeOfDay(ctm));
990                            }
991                            if (getAutoTime()) {
992                                long adj = ctm - tzOffset;
993                                if (DBG) log("pollStateDone: adj ltod=" +
994                                        TimeUtils.logTimeOfDay(adj));
995                                setAndBroadcastNetworkSetTime(adj);
996                            } else {
997                                // Adjust the saved NITZ time to account for tzOffset.
998                                mSavedTime = mSavedTime - tzOffset;
999                            }
1000                        }
1001                        if (DBG) log("pollStateDone: using default TimeZone");
1002                    } else if (iso.equals("")){
1003                        // Country code not found.  This is likely a test network.
1004                        // Get a TimeZone based only on the NITZ parameters (best guess).
1005                        zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
1006                        if (DBG) log("pollStateDone: using NITZ TimeZone");
1007                    } else {
1008                        zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, iso);
1009                        if (DBG) log("pollStateDone: using getTimeZone(off, dst, time, iso)");
1010                    }
1011
1012                    mNeedFixZoneAfterNitz = false;
1013
1014                    if (zone != null) {
1015                        log("pollStateDone: zone != null zone.getID=" + zone.getID());
1016                        if (getAutoTimeZone()) {
1017                            setAndBroadcastNetworkSetTimeZone(zone.getID());
1018                        }
1019                        saveNitzTimeZone(zone.getID());
1020                    } else {
1021                        log("pollStateDone: zone == null");
1022                    }
1023                }
1024            }
1025
1026            phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
1027                ss.getRoaming() ? "true" : "false");
1028
1029            phone.notifyServiceStateChanged(ss);
1030        }
1031
1032        if (hasGprsAttached) {
1033            mAttachedRegistrants.notifyRegistrants();
1034        }
1035
1036        if (hasGprsDetached) {
1037            mDetachedRegistrants.notifyRegistrants();
1038        }
1039
1040        if (hasRilVoiceRadioTechnologyChanged) {
1041            phone.notifyDataConnection(Phone.REASON_NW_TYPE_CHANGED);
1042        }
1043
1044        if (hasRoamingOn) {
1045            mRoamingOnRegistrants.notifyRegistrants();
1046        }
1047
1048        if (hasRoamingOff) {
1049            mRoamingOffRegistrants.notifyRegistrants();
1050        }
1051
1052        if (hasLocationChanged) {
1053            phone.notifyLocationChanged();
1054        }
1055
1056        if (! isGprsConsistent(ss.getDataRegState(), ss.getVoiceRegState())) {
1057            if (!mStartedGprsRegCheck && !mReportedGprsNoReg) {
1058                mStartedGprsRegCheck = true;
1059
1060                int check_period = Settings.Global.getInt(
1061                        phone.getContext().getContentResolver(),
1062                        Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
1063                        DEFAULT_GPRS_CHECK_PERIOD_MILLIS);
1064                sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS),
1065                        check_period);
1066            }
1067        } else {
1068            mReportedGprsNoReg = false;
1069        }
1070        // TODO: Add GsmCellIdenity updating, see CdmaLteServiceStateTracker.
1071    }
1072
1073    /**
1074     * Check if GPRS got registered while voice is registered.
1075     *
1076     * @param dataRegState, i.e. CGREG in GSM
1077     * @param voiceRegState, i.e. CREG in GSM
1078     * @return false if device only register to voice but not gprs
1079     */
1080    private boolean isGprsConsistent(int dataRegState, int voiceRegState) {
1081        return !((voiceRegState == ServiceState.STATE_IN_SERVICE) &&
1082                (dataRegState != ServiceState.STATE_IN_SERVICE));
1083    }
1084
1085    /**
1086     * Returns a TimeZone object based only on parameters from the NITZ string.
1087     */
1088    private TimeZone getNitzTimeZone(int offset, boolean dst, long when) {
1089        TimeZone guess = findTimeZone(offset, dst, when);
1090        if (guess == null) {
1091            // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
1092            guess = findTimeZone(offset, !dst, when);
1093        }
1094        if (DBG) log("getNitzTimeZone returning " + (guess == null ? guess : guess.getID()));
1095        return guess;
1096    }
1097
1098    private TimeZone findTimeZone(int offset, boolean dst, long when) {
1099        int rawOffset = offset;
1100        if (dst) {
1101            rawOffset -= 3600000;
1102        }
1103        String[] zones = TimeZone.getAvailableIDs(rawOffset);
1104        TimeZone guess = null;
1105        Date d = new Date(when);
1106        for (String zone : zones) {
1107            TimeZone tz = TimeZone.getTimeZone(zone);
1108            if (tz.getOffset(when) == offset &&
1109                tz.inDaylightTime(d) == dst) {
1110                guess = tz;
1111                break;
1112            }
1113        }
1114
1115        return guess;
1116    }
1117
1118    private void queueNextSignalStrengthPoll() {
1119        if (dontPollSignalStrength) {
1120            // The radio is telling us about signal strength changes
1121            // we don't have to ask it
1122            return;
1123        }
1124
1125        Message msg;
1126
1127        msg = obtainMessage();
1128        msg.what = EVENT_POLL_SIGNAL_STRENGTH;
1129
1130        long nextTime;
1131
1132        // TODO Don't poll signal strength if screen is off
1133        sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
1134    }
1135
1136    /**
1137     * Set restricted state based on the OnRestrictedStateChanged notification
1138     * If any voice or packet restricted state changes, trigger a UI
1139     * notification and notify registrants when sim is ready.
1140     *
1141     * @param ar an int value of RIL_RESTRICTED_STATE_*
1142     */
1143    private void onRestrictedStateChanged(AsyncResult ar) {
1144        RestrictedState newRs = new RestrictedState();
1145
1146        if (DBG) log("onRestrictedStateChanged: E rs "+ mRestrictedState);
1147
1148        if (ar.exception == null) {
1149            int[] ints = (int[])ar.result;
1150            int state = ints[0];
1151
1152            newRs.setCsEmergencyRestricted(
1153                    ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) ||
1154                    ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
1155            //ignore the normal call and data restricted state before SIM READY
1156            if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY) {
1157                newRs.setCsNormalRestricted(
1158                        ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) ||
1159                        ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) );
1160                newRs.setPsRestricted(
1161                        (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0);
1162            }
1163
1164            if (DBG) log("onRestrictedStateChanged: new rs "+ newRs);
1165
1166            if (!mRestrictedState.isPsRestricted() && newRs.isPsRestricted()) {
1167                mPsRestrictEnabledRegistrants.notifyRegistrants();
1168                setNotification(PS_ENABLED);
1169            } else if (mRestrictedState.isPsRestricted() && !newRs.isPsRestricted()) {
1170                mPsRestrictDisabledRegistrants.notifyRegistrants();
1171                setNotification(PS_DISABLED);
1172            }
1173
1174            /**
1175             * There are two kind of cs restriction, normal and emergency. So
1176             * there are 4 x 4 combinations in current and new restricted states
1177             * and we only need to notify when state is changed.
1178             */
1179            if (mRestrictedState.isCsRestricted()) {
1180                if (!newRs.isCsRestricted()) {
1181                    // remove all restriction
1182                    setNotification(CS_DISABLED);
1183                } else if (!newRs.isCsNormalRestricted()) {
1184                    // remove normal restriction
1185                    setNotification(CS_EMERGENCY_ENABLED);
1186                } else if (!newRs.isCsEmergencyRestricted()) {
1187                    // remove emergency restriction
1188                    setNotification(CS_NORMAL_ENABLED);
1189                }
1190            } else if (mRestrictedState.isCsEmergencyRestricted() &&
1191                    !mRestrictedState.isCsNormalRestricted()) {
1192                if (!newRs.isCsRestricted()) {
1193                    // remove all restriction
1194                    setNotification(CS_DISABLED);
1195                } else if (newRs.isCsRestricted()) {
1196                    // enable all restriction
1197                    setNotification(CS_ENABLED);
1198                } else if (newRs.isCsNormalRestricted()) {
1199                    // remove emergency restriction and enable normal restriction
1200                    setNotification(CS_NORMAL_ENABLED);
1201                }
1202            } else if (!mRestrictedState.isCsEmergencyRestricted() &&
1203                    mRestrictedState.isCsNormalRestricted()) {
1204                if (!newRs.isCsRestricted()) {
1205                    // remove all restriction
1206                    setNotification(CS_DISABLED);
1207                } else if (newRs.isCsRestricted()) {
1208                    // enable all restriction
1209                    setNotification(CS_ENABLED);
1210                } else if (newRs.isCsEmergencyRestricted()) {
1211                    // remove normal restriction and enable emergency restriction
1212                    setNotification(CS_EMERGENCY_ENABLED);
1213                }
1214            } else {
1215                if (newRs.isCsRestricted()) {
1216                    // enable all restriction
1217                    setNotification(CS_ENABLED);
1218                } else if (newRs.isCsEmergencyRestricted()) {
1219                    // enable emergency restriction
1220                    setNotification(CS_EMERGENCY_ENABLED);
1221                } else if (newRs.isCsNormalRestricted()) {
1222                    // enable normal restriction
1223                    setNotification(CS_NORMAL_ENABLED);
1224                }
1225            }
1226
1227            mRestrictedState = newRs;
1228        }
1229        log("onRestrictedStateChanged: X rs "+ mRestrictedState);
1230    }
1231
1232    /** code is registration state 0-5 from TS 27.007 7.2 */
1233    private int regCodeToServiceState(int code) {
1234        switch (code) {
1235            case 0:
1236            case 2: // 2 is "searching"
1237            case 3: // 3 is "registration denied"
1238            case 4: // 4 is "unknown" no vaild in current baseband
1239            case 10:// same as 0, but indicates that emergency call is possible.
1240            case 12:// same as 2, but indicates that emergency call is possible.
1241            case 13:// same as 3, but indicates that emergency call is possible.
1242            case 14:// same as 4, but indicates that emergency call is possible.
1243                return ServiceState.STATE_OUT_OF_SERVICE;
1244
1245            case 1:
1246                return ServiceState.STATE_IN_SERVICE;
1247
1248            case 5:
1249                // in service, roam
1250                return ServiceState.STATE_IN_SERVICE;
1251
1252            default:
1253                loge("regCodeToServiceState: unexpected service state " + code);
1254                return ServiceState.STATE_OUT_OF_SERVICE;
1255        }
1256    }
1257
1258
1259    /**
1260     * code is registration state 0-5 from TS 27.007 7.2
1261     * returns true if registered roam, false otherwise
1262     */
1263    private boolean regCodeIsRoaming (int code) {
1264        return ServiceState.RIL_REG_STATE_ROAMING == code;
1265    }
1266
1267    /**
1268     * Set roaming state when gsmRoaming is true and, if operator mcc is the
1269     * same as sim mcc, ons is different from spn
1270     * @param gsmRoaming TS 27.007 7.2 CREG registered roaming
1271     * @param s ServiceState hold current ons
1272     * @return true for roaming state set
1273     */
1274    private boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) {
1275        String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
1276
1277        String onsl = s.getOperatorAlphaLong();
1278        String onss = s.getOperatorAlphaShort();
1279
1280        boolean equalsOnsl = onsl != null && spn.equals(onsl);
1281        boolean equalsOnss = onss != null && spn.equals(onss);
1282
1283        String simNumeric = SystemProperties.get(
1284                TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
1285        String  operatorNumeric = s.getOperatorNumeric();
1286
1287        boolean equalsMcc = true;
1288        try {
1289            equalsMcc = simNumeric.substring(0, 3).
1290                    equals(operatorNumeric.substring(0, 3));
1291        } catch (Exception e){
1292        }
1293
1294        return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss));
1295    }
1296
1297    private static int twoDigitsAt(String s, int offset) {
1298        int a, b;
1299
1300        a = Character.digit(s.charAt(offset), 10);
1301        b = Character.digit(s.charAt(offset+1), 10);
1302
1303        if (a < 0 || b < 0) {
1304
1305            throw new RuntimeException("invalid format");
1306        }
1307
1308        return a*10 + b;
1309    }
1310
1311    /**
1312     * @return The current GPRS state. IN_SERVICE is the same as "attached"
1313     * and OUT_OF_SERVICE is the same as detached.
1314     */
1315    @Override
1316    public int getCurrentDataConnectionState() {
1317        return ss.getDataRegState();
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 (ss.getRilVoiceRadioTechnology() >= 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                    Rlog.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.Global.getInt(phone.getContext().getContentResolver(),
1531                    Settings.Global.AUTO_TIME) > 0;
1532        } catch (SettingNotFoundException snfe) {
1533            return true;
1534        }
1535    }
1536
1537    private boolean getAutoTimeZone() {
1538        try {
1539            return Settings.Global.getInt(phone.getContext().getContentResolver(),
1540                    Settings.Global.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().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
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().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1589    }
1590
1591    private void revertToNitzTime() {
1592        if (Settings.Global.getInt(phone.getContext().getContentResolver(),
1593                Settings.Global.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.Global.getInt(phone.getContext().getContentResolver(),
1608                Settings.Global.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 onUpdateIccAvailability() {
1680        if (mUiccController == null ) {
1681            return;
1682        }
1683
1684        UiccCardApplication newUiccApplication =
1685                mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP);
1686
1687        if (mUiccApplcation != newUiccApplication) {
1688            if (mUiccApplcation != null) {
1689                log("Removing stale icc objects.");
1690                mUiccApplcation.unregisterForReady(this);
1691                if (mIccRecords != null) {
1692                    mIccRecords.unregisterForRecordsLoaded(this);
1693                }
1694                mIccRecords = null;
1695                mUiccApplcation = null;
1696            }
1697            if (newUiccApplication != null) {
1698                log("New card found");
1699                mUiccApplcation = newUiccApplication;
1700                mIccRecords = mUiccApplcation.getIccRecords();
1701                mUiccApplcation.registerForReady(this, EVENT_SIM_READY, null);
1702                if (mIccRecords != null) {
1703                    mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
1704                }
1705            }
1706        }
1707    }
1708    @Override
1709    protected void log(String s) {
1710        Rlog.d(LOG_TAG, "[GsmSST] " + s);
1711    }
1712
1713    @Override
1714    protected void loge(String s) {
1715        Rlog.e(LOG_TAG, "[GsmSST] " + s);
1716    }
1717
1718    private static void sloge(String s) {
1719        Rlog.e(LOG_TAG, "[GsmSST] " + s);
1720    }
1721
1722    @Override
1723    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1724        pw.println("GsmServiceStateTracker extends:");
1725        super.dump(fd, pw, args);
1726        pw.println(" phone=" + phone);
1727        pw.println(" ss=" + ss);
1728        pw.println(" newSS=" + newSS);
1729        pw.println(" cellLoc=" + cellLoc);
1730        pw.println(" newCellLoc=" + newCellLoc);
1731        pw.println(" mPreferredNetworkType=" + mPreferredNetworkType);
1732        pw.println(" mMaxDataCalls=" + mMaxDataCalls);
1733        pw.println(" mNewMaxDataCalls=" + mNewMaxDataCalls);
1734        pw.println(" mReasonDataDenied=" + mReasonDataDenied);
1735        pw.println(" mNewReasonDataDenied=" + mNewReasonDataDenied);
1736        pw.println(" mGsmRoaming=" + mGsmRoaming);
1737        pw.println(" mDataRoaming=" + mDataRoaming);
1738        pw.println(" mEmergencyOnly=" + mEmergencyOnly);
1739        pw.println(" mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz);
1740        pw.println(" mZoneOffset=" + mZoneOffset);
1741        pw.println(" mZoneDst=" + mZoneDst);
1742        pw.println(" mZoneTime=" + mZoneTime);
1743        pw.println(" mGotCountryCode=" + mGotCountryCode);
1744        pw.println(" mNitzUpdatedTime=" + mNitzUpdatedTime);
1745        pw.println(" mSavedTimeZone=" + mSavedTimeZone);
1746        pw.println(" mSavedTime=" + mSavedTime);
1747        pw.println(" mSavedAtTime=" + mSavedAtTime);
1748        pw.println(" mStartedGprsRegCheck=" + mStartedGprsRegCheck);
1749        pw.println(" mReportedGprsNoReg=" + mReportedGprsNoReg);
1750        pw.println(" mNotification=" + mNotification);
1751        pw.println(" mWakeLock=" + mWakeLock);
1752        pw.println(" curSpn=" + curSpn);
1753        pw.println(" curShowSpn=" + curShowSpn);
1754        pw.println(" curPlmn=" + curPlmn);
1755        pw.println(" curShowPlmn=" + curShowPlmn);
1756    }
1757}
1758