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