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