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