ServiceStateTracker.java revision 88f9756a023639f48e85680b8bf2cd1469a5471a
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;
18
19import android.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Looper;
23import android.os.Message;
24import android.os.Registrant;
25import android.os.RegistrantList;
26import android.os.SystemClock;
27import android.telephony.CellInfo;
28import android.telephony.ServiceState;
29import android.telephony.SignalStrength;
30import android.util.TimeUtils;
31
32import java.io.FileDescriptor;
33import java.io.PrintWriter;
34import java.util.List;
35
36import com.android.internal.telephony.IccCardApplicationStatus.AppState;
37import com.android.internal.telephony.uicc.UiccController;
38
39/**
40 * {@hide}
41 */
42public abstract class ServiceStateTracker extends Handler {
43
44    protected CommandsInterface cm;
45    protected UiccController mUiccController = null;
46    protected UiccCardApplication mUiccApplcation = null;
47    protected IccRecords mIccRecords = null;
48
49    protected PhoneBase mPhoneBase;
50
51    public ServiceState ss = new ServiceState();
52    protected ServiceState newSS = new ServiceState();
53
54    protected CellInfo mLastCellInfo = null;
55
56    // This is final as subclasses alias to a more specific type
57    // so we don't want the reference to change.
58    protected final CellInfo mCellInfo;
59
60    protected SignalStrength mSignalStrength = new SignalStrength();
61
62    // TODO - this should not be public, right now used externally GsmConnetion.
63    public RestrictedState mRestrictedState = new RestrictedState();
64
65    /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
66    static public final int OTASP_UNINITIALIZED = 0;
67    static public final int OTASP_UNKNOWN = 1;
68    static public final int OTASP_NEEDED = 2;
69    static public final int OTASP_NOT_NEEDED = 3;
70
71    /**
72     * A unique identifier to track requests associated with a poll
73     * and ignore stale responses.  The value is a count-down of
74     * expected responses in this pollingContext.
75     */
76    protected int[] pollingContext;
77    protected boolean mDesiredPowerState;
78
79    /**
80     * By default, strength polling is enabled.  However, if we're
81     * getting unsolicited signal strength updates from the radio, set
82     * value to true and don't bother polling any more.
83     */
84    protected boolean dontPollSignalStrength = false;
85
86    protected RegistrantList mRoamingOnRegistrants = new RegistrantList();
87    protected RegistrantList mRoamingOffRegistrants = new RegistrantList();
88    protected RegistrantList mAttachedRegistrants = new RegistrantList();
89    protected RegistrantList mDetachedRegistrants = new RegistrantList();
90    protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
91    protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
92    protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
93
94    /* Radio power off pending flag and tag counter */
95    private boolean mPendingRadioPowerOffAfterDataOff = false;
96    private int mPendingRadioPowerOffAfterDataOffTag = 0;
97
98    protected  static final boolean DBG = true;
99
100    /** Signal strength poll rate. */
101    protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
102
103    /** Waiting period before recheck gprs and voice registration. */
104    public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
105
106    /** GSM events */
107    protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
108    protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
109    protected static final int EVENT_GET_SIGNAL_STRENGTH               = 3;
110    protected static final int EVENT_POLL_STATE_REGISTRATION           = 4;
111    protected static final int EVENT_POLL_STATE_GPRS                   = 5;
112    protected static final int EVENT_POLL_STATE_OPERATOR               = 6;
113    protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
114    protected static final int EVENT_NITZ_TIME                         = 11;
115    protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
116    protected static final int EVENT_RADIO_AVAILABLE                   = 13;
117    protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
118    protected static final int EVENT_GET_LOC_DONE                      = 15;
119    protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
120    protected static final int EVENT_SIM_READY                         = 17;
121    protected static final int EVENT_LOCATION_UPDATES_ENABLED          = 18;
122    protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE        = 19;
123    protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE        = 20;
124    protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE      = 21;
125    protected static final int EVENT_CHECK_REPORT_GPRS                 = 22;
126    protected static final int EVENT_RESTRICTED_STATE_CHANGED          = 23;
127
128    /** CDMA events */
129    protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA      = 24;
130    protected static final int EVENT_POLL_STATE_OPERATOR_CDMA          = 25;
131    protected static final int EVENT_RUIM_READY                        = 26;
132    protected static final int EVENT_RUIM_RECORDS_LOADED               = 27;
133    protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA         = 28;
134    protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA          = 29;
135    protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA        = 30;
136    protected static final int EVENT_GET_LOC_DONE_CDMA                 = 31;
137    //protected static final int EVENT_UNUSED                            = 32;
138    protected static final int EVENT_NV_LOADED                         = 33;
139    protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION      = 34;
140    protected static final int EVENT_NV_READY                          = 35;
141    protected static final int EVENT_ERI_FILE_LOADED                   = 36;
142    protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE       = 37;
143    protected static final int EVENT_SET_RADIO_POWER_OFF               = 38;
144    protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED  = 39;
145    protected static final int EVENT_CDMA_PRL_VERSION_CHANGED          = 40;
146    protected static final int EVENT_RADIO_ON                          = 41;
147    protected static final int EVENT_ICC_CHANGED                       = 42;
148
149    protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
150
151    /**
152     * List of ISO codes for countries that can have an offset of
153     * GMT+0 when not in daylight savings time.  This ignores some
154     * small places such as the Canary Islands (Spain) and
155     * Danmarkshavn (Denmark).  The list must be sorted by code.
156    */
157    protected static final String[] GMT_COUNTRY_CODES = {
158        "bf", // Burkina Faso
159        "ci", // Cote d'Ivoire
160        "eh", // Western Sahara
161        "fo", // Faroe Islands, Denmark
162        "gb", // United Kingdom of Great Britain and Northern Ireland
163        "gh", // Ghana
164        "gm", // Gambia
165        "gn", // Guinea
166        "gw", // Guinea Bissau
167        "ie", // Ireland
168        "lr", // Liberia
169        "is", // Iceland
170        "ma", // Morocco
171        "ml", // Mali
172        "mr", // Mauritania
173        "pt", // Portugal
174        "sl", // Sierra Leone
175        "sn", // Senegal
176        "st", // Sao Tome and Principe
177        "tg", // Togo
178    };
179
180    /** Reason for registration denial. */
181    protected static final String REGISTRATION_DENIED_GEN  = "General";
182    protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
183
184    protected ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo) {
185        mPhoneBase = phoneBase;
186        mCellInfo = cellInfo;
187        cm = ci;
188        mUiccController = UiccController.getInstance();
189        mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
190        cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
191    }
192
193    public void dispose() {
194        cm.unSetOnSignalStrengthUpdate(this);
195    }
196
197    public boolean getDesiredPowerState() {
198        return mDesiredPowerState;
199    }
200
201    private SignalStrength mLastSignalStrength = null;
202    protected boolean notifySignalStrength() {
203        boolean notified = false;
204        synchronized(mCellInfo) {
205            if (!mSignalStrength.equals(mLastSignalStrength)) {
206                try {
207                    mPhoneBase.notifySignalStrength();
208                    notified = true;
209                } catch (NullPointerException ex) {
210                    loge("updateSignalStrength() Phone already destroyed: " + ex
211                            + "SignalStrength not notified");
212                }
213            }
214        }
215        return notified;
216    }
217
218    /**
219     * Registration point for combined roaming on
220     * combined roaming is true when roaming is true and ONS differs SPN
221     *
222     * @param h handler to notify
223     * @param what what code of message when delivered
224     * @param obj placed in Message.obj
225     */
226    public  void registerForRoamingOn(Handler h, int what, Object obj) {
227        Registrant r = new Registrant(h, what, obj);
228        mRoamingOnRegistrants.add(r);
229
230        if (ss.getRoaming()) {
231            r.notifyRegistrant();
232        }
233    }
234
235    public  void unregisterForRoamingOn(Handler h) {
236        mRoamingOnRegistrants.remove(h);
237    }
238
239    /**
240     * Registration point for combined roaming off
241     * combined roaming is true when roaming is true and ONS differs SPN
242     *
243     * @param h handler to notify
244     * @param what what code of message when delivered
245     * @param obj placed in Message.obj
246     */
247    public  void registerForRoamingOff(Handler h, int what, Object obj) {
248        Registrant r = new Registrant(h, what, obj);
249        mRoamingOffRegistrants.add(r);
250
251        if (!ss.getRoaming()) {
252            r.notifyRegistrant();
253        }
254    }
255
256    public  void unregisterForRoamingOff(Handler h) {
257        mRoamingOffRegistrants.remove(h);
258    }
259
260    /**
261     * Re-register network by toggling preferred network type.
262     * This is a work-around to deregister and register network since there is
263     * no ril api to set COPS=2 (deregister) only.
264     *
265     * @param onComplete is dispatched when this is complete.  it will be
266     * an AsyncResult, and onComplete.obj.exception will be non-null
267     * on failure.
268     */
269    public void reRegisterNetwork(Message onComplete) {
270        cm.getPreferredNetworkType(
271                obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
272    }
273
274    public void
275    setRadioPower(boolean power) {
276        mDesiredPowerState = power;
277
278        setPowerStateToDesired();
279    }
280
281    /**
282     * These two flags manage the behavior of the cell lock -- the
283     * lock should be held if either flag is true.  The intention is
284     * to allow temporary acquisition of the lock to get a single
285     * update.  Such a lock grab and release can thus be made to not
286     * interfere with more permanent lock holds -- in other words, the
287     * lock will only be released if both flags are false, and so
288     * releases by temporary users will only affect the lock state if
289     * there is no continuous user.
290     */
291    private boolean mWantContinuousLocationUpdates;
292    private boolean mWantSingleLocationUpdate;
293
294    public void enableSingleLocationUpdate() {
295        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
296        mWantSingleLocationUpdate = true;
297        cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
298    }
299
300    public void enableLocationUpdates() {
301        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
302        mWantContinuousLocationUpdates = true;
303        cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
304    }
305
306    protected void disableSingleLocationUpdate() {
307        mWantSingleLocationUpdate = false;
308        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
309            cm.setLocationUpdates(false, null);
310        }
311    }
312
313    public void disableLocationUpdates() {
314        mWantContinuousLocationUpdates = false;
315        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
316            cm.setLocationUpdates(false, null);
317        }
318    }
319
320    @Override
321    public void handleMessage(Message msg) {
322        switch (msg.what) {
323            case EVENT_SET_RADIO_POWER_OFF:
324                synchronized(this) {
325                    if (mPendingRadioPowerOffAfterDataOff &&
326                            (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
327                        if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
328                        hangupAndPowerOff();
329                        mPendingRadioPowerOffAfterDataOffTag += 1;
330                        mPendingRadioPowerOffAfterDataOff = false;
331                    } else {
332                        log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
333                                "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
334                    }
335                }
336                break;
337
338            case EVENT_ICC_CHANGED:
339                onUpdateIccAvailability();
340                break;
341
342            default:
343                log("Unhandled message with number: " + msg.what);
344                break;
345        }
346    }
347
348    protected abstract Phone getPhone();
349    protected abstract void handlePollStateResult(int what, AsyncResult ar);
350    protected abstract void updateSpnDisplay();
351    protected abstract void setPowerStateToDesired();
352    protected abstract void onUpdateIccAvailability();
353    protected abstract void log(String s);
354    protected abstract void loge(String s);
355
356    public abstract int getCurrentDataConnectionState();
357    public abstract boolean isConcurrentVoiceAndDataAllowed();
358
359    /**
360     * Registration point for transition into DataConnection attached.
361     * @param h handler to notify
362     * @param what what code of message when delivered
363     * @param obj placed in Message.obj
364     */
365    public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
366        Registrant r = new Registrant(h, what, obj);
367        mAttachedRegistrants.add(r);
368
369        if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
370            r.notifyRegistrant();
371        }
372    }
373    public void unregisterForDataConnectionAttached(Handler h) {
374        mAttachedRegistrants.remove(h);
375    }
376
377    /**
378     * Registration point for transition into DataConnection detached.
379     * @param h handler to notify
380     * @param what what code of message when delivered
381     * @param obj placed in Message.obj
382     */
383    public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
384        Registrant r = new Registrant(h, what, obj);
385        mDetachedRegistrants.add(r);
386
387        if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
388            r.notifyRegistrant();
389        }
390    }
391    public void unregisterForDataConnectionDetached(Handler h) {
392        mDetachedRegistrants.remove(h);
393    }
394
395    /**
396     * Registration point for transition into network attached.
397     * @param h handler to notify
398     * @param what what code of message when delivered
399     * @param obj in Message.obj
400     */
401    public void registerForNetworkAttached(Handler h, int what, Object obj) {
402        Registrant r = new Registrant(h, what, obj);
403
404        mNetworkAttachedRegistrants.add(r);
405        if (ss.getVoiceState() == ServiceState.STATE_IN_SERVICE) {
406            r.notifyRegistrant();
407        }
408    }
409    public void unregisterForNetworkAttached(Handler h) {
410        mNetworkAttachedRegistrants.remove(h);
411    }
412
413    /**
414     * Registration point for transition into packet service restricted zone.
415     * @param h handler to notify
416     * @param what what code of message when delivered
417     * @param obj placed in Message.obj
418     */
419    public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
420        Registrant r = new Registrant(h, what, obj);
421        mPsRestrictEnabledRegistrants.add(r);
422
423        if (mRestrictedState.isPsRestricted()) {
424            r.notifyRegistrant();
425        }
426    }
427
428    public void unregisterForPsRestrictedEnabled(Handler h) {
429        mPsRestrictEnabledRegistrants.remove(h);
430    }
431
432    /**
433     * Registration point for transition out of packet service restricted zone.
434     * @param h handler to notify
435     * @param what what code of message when delivered
436     * @param obj placed in Message.obj
437     */
438    public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
439        Registrant r = new Registrant(h, what, obj);
440        mPsRestrictDisabledRegistrants.add(r);
441
442        if (mRestrictedState.isPsRestricted()) {
443            r.notifyRegistrant();
444        }
445    }
446
447    public void unregisterForPsRestrictedDisabled(Handler h) {
448        mPsRestrictDisabledRegistrants.remove(h);
449    }
450
451    /**
452     * Clean up existing voice and data connection then turn off radio power.
453     *
454     * Hang up the existing voice calls to decrease call drop rate.
455     */
456    public void powerOffRadioSafely(DataConnectionTracker dcTracker) {
457        synchronized (this) {
458            if (!mPendingRadioPowerOffAfterDataOff) {
459                // To minimize race conditions we call cleanUpAllConnections on
460                // both if else paths instead of before this isDisconnected test.
461                if (dcTracker.isDisconnected()) {
462                    // To minimize race conditions we do this after isDisconnected
463                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
464                    if (DBG) log("Data disconnected, turn off radio right away.");
465                    hangupAndPowerOff();
466                } else {
467                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
468                    Message msg = Message.obtain(this);
469                    msg.what = EVENT_SET_RADIO_POWER_OFF;
470                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
471                    if (sendMessageDelayed(msg, 30000)) {
472                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
473                        mPendingRadioPowerOffAfterDataOff = true;
474                    } else {
475                        log("Cannot send delayed Msg, turn off radio right away.");
476                        hangupAndPowerOff();
477                    }
478                }
479            }
480        }
481    }
482
483    /**
484     * process the pending request to turn radio off after data is disconnected
485     *
486     * return true if there is pending request to process; false otherwise.
487     */
488    public boolean processPendingRadioPowerOffAfterDataOff() {
489        synchronized(this) {
490            if (mPendingRadioPowerOffAfterDataOff) {
491                if (DBG) log("Process pending request to turn radio off.");
492                mPendingRadioPowerOffAfterDataOffTag += 1;
493                hangupAndPowerOff();
494                mPendingRadioPowerOffAfterDataOff = false;
495                return true;
496            }
497            return false;
498        }
499    }
500
501    /**
502     * send signal-strength-changed notification if changed Called both for
503     * solicited and unsolicited signal strength updates
504     *
505     * @return true if the signal strength changed and a notification was sent.
506     */
507    protected boolean onSignalStrengthResult(AsyncResult ar, boolean isGsm) {
508        SignalStrength oldSignalStrength = mSignalStrength;
509
510        // This signal is used for both voice and data radio signal so parse
511        // all fields
512
513        if ((ar.exception == null) && (ar.result != null)) {
514            mSignalStrength = (SignalStrength) ar.result;
515            mSignalStrength.validateInput();
516            mSignalStrength.setGsm(isGsm);
517        } else {
518            log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
519            mSignalStrength = new SignalStrength(isGsm);
520        }
521
522        return notifySignalStrength();
523    }
524
525    /**
526     * Hang up all voice call and turn off radio. Implemented by derived class.
527     */
528    protected abstract void hangupAndPowerOff();
529
530    /** Cancel a pending (if any) pollState() operation */
531    protected void cancelPollState() {
532        // This will effectively cancel the rest of the poll requests.
533        pollingContext = new int[1];
534    }
535
536    /**
537     * Return true if time zone needs fixing.
538     *
539     * @param phoneBase
540     * @param operatorNumeric
541     * @param prevOperatorNumeric
542     * @param needToFixTimeZone
543     * @return true if time zone needs to be fixed
544     */
545    protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
546            String prevOperatorNumeric, boolean needToFixTimeZone) {
547        // Return false if the mcc isn't valid as we don't know where we are.
548        // Return true if we have an IccCard and the mcc changed or we
549        // need to fix it because when the NITZ time came in we didn't
550        // know the country code.
551
552        // If mcc is invalid then we'll return false
553        int mcc;
554        try {
555            mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
556        } catch (Exception e) {
557            if (DBG) {
558                log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
559                        " retVal=false");
560            }
561            return false;
562        }
563
564        // If prevMcc is invalid will make it different from mcc
565        // so we'll return true if the card exists.
566        int prevMcc;
567        try {
568            prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
569        } catch (Exception e) {
570            prevMcc = mcc + 1;
571        }
572
573        // Determine if the Icc card exists
574        boolean iccCardExist = false;
575        if (mUiccApplcation != null) {
576            iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
577        }
578
579        // Determine retVal
580        boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
581        if (DBG) {
582            long ctm = System.currentTimeMillis();
583            log("shouldFixTimeZoneNow: retVal=" + retVal +
584                    " iccCardExist=" + iccCardExist +
585                    " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
586                    " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
587                    " needToFixTimeZone=" + needToFixTimeZone +
588                    " ltod=" + TimeUtils.logTimeOfDay(ctm));
589        }
590        return retVal;
591    }
592
593    /**
594     * @return all available cell information or null if none.
595     */
596    public List<CellInfo> getAllCellInfo() {
597        return null;
598    }
599
600    /**
601     * @return signal strength
602     */
603    public SignalStrength getSignalStrength() {
604        synchronized(mCellInfo) {
605            return mSignalStrength;
606        }
607    }
608
609    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
610        pw.println("ServiceStateTracker:");
611        pw.println(" ss=" + ss);
612        pw.println(" newSS=" + newSS);
613        pw.println(" mCellInfo=" + mCellInfo);
614        pw.println(" mRestrictedState=" + mRestrictedState);
615        pw.println(" pollingContext=" + pollingContext);
616        pw.println(" mDesiredPowerState=" + mDesiredPowerState);
617        pw.println(" dontPollSignalStrength=" + dontPollSignalStrength);
618        pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
619        pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
620    }
621
622    /**
623     * Verifies the current thread is the same as the thread originally
624     * used in the initialization of this instance. Throws RuntimeException
625     * if not.
626     *
627     * @exception RuntimeException if the current thread is not
628     * the thread that originally obtained this PhoneBase instance.
629     */
630    protected void checkCorrectThread() {
631        if (Thread.currentThread() != getLooper().getThread()) {
632            throw new RuntimeException(
633                    "ServiceStateTracker must be used from within one thread");
634        }
635    }
636}
637