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