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