ServiceStateTracker.java revision ffdf8ce51e37e5e45791c9ea11604aa00dffc88e
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.app.PendingIntent; 20import android.content.Context; 21import android.content.IntentFilter; 22import android.os.AsyncResult; 23import android.os.Handler; 24import android.os.Message; 25import android.os.Registrant; 26import android.os.RegistrantList; 27import android.os.SystemClock; 28import android.os.SystemProperties; 29import android.telephony.CellInfo; 30import android.telephony.Rlog; 31import android.telephony.ServiceState; 32import android.telephony.SignalStrength; 33import android.telephony.SubscriptionManager; 34import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 35import android.telephony.TelephonyManager; 36import android.text.TextUtils; 37import android.util.Pair; 38import android.util.TimeUtils; 39 40import java.io.FileDescriptor; 41import java.io.PrintWriter; 42import java.util.ArrayList; 43import java.util.List; 44 45import com.android.internal.telephony.dataconnection.DcTrackerBase; 46import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; 47import com.android.internal.telephony.uicc.IccRecords; 48import com.android.internal.telephony.uicc.UiccCardApplication; 49import com.android.internal.telephony.uicc.UiccController; 50 51/** 52 * {@hide} 53 */ 54public abstract class ServiceStateTracker extends Handler { 55 private static final String LOG_TAG = "SST"; 56 protected static final boolean DBG = true; 57 protected static final boolean VDBG = false; 58 59 protected static final String PROP_FORCE_ROAMING = "telephony.test.forceRoaming"; 60 61 protected CommandsInterface mCi; 62 protected UiccController mUiccController = null; 63 protected UiccCardApplication mUiccApplcation = null; 64 protected IccRecords mIccRecords = null; 65 66 protected PhoneBase mPhoneBase; 67 68 protected boolean mVoiceCapable; 69 70 public ServiceState mSS = new ServiceState(); 71 protected ServiceState mNewSS = new ServiceState(); 72 73 private static final long LAST_CELL_INFO_LIST_MAX_AGE_MS = 2000; 74 protected long mLastCellInfoListTime; 75 protected List<CellInfo> mLastCellInfoList = null; 76 77 // This is final as subclasses alias to a more specific type 78 // so we don't want the reference to change. 79 protected final CellInfo mCellInfo; 80 81 protected SignalStrength mSignalStrength = new SignalStrength(); 82 83 // TODO - this should not be public, right now used externally GsmConnetion. 84 public RestrictedState mRestrictedState = new RestrictedState(); 85 86 /* The otaspMode passed to PhoneStateListener#onOtaspChanged */ 87 static public final int OTASP_UNINITIALIZED = 0; 88 static public final int OTASP_UNKNOWN = 1; 89 static public final int OTASP_NEEDED = 2; 90 static public final int OTASP_NOT_NEEDED = 3; 91 92 /** 93 * A unique identifier to track requests associated with a poll 94 * and ignore stale responses. The value is a count-down of 95 * expected responses in this pollingContext. 96 */ 97 protected int[] mPollingContext; 98 protected boolean mDesiredPowerState; 99 100 /** 101 * By default, strength polling is enabled. However, if we're 102 * getting unsolicited signal strength updates from the radio, set 103 * value to true and don't bother polling any more. 104 */ 105 protected boolean mDontPollSignalStrength = false; 106 107 protected RegistrantList mVoiceRoamingOnRegistrants = new RegistrantList(); 108 protected RegistrantList mVoiceRoamingOffRegistrants = new RegistrantList(); 109 protected RegistrantList mDataRoamingOnRegistrants = new RegistrantList(); 110 protected RegistrantList mDataRoamingOffRegistrants = new RegistrantList(); 111 protected RegistrantList mAttachedRegistrants = new RegistrantList(); 112 protected RegistrantList mDetachedRegistrants = new RegistrantList(); 113 protected RegistrantList mDataRegStateOrRatChangedRegistrants = new RegistrantList(); 114 protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList(); 115 protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList(); 116 protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList(); 117 118 /* Radio power off pending flag and tag counter */ 119 protected boolean mPendingRadioPowerOffAfterDataOff = false; 120 protected int mPendingRadioPowerOffAfterDataOffTag = 0; 121 122 /** Signal strength poll rate. */ 123 protected static final int POLL_PERIOD_MILLIS = 20 * 1000; 124 125 /** Waiting period before recheck gprs and voice registration. */ 126 public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; 127 128 /** GSM events */ 129 protected static final int EVENT_RADIO_STATE_CHANGED = 1; 130 protected static final int EVENT_NETWORK_STATE_CHANGED = 2; 131 protected static final int EVENT_GET_SIGNAL_STRENGTH = 3; 132 protected static final int EVENT_POLL_STATE_REGISTRATION = 4; 133 protected static final int EVENT_POLL_STATE_GPRS = 5; 134 protected static final int EVENT_POLL_STATE_OPERATOR = 6; 135 protected static final int EVENT_POLL_SIGNAL_STRENGTH = 10; 136 protected static final int EVENT_NITZ_TIME = 11; 137 protected static final int EVENT_SIGNAL_STRENGTH_UPDATE = 12; 138 protected static final int EVENT_RADIO_AVAILABLE = 13; 139 protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14; 140 protected static final int EVENT_GET_LOC_DONE = 15; 141 protected static final int EVENT_SIM_RECORDS_LOADED = 16; 142 protected static final int EVENT_SIM_READY = 17; 143 protected static final int EVENT_LOCATION_UPDATES_ENABLED = 18; 144 protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19; 145 protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20; 146 protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21; 147 protected static final int EVENT_CHECK_REPORT_GPRS = 22; 148 protected static final int EVENT_RESTRICTED_STATE_CHANGED = 23; 149 150 /** CDMA events */ 151 protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA = 24; 152 protected static final int EVENT_POLL_STATE_OPERATOR_CDMA = 25; 153 protected static final int EVENT_RUIM_READY = 26; 154 protected static final int EVENT_RUIM_RECORDS_LOADED = 27; 155 protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA = 28; 156 protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA = 29; 157 protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA = 30; 158 protected static final int EVENT_GET_LOC_DONE_CDMA = 31; 159 //protected static final int EVENT_UNUSED = 32; 160 protected static final int EVENT_NV_LOADED = 33; 161 protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION = 34; 162 protected static final int EVENT_NV_READY = 35; 163 protected static final int EVENT_ERI_FILE_LOADED = 36; 164 protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE = 37; 165 protected static final int EVENT_SET_RADIO_POWER_OFF = 38; 166 protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 39; 167 protected static final int EVENT_CDMA_PRL_VERSION_CHANGED = 40; 168 protected static final int EVENT_RADIO_ON = 41; 169 public static final int EVENT_ICC_CHANGED = 42; 170 protected static final int EVENT_GET_CELL_INFO_LIST = 43; 171 protected static final int EVENT_UNSOL_CELL_INFO_LIST = 44; 172 protected static final int EVENT_CHANGE_IMS_STATE = 45; 173 174 protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; 175 176 /** 177 * List of ISO codes for countries that can have an offset of 178 * GMT+0 when not in daylight savings time. This ignores some 179 * small places such as the Canary Islands (Spain) and 180 * Danmarkshavn (Denmark). The list must be sorted by code. 181 */ 182 protected static final String[] GMT_COUNTRY_CODES = { 183 "bf", // Burkina Faso 184 "ci", // Cote d'Ivoire 185 "eh", // Western Sahara 186 "fo", // Faroe Islands, Denmark 187 "gb", // United Kingdom of Great Britain and Northern Ireland 188 "gh", // Ghana 189 "gm", // Gambia 190 "gn", // Guinea 191 "gw", // Guinea Bissau 192 "ie", // Ireland 193 "lr", // Liberia 194 "is", // Iceland 195 "ma", // Morocco 196 "ml", // Mali 197 "mr", // Mauritania 198 "pt", // Portugal 199 "sl", // Sierra Leone 200 "sn", // Senegal 201 "st", // Sao Tome and Principe 202 "tg", // Togo 203 }; 204 205 private class CellInfoResult { 206 List<CellInfo> list; 207 Object lockObj = new Object(); 208 } 209 210 /** Reason for registration denial. */ 211 protected static final String REGISTRATION_DENIED_GEN = "General"; 212 protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure"; 213 214 protected boolean mImsRegistrationOnOff = false; 215 protected boolean mAlarmSwitch = false; 216 protected IntentFilter mIntentFilter = null; 217 protected PendingIntent mRadioOffIntent = null; 218 protected static final String ACTION_RADIO_OFF = "android.intent.action.ACTION_RADIO_OFF"; 219 protected boolean mPowerOffDelayNeed = true; 220 protected boolean mDeviceShuttingDown = false; 221 222 protected SubscriptionManager mSubscriptionManager; 223 protected final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener = 224 new OnSubscriptionsChangedListener() { 225 /** 226 * Callback invoked when there is any change to any SubscriptionInfo. Typically 227 * this method would invoke {@link SubscriptionManager#getActiveSubscriptionInfoList} 228 */ 229 @Override 230 public void onSubscriptionsChanged() { 231 if (DBG) log("SubscriptionListener.onSubscriptionInfoChanged"); 232 // Set the network type, in case the radio does not restore it. 233 if (mPhoneBase.getSubId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 234 int networkType = PhoneFactory.calculatePreferredNetworkType( 235 mPhoneBase.getContext(), mPhoneBase.getSubId()); 236 mCi.setPreferredNetworkType(networkType, null); 237 } 238 } 239 }; 240 241 protected ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo) { 242 mPhoneBase = phoneBase; 243 mCellInfo = cellInfo; 244 mCi = ci; 245 mVoiceCapable = mPhoneBase.getContext().getResources().getBoolean( 246 com.android.internal.R.bool.config_voice_capable); 247 mUiccController = UiccController.getInstance(); 248 mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null); 249 mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); 250 mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null); 251 252 mSubscriptionManager = SubscriptionManager.from(phoneBase.getContext()); 253 mSubscriptionManager 254 .registerOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); 255 256 mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, 257 ServiceState.rilRadioTechnologyToString(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)); 258 } 259 260 void requestShutdown() { 261 if (mDeviceShuttingDown == true) return; 262 mDeviceShuttingDown = true; 263 mDesiredPowerState = false; 264 setPowerStateToDesired(); 265 } 266 267 public void dispose() { 268 mCi.unSetOnSignalStrengthUpdate(this); 269 mUiccController.unregisterForIccChanged(this); 270 mCi.unregisterForCellInfoList(this); 271 mSubscriptionManager 272 .unregisterOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); 273 } 274 275 public boolean getDesiredPowerState() { 276 return mDesiredPowerState; 277 } 278 279 private SignalStrength mLastSignalStrength = null; 280 protected boolean notifySignalStrength() { 281 boolean notified = false; 282 synchronized(mCellInfo) { 283 if (!mSignalStrength.equals(mLastSignalStrength)) { 284 try { 285 mPhoneBase.notifySignalStrength(); 286 notified = true; 287 } catch (NullPointerException ex) { 288 loge("updateSignalStrength() Phone already destroyed: " + ex 289 + "SignalStrength not notified"); 290 } 291 } 292 } 293 return notified; 294 } 295 296 /** 297 * Notify all mDataConnectionRatChangeRegistrants using an 298 * AsyncResult in msg.obj where AsyncResult#result contains the 299 * new RAT as an Integer Object. 300 */ 301 protected void notifyDataRegStateRilRadioTechnologyChanged() { 302 int rat = mSS.getRilDataRadioTechnology(); 303 int drs = mSS.getDataRegState(); 304 if (DBG) log("notifyDataRegStateRilRadioTechnologyChanged: drs=" + drs + " rat=" + rat); 305 mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, 306 ServiceState.rilRadioTechnologyToString(rat)); 307 mDataRegStateOrRatChangedRegistrants.notifyResult(new Pair<Integer, Integer>(drs, rat)); 308 } 309 310 /** 311 * Some operators have been known to report registration failure 312 * data only devices, to fix that use DataRegState. 313 */ 314 protected void useDataRegStateForDataOnlyDevices() { 315 if (mVoiceCapable == false) { 316 if (DBG) { 317 log("useDataRegStateForDataOnlyDevice: VoiceRegState=" + mNewSS.getVoiceRegState() 318 + " DataRegState=" + mNewSS.getDataRegState()); 319 } 320 // TODO: Consider not lying and instead have callers know the difference. 321 mNewSS.setVoiceRegState(mNewSS.getDataRegState()); 322 } 323 } 324 325 protected void updatePhoneObject() { 326 if (mPhoneBase.getContext().getResources(). 327 getBoolean(com.android.internal.R.bool.config_switch_phone_on_voice_reg_state_change)) { 328 // If the phone is not registered on a network, no need to update. 329 boolean isRegistered = mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE || 330 mSS.getVoiceRegState() == ServiceState.STATE_EMERGENCY_ONLY; 331 if (!isRegistered) { 332 Rlog.d(LOG_TAG, "updatePhoneObject: Ignore update"); 333 return; 334 } 335 mPhoneBase.updatePhoneObject(mSS.getRilVoiceRadioTechnology()); 336 } 337 } 338 339 /** 340 * Registration point for combined roaming on of mobile voice 341 * combined roaming is true when roaming is true and ONS differs SPN 342 * 343 * @param h handler to notify 344 * @param what what code of message when delivered 345 * @param obj placed in Message.obj 346 */ 347 public void registerForVoiceRoamingOn(Handler h, int what, Object obj) { 348 Registrant r = new Registrant(h, what, obj); 349 mVoiceRoamingOnRegistrants.add(r); 350 351 if (mSS.getVoiceRoaming()) { 352 r.notifyRegistrant(); 353 } 354 } 355 356 public void unregisterForVoiceRoamingOn(Handler h) { 357 mVoiceRoamingOnRegistrants.remove(h); 358 } 359 360 /** 361 * Registration point for roaming off of mobile voice 362 * combined roaming is true when roaming is true and ONS differs SPN 363 * 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 registerForVoiceRoamingOff(Handler h, int what, Object obj) { 369 Registrant r = new Registrant(h, what, obj); 370 mVoiceRoamingOffRegistrants.add(r); 371 372 if (!mSS.getVoiceRoaming()) { 373 r.notifyRegistrant(); 374 } 375 } 376 377 public void unregisterForVoiceRoamingOff(Handler h) { 378 mVoiceRoamingOffRegistrants.remove(h); 379 } 380 381 /** 382 * Registration point for combined roaming on of mobile data 383 * combined roaming is true when roaming is true and ONS differs SPN 384 * 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 registerForDataRoamingOn(Handler h, int what, Object obj) { 390 Registrant r = new Registrant(h, what, obj); 391 mDataRoamingOnRegistrants.add(r); 392 393 if (mSS.getDataRoaming()) { 394 r.notifyRegistrant(); 395 } 396 } 397 398 public void unregisterForDataRoamingOn(Handler h) { 399 mDataRoamingOnRegistrants.remove(h); 400 } 401 402 /** 403 * Registration point for roaming off of mobile data 404 * combined roaming is true when roaming is true and ONS differs SPN 405 * 406 * @param h handler to notify 407 * @param what what code of message when delivered 408 * @param obj placed in Message.obj 409 */ 410 public void registerForDataRoamingOff(Handler h, int what, Object obj) { 411 Registrant r = new Registrant(h, what, obj); 412 mDataRoamingOffRegistrants.add(r); 413 414 if (!mSS.getDataRoaming()) { 415 r.notifyRegistrant(); 416 } 417 } 418 419 public void unregisterForDataRoamingOff(Handler h) { 420 mDataRoamingOffRegistrants.remove(h); 421 } 422 423 /** 424 * Re-register network by toggling preferred network type. 425 * This is a work-around to deregister and register network since there is 426 * no ril api to set COPS=2 (deregister) only. 427 * 428 * @param onComplete is dispatched when this is complete. it will be 429 * an AsyncResult, and onComplete.obj.exception will be non-null 430 * on failure. 431 */ 432 public void reRegisterNetwork(Message onComplete) { 433 mCi.getPreferredNetworkType( 434 obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete)); 435 } 436 437 public void 438 setRadioPower(boolean power) { 439 mDesiredPowerState = power; 440 441 setPowerStateToDesired(); 442 } 443 444 /** 445 * These two flags manage the behavior of the cell lock -- the 446 * lock should be held if either flag is true. The intention is 447 * to allow temporary acquisition of the lock to get a single 448 * update. Such a lock grab and release can thus be made to not 449 * interfere with more permanent lock holds -- in other words, the 450 * lock will only be released if both flags are false, and so 451 * releases by temporary users will only affect the lock state if 452 * there is no continuous user. 453 */ 454 private boolean mWantContinuousLocationUpdates; 455 private boolean mWantSingleLocationUpdate; 456 457 public void enableSingleLocationUpdate() { 458 if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return; 459 mWantSingleLocationUpdate = true; 460 mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); 461 } 462 463 public void enableLocationUpdates() { 464 if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return; 465 mWantContinuousLocationUpdates = true; 466 mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); 467 } 468 469 protected void disableSingleLocationUpdate() { 470 mWantSingleLocationUpdate = false; 471 if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) { 472 mCi.setLocationUpdates(false, null); 473 } 474 } 475 476 public void disableLocationUpdates() { 477 mWantContinuousLocationUpdates = false; 478 if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) { 479 mCi.setLocationUpdates(false, null); 480 } 481 } 482 483 @Override 484 public void handleMessage(Message msg) { 485 switch (msg.what) { 486 case EVENT_SET_RADIO_POWER_OFF: 487 synchronized(this) { 488 if (mPendingRadioPowerOffAfterDataOff && 489 (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) { 490 if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now."); 491 hangupAndPowerOff(); 492 mPendingRadioPowerOffAfterDataOffTag += 1; 493 mPendingRadioPowerOffAfterDataOff = false; 494 } else { 495 log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 + 496 "!= tag=" + mPendingRadioPowerOffAfterDataOffTag); 497 } 498 } 499 break; 500 501 case EVENT_ICC_CHANGED: 502 onUpdateIccAvailability(); 503 break; 504 505 case EVENT_GET_CELL_INFO_LIST: { 506 AsyncResult ar = (AsyncResult) msg.obj; 507 CellInfoResult result = (CellInfoResult) ar.userObj; 508 synchronized(result.lockObj) { 509 if (ar.exception != null) { 510 log("EVENT_GET_CELL_INFO_LIST: error ret null, e=" + ar.exception); 511 result.list = null; 512 } else { 513 result.list = (List<CellInfo>) ar.result; 514 515 if (VDBG) { 516 log("EVENT_GET_CELL_INFO_LIST: size=" + result.list.size() 517 + " list=" + result.list); 518 } 519 } 520 mLastCellInfoListTime = SystemClock.elapsedRealtime(); 521 mLastCellInfoList = result.list; 522 result.lockObj.notify(); 523 } 524 break; 525 } 526 527 case EVENT_UNSOL_CELL_INFO_LIST: { 528 AsyncResult ar = (AsyncResult) msg.obj; 529 if (ar.exception != null) { 530 log("EVENT_UNSOL_CELL_INFO_LIST: error ignoring, e=" + ar.exception); 531 } else { 532 List<CellInfo> list = (List<CellInfo>) ar.result; 533 if (DBG) { 534 log("EVENT_UNSOL_CELL_INFO_LIST: size=" + list.size() 535 + " list=" + list); 536 } 537 mLastCellInfoListTime = SystemClock.elapsedRealtime(); 538 mLastCellInfoList = list; 539 mPhoneBase.notifyCellInfo(list); 540 } 541 break; 542 } 543 544 default: 545 log("Unhandled message with number: " + msg.what); 546 break; 547 } 548 } 549 550 protected abstract Phone getPhone(); 551 protected abstract void handlePollStateResult(int what, AsyncResult ar); 552 protected abstract void updateSpnDisplay(); 553 protected abstract void setPowerStateToDesired(); 554 protected abstract void onUpdateIccAvailability(); 555 protected abstract void log(String s); 556 protected abstract void loge(String s); 557 558 public abstract int getCurrentDataConnectionState(); 559 public abstract boolean isConcurrentVoiceAndDataAllowed(); 560 561 public abstract void setImsRegistrationState(boolean registered); 562 public abstract void pollState(); 563 564 /** 565 * Registration point for transition into DataConnection attached. 566 * @param h handler to notify 567 * @param what what code of message when delivered 568 * @param obj placed in Message.obj 569 */ 570 public void registerForDataConnectionAttached(Handler h, int what, Object obj) { 571 Registrant r = new Registrant(h, what, obj); 572 mAttachedRegistrants.add(r); 573 574 if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) { 575 r.notifyRegistrant(); 576 } 577 } 578 public void unregisterForDataConnectionAttached(Handler h) { 579 mAttachedRegistrants.remove(h); 580 } 581 582 /** 583 * Registration point for transition into DataConnection detached. 584 * @param h handler to notify 585 * @param what what code of message when delivered 586 * @param obj placed in Message.obj 587 */ 588 public void registerForDataConnectionDetached(Handler h, int what, Object obj) { 589 Registrant r = new Registrant(h, what, obj); 590 mDetachedRegistrants.add(r); 591 592 if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) { 593 r.notifyRegistrant(); 594 } 595 } 596 public void unregisterForDataConnectionDetached(Handler h) { 597 mDetachedRegistrants.remove(h); 598 } 599 600 /** 601 * Registration for DataConnection RIL Data Radio Technology changing. The 602 * new radio technology will be returned AsyncResult#result as an Integer Object. 603 * The AsyncResult will be in the notification Message#obj. 604 * 605 * @param h handler to notify 606 * @param what what code of message when delivered 607 * @param obj placed in Message.obj 608 */ 609 public void registerForDataRegStateOrRatChanged(Handler h, int what, Object obj) { 610 Registrant r = new Registrant(h, what, obj); 611 mDataRegStateOrRatChangedRegistrants.add(r); 612 notifyDataRegStateRilRadioTechnologyChanged(); 613 } 614 public void unregisterForDataRegStateOrRatChanged(Handler h) { 615 mDataRegStateOrRatChangedRegistrants.remove(h); 616 } 617 618 /** 619 * Registration point for transition into network attached. 620 * @param h handler to notify 621 * @param what what code of message when delivered 622 * @param obj in Message.obj 623 */ 624 public void registerForNetworkAttached(Handler h, int what, Object obj) { 625 Registrant r = new Registrant(h, what, obj); 626 627 mNetworkAttachedRegistrants.add(r); 628 if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) { 629 r.notifyRegistrant(); 630 } 631 } 632 public void unregisterForNetworkAttached(Handler h) { 633 mNetworkAttachedRegistrants.remove(h); 634 } 635 636 /** 637 * Registration point for transition into packet service restricted zone. 638 * @param h handler to notify 639 * @param what what code of message when delivered 640 * @param obj placed in Message.obj 641 */ 642 public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) { 643 Registrant r = new Registrant(h, what, obj); 644 mPsRestrictEnabledRegistrants.add(r); 645 646 if (mRestrictedState.isPsRestricted()) { 647 r.notifyRegistrant(); 648 } 649 } 650 651 public void unregisterForPsRestrictedEnabled(Handler h) { 652 mPsRestrictEnabledRegistrants.remove(h); 653 } 654 655 /** 656 * Registration point for transition out of packet service restricted zone. 657 * @param h handler to notify 658 * @param what what code of message when delivered 659 * @param obj placed in Message.obj 660 */ 661 public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) { 662 Registrant r = new Registrant(h, what, obj); 663 mPsRestrictDisabledRegistrants.add(r); 664 665 if (mRestrictedState.isPsRestricted()) { 666 r.notifyRegistrant(); 667 } 668 } 669 670 public void unregisterForPsRestrictedDisabled(Handler h) { 671 mPsRestrictDisabledRegistrants.remove(h); 672 } 673 674 /** 675 * Clean up existing voice and data connection then turn off radio power. 676 * 677 * Hang up the existing voice calls to decrease call drop rate. 678 */ 679 public void powerOffRadioSafely(DcTrackerBase dcTracker) { 680 synchronized (this) { 681 if (!mPendingRadioPowerOffAfterDataOff) { 682 // In some network, deactivate PDP connection cause releasing of RRC connection, 683 // which MM/IMSI detaching request needs. Without this detaching, network can 684 // not release the network resources previously attached. 685 // So we are avoiding data detaching on these networks. 686 String[] networkNotClearData = mPhoneBase.getContext().getResources() 687 .getStringArray(com.android.internal.R.array.networks_not_clear_data); 688 String currentNetwork = mSS.getOperatorNumeric(); 689 if ((networkNotClearData != null) && (currentNetwork != null)) { 690 for (int i = 0; i < networkNotClearData.length; i++) { 691 if (currentNetwork.equals(networkNotClearData[i])) { 692 // Don't clear data connection for this carrier 693 if (DBG) 694 log("Not disconnecting data for " + currentNetwork); 695 hangupAndPowerOff(); 696 return; 697 } 698 } 699 } 700 // To minimize race conditions we call cleanUpAllConnections on 701 // both if else paths instead of before this isDisconnected test. 702 if (dcTracker.isDisconnected()) { 703 // To minimize race conditions we do this after isDisconnected 704 dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF); 705 if (DBG) log("Data disconnected, turn off radio right away."); 706 hangupAndPowerOff(); 707 } else { 708 dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF); 709 Message msg = Message.obtain(this); 710 msg.what = EVENT_SET_RADIO_POWER_OFF; 711 msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag; 712 if (sendMessageDelayed(msg, 30000)) { 713 if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio."); 714 mPendingRadioPowerOffAfterDataOff = true; 715 } else { 716 log("Cannot send delayed Msg, turn off radio right away."); 717 hangupAndPowerOff(); 718 } 719 } 720 } 721 } 722 } 723 724 /** 725 * process the pending request to turn radio off after data is disconnected 726 * 727 * return true if there is pending request to process; false otherwise. 728 */ 729 public boolean processPendingRadioPowerOffAfterDataOff() { 730 synchronized(this) { 731 if (mPendingRadioPowerOffAfterDataOff) { 732 if (DBG) log("Process pending request to turn radio off."); 733 mPendingRadioPowerOffAfterDataOffTag += 1; 734 hangupAndPowerOff(); 735 mPendingRadioPowerOffAfterDataOff = false; 736 return true; 737 } 738 return false; 739 } 740 } 741 742 /** 743 * send signal-strength-changed notification if changed Called both for 744 * solicited and unsolicited signal strength updates 745 * 746 * @return true if the signal strength changed and a notification was sent. 747 */ 748 protected boolean onSignalStrengthResult(AsyncResult ar, boolean isGsm) { 749 SignalStrength oldSignalStrength = mSignalStrength; 750 751 // This signal is used for both voice and data radio signal so parse 752 // all fields 753 754 if ((ar.exception == null) && (ar.result != null)) { 755 mSignalStrength = (SignalStrength) ar.result; 756 mSignalStrength.validateInput(); 757 mSignalStrength.setGsm(isGsm); 758 } else { 759 log("onSignalStrengthResult() Exception from RIL : " + ar.exception); 760 mSignalStrength = new SignalStrength(isGsm); 761 } 762 763 return notifySignalStrength(); 764 } 765 766 /** 767 * Hang up all voice call and turn off radio. Implemented by derived class. 768 */ 769 protected abstract void hangupAndPowerOff(); 770 771 /** Cancel a pending (if any) pollState() operation */ 772 protected void cancelPollState() { 773 // This will effectively cancel the rest of the poll requests. 774 mPollingContext = new int[1]; 775 } 776 777 /** 778 * Return true if time zone needs fixing. 779 * 780 * @param phoneBase 781 * @param operatorNumeric 782 * @param prevOperatorNumeric 783 * @param needToFixTimeZone 784 * @return true if time zone needs to be fixed 785 */ 786 protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric, 787 String prevOperatorNumeric, boolean needToFixTimeZone) { 788 // Return false if the mcc isn't valid as we don't know where we are. 789 // Return true if we have an IccCard and the mcc changed or we 790 // need to fix it because when the NITZ time came in we didn't 791 // know the country code. 792 793 // If mcc is invalid then we'll return false 794 int mcc; 795 try { 796 mcc = Integer.parseInt(operatorNumeric.substring(0, 3)); 797 } catch (Exception e) { 798 if (DBG) { 799 log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric + 800 " retVal=false"); 801 } 802 return false; 803 } 804 805 // If prevMcc is invalid will make it different from mcc 806 // so we'll return true if the card exists. 807 int prevMcc; 808 try { 809 prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3)); 810 } catch (Exception e) { 811 prevMcc = mcc + 1; 812 } 813 814 // Determine if the Icc card exists 815 boolean iccCardExist = false; 816 if (mUiccApplcation != null) { 817 iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN; 818 } 819 820 // Determine retVal 821 boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone); 822 if (DBG) { 823 long ctm = System.currentTimeMillis(); 824 log("shouldFixTimeZoneNow: retVal=" + retVal + 825 " iccCardExist=" + iccCardExist + 826 " operatorNumeric=" + operatorNumeric + " mcc=" + mcc + 827 " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc + 828 " needToFixTimeZone=" + needToFixTimeZone + 829 " ltod=" + TimeUtils.logTimeOfDay(ctm)); 830 } 831 return retVal; 832 } 833 834 public String getSystemProperty(String property, String defValue) { 835 return TelephonyManager.getTelephonyProperty(mPhoneBase.getPhoneId(), property, defValue); 836 } 837 838 /** 839 * @return all available cell information or null if none. 840 */ 841 public List<CellInfo> getAllCellInfo() { 842 CellInfoResult result = new CellInfoResult(); 843 if (VDBG) log("SST.getAllCellInfo(): E"); 844 int ver = mCi.getRilVersion(); 845 if (ver >= 8) { 846 if (isCallerOnDifferentThread()) { 847 if ((SystemClock.elapsedRealtime() - mLastCellInfoListTime) 848 > LAST_CELL_INFO_LIST_MAX_AGE_MS) { 849 Message msg = obtainMessage(EVENT_GET_CELL_INFO_LIST, result); 850 synchronized(result.lockObj) { 851 result.list = null; 852 mCi.getCellInfoList(msg); 853 try { 854 result.lockObj.wait(5000); 855 } catch (InterruptedException e) { 856 e.printStackTrace(); 857 } 858 } 859 } else { 860 if (DBG) log("SST.getAllCellInfo(): return last, back to back calls"); 861 result.list = mLastCellInfoList; 862 } 863 } else { 864 if (DBG) log("SST.getAllCellInfo(): return last, same thread can't block"); 865 result.list = mLastCellInfoList; 866 } 867 } else { 868 if (DBG) log("SST.getAllCellInfo(): not implemented"); 869 result.list = null; 870 } 871 synchronized(result.lockObj) { 872 if (result.list != null) { 873 if (DBG) log("SST.getAllCellInfo(): X size=" + result.list.size() 874 + " list=" + result.list); 875 return result.list; 876 } else { 877 if (DBG) log("SST.getAllCellInfo(): X size=0 list=null"); 878 return null; 879 } 880 } 881 } 882 883 /** 884 * @return signal strength 885 */ 886 public SignalStrength getSignalStrength() { 887 synchronized(mCellInfo) { 888 return mSignalStrength; 889 } 890 } 891 892 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 893 pw.println("ServiceStateTracker:"); 894 pw.println(" mSS=" + mSS); 895 pw.println(" mNewSS=" + mNewSS); 896 pw.println(" mCellInfo=" + mCellInfo); 897 pw.println(" mRestrictedState=" + mRestrictedState); 898 pw.println(" mPollingContext=" + mPollingContext); 899 pw.println(" mDesiredPowerState=" + mDesiredPowerState); 900 pw.println(" mDontPollSignalStrength=" + mDontPollSignalStrength); 901 pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff); 902 pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag); 903 pw.flush(); 904 } 905 906 /** 907 * Verifies the current thread is the same as the thread originally 908 * used in the initialization of this instance. Throws RuntimeException 909 * if not. 910 * 911 * @exception RuntimeException if the current thread is not 912 * the thread that originally obtained this PhoneBase instance. 913 */ 914 protected void checkCorrectThread() { 915 if (Thread.currentThread() != getLooper().getThread()) { 916 throw new RuntimeException( 917 "ServiceStateTracker must be used from within one thread"); 918 } 919 } 920 921 protected boolean isCallerOnDifferentThread() { 922 boolean value = Thread.currentThread() != getLooper().getThread(); 923 if (VDBG) log("isCallerOnDifferentThread: " + value); 924 return value; 925 } 926 927 protected void updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context) { 928 // if we have a change in operator, notify wifi (even to/from none) 929 if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) || 930 ((newOp != null) && (newOp.equals(oldOp) == false))) { 931 log("update mccmnc=" + newOp + " fromServiceState=true"); 932 MccTable.updateMccMncConfiguration(context, newOp, true); 933 } 934 } 935 936 /** 937 * Check ISO country by MCC to see if phone is roaming in same registered country 938 */ 939 protected boolean inSameCountry(String operatorNumeric) { 940 if (TextUtils.isEmpty(operatorNumeric) || (operatorNumeric.length() < 5)) { 941 // Not a valid network 942 return false; 943 } 944 final String homeNumeric = getHomeOperatorNumeric(); 945 if (TextUtils.isEmpty(homeNumeric) || (homeNumeric.length() < 5)) { 946 // Not a valid SIM MCC 947 return false; 948 } 949 boolean inSameCountry = true; 950 final String networkMCC = operatorNumeric.substring(0, 3); 951 final String homeMCC = homeNumeric.substring(0, 3); 952 final String networkCountry = MccTable.countryCodeForMcc(Integer.parseInt(networkMCC)); 953 final String homeCountry = MccTable.countryCodeForMcc(Integer.parseInt(homeMCC)); 954 if (networkCountry.isEmpty() || homeCountry.isEmpty()) { 955 // Not a valid country 956 return false; 957 } 958 inSameCountry = homeCountry.equals(networkCountry); 959 if (inSameCountry) { 960 return inSameCountry; 961 } 962 // special same country cases 963 if ("us".equals(homeCountry) && "vi".equals(networkCountry)) { 964 inSameCountry = true; 965 } else if ("vi".equals(homeCountry) && "us".equals(networkCountry)) { 966 inSameCountry = true; 967 } 968 return inSameCountry; 969 } 970 971 protected abstract void setRoamingType(ServiceState currentServiceState); 972 973 protected String getHomeOperatorNumeric() { 974 return SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, ""); 975 } 976} 977