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