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