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