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