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