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