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