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