ServiceStateTracker.java revision 454b1dfd508844b42eb775e4ab2359be74d3672b
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.os.AsyncResult;
20import android.os.Handler;
21import android.os.Message;
22import android.os.Registrant;
23import android.os.RegistrantList;
24import android.telephony.CellInfo;
25import android.telephony.ServiceState;
26import android.telephony.SignalStrength;
27import android.util.TimeUtils;
28
29import java.io.FileDescriptor;
30import java.io.PrintWriter;
31import java.util.List;
32
33import com.android.internal.telephony.dataconnection.DcTrackerBase;
34import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
35import com.android.internal.telephony.uicc.IccRecords;
36import com.android.internal.telephony.uicc.UiccCardApplication;
37import com.android.internal.telephony.uicc.UiccController;
38
39/**
40 * {@hide}
41 */
42public abstract class ServiceStateTracker extends Handler {
43
44    protected CommandsInterface mCi;
45    protected UiccController mUiccController = null;
46    protected UiccCardApplication mUiccApplcation = null;
47    protected IccRecords mIccRecords = null;
48
49    protected PhoneBase mPhoneBase;
50
51    public ServiceState mSS = new ServiceState();
52    protected ServiceState mNewSS = 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[] mPollingContext;
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 mDontPollSignalStrength = 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        mCi = ci;
188        mUiccController = UiccController.getInstance();
189        mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
190        mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
191    }
192
193    public void dispose() {
194        mCi.unSetOnSignalStrengthUpdate(this);
195        mUiccController.unregisterForIccChanged(this);
196    }
197
198    public boolean getDesiredPowerState() {
199        return mDesiredPowerState;
200    }
201
202    private SignalStrength mLastSignalStrength = null;
203    protected boolean notifySignalStrength() {
204        boolean notified = false;
205        synchronized(mCellInfo) {
206            if (!mSignalStrength.equals(mLastSignalStrength)) {
207                try {
208                    mPhoneBase.notifySignalStrength();
209                    notified = true;
210                } catch (NullPointerException ex) {
211                    loge("updateSignalStrength() Phone already destroyed: " + ex
212                            + "SignalStrength not notified");
213                }
214            }
215        }
216        return notified;
217    }
218
219    /**
220     * Registration point for combined roaming on
221     * combined roaming is true when roaming is true and ONS differs SPN
222     *
223     * @param h handler to notify
224     * @param what what code of message when delivered
225     * @param obj placed in Message.obj
226     */
227    public  void registerForRoamingOn(Handler h, int what, Object obj) {
228        Registrant r = new Registrant(h, what, obj);
229        mRoamingOnRegistrants.add(r);
230
231        if (mSS.getRoaming()) {
232            r.notifyRegistrant();
233        }
234    }
235
236    public  void unregisterForRoamingOn(Handler h) {
237        mRoamingOnRegistrants.remove(h);
238    }
239
240    /**
241     * Registration point for combined roaming off
242     * combined roaming is true when roaming is true and ONS differs SPN
243     *
244     * @param h handler to notify
245     * @param what what code of message when delivered
246     * @param obj placed in Message.obj
247     */
248    public  void registerForRoamingOff(Handler h, int what, Object obj) {
249        Registrant r = new Registrant(h, what, obj);
250        mRoamingOffRegistrants.add(r);
251
252        if (!mSS.getRoaming()) {
253            r.notifyRegistrant();
254        }
255    }
256
257    public  void unregisterForRoamingOff(Handler h) {
258        mRoamingOffRegistrants.remove(h);
259    }
260
261    /**
262     * Re-register network by toggling preferred network type.
263     * This is a work-around to deregister and register network since there is
264     * no ril api to set COPS=2 (deregister) only.
265     *
266     * @param onComplete is dispatched when this is complete.  it will be
267     * an AsyncResult, and onComplete.obj.exception will be non-null
268     * on failure.
269     */
270    public void reRegisterNetwork(Message onComplete) {
271        mCi.getPreferredNetworkType(
272                obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
273    }
274
275    public void
276    setRadioPower(boolean power) {
277        mDesiredPowerState = power;
278
279        setPowerStateToDesired();
280    }
281
282    /**
283     * These two flags manage the behavior of the cell lock -- the
284     * lock should be held if either flag is true.  The intention is
285     * to allow temporary acquisition of the lock to get a single
286     * update.  Such a lock grab and release can thus be made to not
287     * interfere with more permanent lock holds -- in other words, the
288     * lock will only be released if both flags are false, and so
289     * releases by temporary users will only affect the lock state if
290     * there is no continuous user.
291     */
292    private boolean mWantContinuousLocationUpdates;
293    private boolean mWantSingleLocationUpdate;
294
295    public void enableSingleLocationUpdate() {
296        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
297        mWantSingleLocationUpdate = true;
298        mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
299    }
300
301    public void enableLocationUpdates() {
302        if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
303        mWantContinuousLocationUpdates = true;
304        mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
305    }
306
307    protected void disableSingleLocationUpdate() {
308        mWantSingleLocationUpdate = false;
309        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
310            mCi.setLocationUpdates(false, null);
311        }
312    }
313
314    public void disableLocationUpdates() {
315        mWantContinuousLocationUpdates = false;
316        if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
317            mCi.setLocationUpdates(false, null);
318        }
319    }
320
321    @Override
322    public void handleMessage(Message msg) {
323        switch (msg.what) {
324            case EVENT_SET_RADIO_POWER_OFF:
325                synchronized(this) {
326                    if (mPendingRadioPowerOffAfterDataOff &&
327                            (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
328                        if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
329                        hangupAndPowerOff();
330                        mPendingRadioPowerOffAfterDataOffTag += 1;
331                        mPendingRadioPowerOffAfterDataOff = false;
332                    } else {
333                        log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
334                                "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
335                    }
336                }
337                break;
338
339            case EVENT_ICC_CHANGED:
340                onUpdateIccAvailability();
341                break;
342
343            default:
344                log("Unhandled message with number: " + msg.what);
345                break;
346        }
347    }
348
349    protected abstract Phone getPhone();
350    protected abstract void handlePollStateResult(int what, AsyncResult ar);
351    protected abstract void updateSpnDisplay();
352    protected abstract void setPowerStateToDesired();
353    protected abstract void onUpdateIccAvailability();
354    protected abstract void log(String s);
355    protected abstract void loge(String s);
356
357    public abstract int getCurrentDataConnectionState();
358    public abstract boolean isConcurrentVoiceAndDataAllowed();
359
360    /**
361     * Registration point for transition into DataConnection attached.
362     * @param h handler to notify
363     * @param what what code of message when delivered
364     * @param obj placed in Message.obj
365     */
366    public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
367        Registrant r = new Registrant(h, what, obj);
368        mAttachedRegistrants.add(r);
369
370        if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
371            r.notifyRegistrant();
372        }
373    }
374    public void unregisterForDataConnectionAttached(Handler h) {
375        mAttachedRegistrants.remove(h);
376    }
377
378    /**
379     * Registration point for transition into DataConnection detached.
380     * @param h handler to notify
381     * @param what what code of message when delivered
382     * @param obj placed in Message.obj
383     */
384    public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
385        Registrant r = new Registrant(h, what, obj);
386        mDetachedRegistrants.add(r);
387
388        if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
389            r.notifyRegistrant();
390        }
391    }
392    public void unregisterForDataConnectionDetached(Handler h) {
393        mDetachedRegistrants.remove(h);
394    }
395
396    /**
397     * Registration point for transition into network attached.
398     * @param h handler to notify
399     * @param what what code of message when delivered
400     * @param obj in Message.obj
401     */
402    public void registerForNetworkAttached(Handler h, int what, Object obj) {
403        Registrant r = new Registrant(h, what, obj);
404
405        mNetworkAttachedRegistrants.add(r);
406        if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
407            r.notifyRegistrant();
408        }
409    }
410    public void unregisterForNetworkAttached(Handler h) {
411        mNetworkAttachedRegistrants.remove(h);
412    }
413
414    /**
415     * Registration point for transition into packet service restricted zone.
416     * @param h handler to notify
417     * @param what what code of message when delivered
418     * @param obj placed in Message.obj
419     */
420    public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
421        Registrant r = new Registrant(h, what, obj);
422        mPsRestrictEnabledRegistrants.add(r);
423
424        if (mRestrictedState.isPsRestricted()) {
425            r.notifyRegistrant();
426        }
427    }
428
429    public void unregisterForPsRestrictedEnabled(Handler h) {
430        mPsRestrictEnabledRegistrants.remove(h);
431    }
432
433    /**
434     * Registration point for transition out of packet service restricted zone.
435     * @param h handler to notify
436     * @param what what code of message when delivered
437     * @param obj placed in Message.obj
438     */
439    public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
440        Registrant r = new Registrant(h, what, obj);
441        mPsRestrictDisabledRegistrants.add(r);
442
443        if (mRestrictedState.isPsRestricted()) {
444            r.notifyRegistrant();
445        }
446    }
447
448    public void unregisterForPsRestrictedDisabled(Handler h) {
449        mPsRestrictDisabledRegistrants.remove(h);
450    }
451
452    /**
453     * Clean up existing voice and data connection then turn off radio power.
454     *
455     * Hang up the existing voice calls to decrease call drop rate.
456     */
457    public void powerOffRadioSafely(DcTrackerBase dcTracker) {
458        synchronized (this) {
459            if (!mPendingRadioPowerOffAfterDataOff) {
460                // To minimize race conditions we call cleanUpAllConnections on
461                // both if else paths instead of before this isDisconnected test.
462                if (dcTracker.isDisconnected()) {
463                    // To minimize race conditions we do this after isDisconnected
464                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
465                    if (DBG) log("Data disconnected, turn off radio right away.");
466                    hangupAndPowerOff();
467                } else {
468                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
469                    Message msg = Message.obtain(this);
470                    msg.what = EVENT_SET_RADIO_POWER_OFF;
471                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
472                    if (sendMessageDelayed(msg, 30000)) {
473                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
474                        mPendingRadioPowerOffAfterDataOff = true;
475                    } else {
476                        log("Cannot send delayed Msg, turn off radio right away.");
477                        hangupAndPowerOff();
478                    }
479                }
480            }
481        }
482    }
483
484    /**
485     * process the pending request to turn radio off after data is disconnected
486     *
487     * return true if there is pending request to process; false otherwise.
488     */
489    public boolean processPendingRadioPowerOffAfterDataOff() {
490        synchronized(this) {
491            if (mPendingRadioPowerOffAfterDataOff) {
492                if (DBG) log("Process pending request to turn radio off.");
493                mPendingRadioPowerOffAfterDataOffTag += 1;
494                hangupAndPowerOff();
495                mPendingRadioPowerOffAfterDataOff = false;
496                return true;
497            }
498            return false;
499        }
500    }
501
502    /**
503     * send signal-strength-changed notification if changed Called both for
504     * solicited and unsolicited signal strength updates
505     *
506     * @return true if the signal strength changed and a notification was sent.
507     */
508    protected boolean onSignalStrengthResult(AsyncResult ar, boolean isGsm) {
509        SignalStrength oldSignalStrength = mSignalStrength;
510
511        // This signal is used for both voice and data radio signal so parse
512        // all fields
513
514        if ((ar.exception == null) && (ar.result != null)) {
515            mSignalStrength = (SignalStrength) ar.result;
516            mSignalStrength.validateInput();
517            mSignalStrength.setGsm(isGsm);
518        } else {
519            log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
520            mSignalStrength = new SignalStrength(isGsm);
521        }
522
523        return notifySignalStrength();
524    }
525
526    /**
527     * Hang up all voice call and turn off radio. Implemented by derived class.
528     */
529    protected abstract void hangupAndPowerOff();
530
531    /** Cancel a pending (if any) pollState() operation */
532    protected void cancelPollState() {
533        // This will effectively cancel the rest of the poll requests.
534        mPollingContext = new int[1];
535    }
536
537    /**
538     * Return true if time zone needs fixing.
539     *
540     * @param phoneBase
541     * @param operatorNumeric
542     * @param prevOperatorNumeric
543     * @param needToFixTimeZone
544     * @return true if time zone needs to be fixed
545     */
546    protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
547            String prevOperatorNumeric, boolean needToFixTimeZone) {
548        // Return false if the mcc isn't valid as we don't know where we are.
549        // Return true if we have an IccCard and the mcc changed or we
550        // need to fix it because when the NITZ time came in we didn't
551        // know the country code.
552
553        // If mcc is invalid then we'll return false
554        int mcc;
555        try {
556            mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
557        } catch (Exception e) {
558            if (DBG) {
559                log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
560                        " retVal=false");
561            }
562            return false;
563        }
564
565        // If prevMcc is invalid will make it different from mcc
566        // so we'll return true if the card exists.
567        int prevMcc;
568        try {
569            prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
570        } catch (Exception e) {
571            prevMcc = mcc + 1;
572        }
573
574        // Determine if the Icc card exists
575        boolean iccCardExist = false;
576        if (mUiccApplcation != null) {
577            iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
578        }
579
580        // Determine retVal
581        boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
582        if (DBG) {
583            long ctm = System.currentTimeMillis();
584            log("shouldFixTimeZoneNow: retVal=" + retVal +
585                    " iccCardExist=" + iccCardExist +
586                    " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
587                    " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
588                    " needToFixTimeZone=" + needToFixTimeZone +
589                    " ltod=" + TimeUtils.logTimeOfDay(ctm));
590        }
591        return retVal;
592    }
593
594    /**
595     * @return all available cell information or null if none.
596     */
597    public List<CellInfo> getAllCellInfo() {
598        return null;
599    }
600
601    /**
602     * @return signal strength
603     */
604    public SignalStrength getSignalStrength() {
605        synchronized(mCellInfo) {
606            return mSignalStrength;
607        }
608    }
609
610    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
611        pw.println("ServiceStateTracker:");
612        pw.println(" mSS=" + mSS);
613        pw.println(" mNewSS=" + mNewSS);
614        pw.println(" mCellInfo=" + mCellInfo);
615        pw.println(" mRestrictedState=" + mRestrictedState);
616        pw.println(" mPollingContext=" + mPollingContext);
617        pw.println(" mDesiredPowerState=" + mDesiredPowerState);
618        pw.println(" mDontPollSignalStrength=" + mDontPollSignalStrength);
619        pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
620        pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
621    }
622
623    /**
624     * Verifies the current thread is the same as the thread originally
625     * used in the initialization of this instance. Throws RuntimeException
626     * if not.
627     *
628     * @exception RuntimeException if the current thread is not
629     * the thread that originally obtained this PhoneBase instance.
630     */
631    protected void checkCorrectThread() {
632        if (Thread.currentThread() != getLooper().getThread()) {
633            throw new RuntimeException(
634                    "ServiceStateTracker must be used from within one thread");
635        }
636    }
637}
638