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