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