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