2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony;
19 import android.os.AsyncResult;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.Message;
23 import android.os.Registrant;
24 import android.os.RegistrantList;
25 import android.os.SystemClock;
26 import android.telephony.CellInfo;
27 import android.telephony.ServiceState;
28 import android.telephony.SignalStrength;
29 import android.util.TimeUtils;
31 import java.io.FileDescriptor;
32 import java.io.PrintWriter;
33 import java.util.List;
35 import com.android.internal.telephony.uicc.UiccController;
40 public abstract class ServiceStateTracker extends Handler {
42 protected CommandsInterface cm;
43 protected UiccController mUiccController = null;
44 protected IccCard mIccCard = null;
45 protected IccRecords mIccRecords = null;
47 protected PhoneBase mPhoneBase;
49 public ServiceState ss = new ServiceState();
50 protected ServiceState newSS = new ServiceState();
52 protected CellInfo mLastCellInfo = null;
54 // This is final as subclasses alias to a more specific type
55 // so we don't want the reference to change.
56 protected final CellInfo mCellInfo;
58 protected SignalStrength mSignalStrength = new SignalStrength();
60 // TODO - this should not be public, right now used externally GsmConnetion.
61 public RestrictedState mRestrictedState = new RestrictedState();
63 /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
64 static public final int OTASP_UNINITIALIZED = 0;
65 static public final int OTASP_UNKNOWN = 1;
66 static public final int OTASP_NEEDED = 2;
67 static public final int OTASP_NOT_NEEDED = 3;
70 * A unique identifier to track requests associated with a poll
71 * and ignore stale responses. The value is a count-down of
72 * expected responses in this pollingContext.
74 protected int[] pollingContext;
75 protected boolean mDesiredPowerState;
78 * Values correspond to ServiceState.RIL_RADIO_TECHNOLOGY_ definitions.
80 protected int mRilRadioTechnology = 0;
81 protected int mNewRilRadioTechnology = 0;
84 * By default, strength polling is enabled. However, if we're
85 * getting unsolicited signal strength updates from the radio, set
86 * value to true and don't bother polling any more.
88 protected boolean dontPollSignalStrength = false;
90 protected RegistrantList mRoamingOnRegistrants = new RegistrantList();
91 protected RegistrantList mRoamingOffRegistrants = new RegistrantList();
92 protected RegistrantList mAttachedRegistrants = new RegistrantList();
93 protected RegistrantList mDetachedRegistrants = new RegistrantList();
94 protected RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
95 protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
96 protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
98 /* Radio power off pending flag and tag counter */
99 private boolean mPendingRadioPowerOffAfterDataOff = false;
100 private int mPendingRadioPowerOffAfterDataOffTag = 0;
102 protected static final boolean DBG = true;
104 /** Signal strength poll rate. */
105 protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
107 /** Waiting period before recheck gprs and voice registration. */
108 public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
111 protected static final int EVENT_RADIO_STATE_CHANGED = 1;
112 protected static final int EVENT_NETWORK_STATE_CHANGED = 2;
113 protected static final int EVENT_GET_SIGNAL_STRENGTH = 3;
114 protected static final int EVENT_POLL_STATE_REGISTRATION = 4;
115 protected static final int EVENT_POLL_STATE_GPRS = 5;
116 protected static final int EVENT_POLL_STATE_OPERATOR = 6;
117 protected static final int EVENT_POLL_SIGNAL_STRENGTH = 10;
118 protected static final int EVENT_NITZ_TIME = 11;
119 protected static final int EVENT_SIGNAL_STRENGTH_UPDATE = 12;
120 protected static final int EVENT_RADIO_AVAILABLE = 13;
121 protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
122 protected static final int EVENT_GET_LOC_DONE = 15;
123 protected static final int EVENT_SIM_RECORDS_LOADED = 16;
124 protected static final int EVENT_SIM_READY = 17;
125 protected static final int EVENT_LOCATION_UPDATES_ENABLED = 18;
126 protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19;
127 protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20;
128 protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21;
129 protected static final int EVENT_CHECK_REPORT_GPRS = 22;
130 protected static final int EVENT_RESTRICTED_STATE_CHANGED = 23;
133 protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA = 24;
134 protected static final int EVENT_POLL_STATE_OPERATOR_CDMA = 25;
135 protected static final int EVENT_RUIM_READY = 26;
136 protected static final int EVENT_RUIM_RECORDS_LOADED = 27;
137 protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA = 28;
138 protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA = 29;
139 protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA = 30;
140 protected static final int EVENT_GET_LOC_DONE_CDMA = 31;
141 //protected static final int EVENT_UNUSED = 32;
142 protected static final int EVENT_NV_LOADED = 33;
143 protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION = 34;
144 protected static final int EVENT_NV_READY = 35;
145 protected static final int EVENT_ERI_FILE_LOADED = 36;
146 protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE = 37;
147 protected static final int EVENT_SET_RADIO_POWER_OFF = 38;
148 protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 39;
149 protected static final int EVENT_CDMA_PRL_VERSION_CHANGED = 40;
150 protected static final int EVENT_RADIO_ON = 41;
151 protected static final int EVENT_ICC_CHANGED = 42;
153 protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
156 * List of ISO codes for countries that can have an offset of
157 * GMT+0 when not in daylight savings time. This ignores some
158 * small places such as the Canary Islands (Spain) and
159 * Danmarkshavn (Denmark). The list must be sorted by code.
161 protected static final String[] GMT_COUNTRY_CODES = {
162 "bf", // Burkina Faso
163 "ci", // Cote d'Ivoire
164 "eh", // Western Sahara
165 "fo", // Faroe Islands, Denmark
166 "gb", // United Kingdom of Great Britain and Northern Ireland
170 "gw", // Guinea Bissau
178 "sl", // Sierra Leone
180 "st", // Sao Tome and Principe
184 /** Reason for registration denial. */
185 protected static final String REGISTRATION_DENIED_GEN = "General";
186 protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
188 protected ServiceStateTracker(PhoneBase phoneBase, CommandsInterface ci, CellInfo cellInfo) {
189 mPhoneBase = phoneBase;
190 mCellInfo = cellInfo;
192 mUiccController = UiccController.getInstance();
193 mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
194 cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
197 public void dispose() {
198 cm.unSetOnSignalStrengthUpdate(this);
201 public boolean getDesiredPowerState() {
202 return mDesiredPowerState;
205 private SignalStrength mLastSignalStrength = null;
206 protected boolean notifySignalStrength() {
207 boolean notified = false;
208 synchronized(mCellInfo) {
209 if (!mSignalStrength.equals(mLastSignalStrength)) {
211 mPhoneBase.notifySignalStrength();
213 } catch (NullPointerException ex) {
214 loge("updateSignalStrength() Phone already destroyed: " + ex
215 + "SignalStrength not notified");
223 * Set the mCellInfo.signalStrength to its default values
225 protected void setSignalStrengthDefaultValues() {
226 setSignalStrengthDefaultValues(mSignalStrength);
230 * Set the signal strength default values
232 protected void setSignalStrengthDefaultValues(SignalStrength signalStrength) {
233 signalStrength.initialize(99, -1, -1, -1, -1, -1, -1,
234 -1, -1, -1, SignalStrength.INVALID_SNR, -1, isGsmSignalStrength());
238 * Return true if this SST is a GSM category device.
240 protected abstract boolean isGsmSignalStrength();
243 * Registration point for combined roaming on
244 * combined roaming is true when roaming is true and ONS differs SPN
246 * @param h handler to notify
247 * @param what what code of message when delivered
248 * @param obj placed in Message.obj
250 public void registerForRoamingOn(Handler h, int what, Object obj) {
251 Registrant r = new Registrant(h, what, obj);
252 mRoamingOnRegistrants.add(r);
254 if (ss.getRoaming()) {
255 r.notifyRegistrant();
259 public void unregisterForRoamingOn(Handler h) {
260 mRoamingOnRegistrants.remove(h);
264 * Registration point for combined roaming off
265 * combined roaming is true when roaming is true and ONS differs SPN
267 * @param h handler to notify
268 * @param what what code of message when delivered
269 * @param obj placed in Message.obj
271 public void registerForRoamingOff(Handler h, int what, Object obj) {
272 Registrant r = new Registrant(h, what, obj);
273 mRoamingOffRegistrants.add(r);
275 if (!ss.getRoaming()) {
276 r.notifyRegistrant();
280 public void unregisterForRoamingOff(Handler h) {
281 mRoamingOffRegistrants.remove(h);
285 * Re-register network by toggling preferred network type.
286 * This is a work-around to deregister and register network since there is
287 * no ril api to set COPS=2 (deregister) only.
289 * @param onComplete is dispatched when this is complete. it will be
290 * an AsyncResult, and onComplete.obj.exception will be non-null
293 public void reRegisterNetwork(Message onComplete) {
294 cm.getPreferredNetworkType(
295 obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
299 setRadioPower(boolean power) {
300 mDesiredPowerState = power;
302 setPowerStateToDesired();
306 * These two flags manage the behavior of the cell lock -- the
307 * lock should be held if either flag is true. The intention is
308 * to allow temporary acquisition of the lock to get a single
309 * update. Such a lock grab and release can thus be made to not
310 * interfere with more permanent lock holds -- in other words, the
311 * lock will only be released if both flags are false, and so
312 * releases by temporary users will only affect the lock state if
313 * there is no continuous user.
315 private boolean mWantContinuousLocationUpdates;
316 private boolean mWantSingleLocationUpdate;
318 public void enableSingleLocationUpdate() {
319 if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
320 mWantSingleLocationUpdate = true;
321 cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
324 public void enableLocationUpdates() {
325 if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
326 mWantContinuousLocationUpdates = true;
327 cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
330 protected void disableSingleLocationUpdate() {
331 mWantSingleLocationUpdate = false;
332 if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
333 cm.setLocationUpdates(false, null);
337 public void disableLocationUpdates() {
338 mWantContinuousLocationUpdates = false;
339 if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
340 cm.setLocationUpdates(false, null);
345 public void handleMessage(Message msg) {
347 case EVENT_SET_RADIO_POWER_OFF:
349 if (mPendingRadioPowerOffAfterDataOff &&
350 (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
351 if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
353 mPendingRadioPowerOffAfterDataOffTag += 1;
354 mPendingRadioPowerOffAfterDataOff = false;
356 log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
357 "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
362 case EVENT_ICC_CHANGED:
363 onUpdateIccAvailability();
367 log("Unhandled message with number: " + msg.what);
372 protected abstract Phone getPhone();
373 protected abstract void handlePollStateResult(int what, AsyncResult ar);
374 protected abstract void updateSpnDisplay();
375 protected abstract void setPowerStateToDesired();
376 protected abstract void onUpdateIccAvailability();
377 protected abstract void log(String s);
378 protected abstract void loge(String s);
380 public abstract int getCurrentDataConnectionState();
381 public abstract boolean isConcurrentVoiceAndDataAllowed();
384 * Registration point for transition into DataConnection attached.
385 * @param h handler to notify
386 * @param what what code of message when delivered
387 * @param obj placed in Message.obj
389 public void registerForDataConnectionAttached(Handler h, int what, Object obj) {
390 Registrant r = new Registrant(h, what, obj);
391 mAttachedRegistrants.add(r);
393 if (getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
394 r.notifyRegistrant();
397 public void unregisterForDataConnectionAttached(Handler h) {
398 mAttachedRegistrants.remove(h);
402 * Registration point for transition into DataConnection detached.
403 * @param h handler to notify
404 * @param what what code of message when delivered
405 * @param obj placed in Message.obj
407 public void registerForDataConnectionDetached(Handler h, int what, Object obj) {
408 Registrant r = new Registrant(h, what, obj);
409 mDetachedRegistrants.add(r);
411 if (getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
412 r.notifyRegistrant();
415 public void unregisterForDataConnectionDetached(Handler h) {
416 mDetachedRegistrants.remove(h);
420 * Registration point for transition into network attached.
421 * @param h handler to notify
422 * @param what what code of message when delivered
423 * @param obj in Message.obj
425 public void registerForNetworkAttached(Handler h, int what, Object obj) {
426 Registrant r = new Registrant(h, what, obj);
428 mNetworkAttachedRegistrants.add(r);
429 if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
430 r.notifyRegistrant();
433 public void unregisterForNetworkAttached(Handler h) {
434 mNetworkAttachedRegistrants.remove(h);
438 * Registration point for transition into packet service restricted zone.
439 * @param h handler to notify
440 * @param what what code of message when delivered
441 * @param obj placed in Message.obj
443 public void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
444 Registrant r = new Registrant(h, what, obj);
445 mPsRestrictEnabledRegistrants.add(r);
447 if (mRestrictedState.isPsRestricted()) {
448 r.notifyRegistrant();
452 public void unregisterForPsRestrictedEnabled(Handler h) {
453 mPsRestrictEnabledRegistrants.remove(h);
457 * Registration point for transition out of packet service restricted zone.
458 * @param h handler to notify
459 * @param what what code of message when delivered
460 * @param obj placed in Message.obj
462 public void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
463 Registrant r = new Registrant(h, what, obj);
464 mPsRestrictDisabledRegistrants.add(r);
466 if (mRestrictedState.isPsRestricted()) {
467 r.notifyRegistrant();
471 public void unregisterForPsRestrictedDisabled(Handler h) {
472 mPsRestrictDisabledRegistrants.remove(h);
476 * Clean up existing voice and data connection then turn off radio power.
478 * Hang up the existing voice calls to decrease call drop rate.
480 public void powerOffRadioSafely(DataConnectionTracker dcTracker) {
481 synchronized (this) {
482 if (!mPendingRadioPowerOffAfterDataOff) {
483 // To minimize race conditions we call cleanUpAllConnections on
484 // both if else paths instead of before this isDisconnected test.
485 if (dcTracker.isDisconnected()) {
486 // To minimize race conditions we do this after isDisconnected
487 dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
488 if (DBG) log("Data disconnected, turn off radio right away.");
491 dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
492 Message msg = Message.obtain(this);
493 msg.what = EVENT_SET_RADIO_POWER_OFF;
494 msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
495 if (sendMessageDelayed(msg, 30000)) {
496 if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
497 mPendingRadioPowerOffAfterDataOff = true;
499 log("Cannot send delayed Msg, turn off radio right away.");
508 * process the pending request to turn radio off after data is disconnected
510 * return true if there is pending request to process; false otherwise.
512 public boolean processPendingRadioPowerOffAfterDataOff() {
514 if (mPendingRadioPowerOffAfterDataOff) {
515 if (DBG) log("Process pending request to turn radio off.");
516 mPendingRadioPowerOffAfterDataOffTag += 1;
518 mPendingRadioPowerOffAfterDataOff = false;
526 * Hang up all voice call and turn off radio. Implemented by derived class.
528 protected abstract void hangupAndPowerOff();
530 /** Cancel a pending (if any) pollState() operation */
531 protected void cancelPollState() {
532 // This will effectively cancel the rest of the poll requests.
533 pollingContext = new int[1];
537 * Return true if time zone needs fixing.
540 * @param operatorNumeric
541 * @param prevOperatorNumeric
542 * @param needToFixTimeZone
543 * @return true if time zone needs to be fixed
545 protected boolean shouldFixTimeZoneNow(PhoneBase phoneBase, String operatorNumeric,
546 String prevOperatorNumeric, boolean needToFixTimeZone) {
547 // Return false if the mcc isn't valid as we don't know where we are.
548 // Return true if we have an IccCard and the mcc changed or we
549 // need to fix it because when the NITZ time came in we didn't
550 // know the country code.
552 // If mcc is invalid then we'll return false
555 mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
556 } catch (Exception e) {
558 log("shouldFixTimeZoneNow: no mcc, operatorNumeric=" + operatorNumeric +
564 // If prevMcc is invalid will make it different from mcc
565 // so we'll return true if the card exists.
568 prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
569 } catch (Exception e) {
573 // Determine if the Icc card exists
574 IccCard iccCard = phoneBase.getIccCard();
575 boolean iccCardExist = (iccCard != null) && iccCard.getState().iccCardExist();
578 boolean retVal = ((iccCardExist && (mcc != prevMcc)) || needToFixTimeZone);
580 long ctm = System.currentTimeMillis();
581 log("shouldFixTimeZoneNow: retVal=" + retVal +
582 " iccCard=" + iccCard +
583 " iccCard.state=" + (iccCard == null ? "null" : iccCard.getState().toString()) +
584 " iccCardExist=" + iccCardExist +
585 " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
586 " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
587 " needToFixTimeZone=" + needToFixTimeZone +
588 " ltod=" + TimeUtils.logTimeOfDay(ctm));
594 * @return all available cell information or null if none.
596 public List<CellInfo> getAllCellInfo() {
601 * @return signal strength
603 public SignalStrength getSignalStrength() {
604 synchronized(mCellInfo) {
605 return mSignalStrength;
609 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
610 pw.println("ServiceStateTracker:");
611 pw.println(" ss=" + ss);
612 pw.println(" newSS=" + newSS);
613 pw.println(" mCellInfo=" + mCellInfo);
614 pw.println(" mRestrictedState=" + mRestrictedState);
615 pw.println(" pollingContext=" + pollingContext);
616 pw.println(" mDesiredPowerState=" + mDesiredPowerState);
617 pw.println(" mRilRadioTechnology=" + mRilRadioTechnology);
618 pw.println(" mNewRilRadioTechnology=" + mNewRilRadioTechnology);
619 pw.println(" dontPollSignalStrength=" + dontPollSignalStrength);
620 pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
621 pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
625 * Verifies the current thread is the same as the thread originally
626 * used in the initialization of this instance. Throws RuntimeException
629 * @exception RuntimeException if the current thread is not
630 * the thread that originally obtained this PhoneBase instance.
632 protected void checkCorrectThread() {
633 if (Thread.currentThread() != getLooper().getThread()) {
634 throw new RuntimeException(
635 "ServiceStateTracker must be used from within one thread");