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.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.BroadcastReceiver; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.SharedPreferences; 27 import android.database.ContentObserver; 28 import android.net.LinkCapabilities; 29 import android.net.LinkProperties; 30 import android.net.NetworkInfo; 31 import android.net.TrafficStats; 32 import android.net.wifi.WifiManager; 33 import android.os.AsyncResult; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.Message; 37 import android.os.Messenger; 38 import android.os.SystemClock; 39 import android.os.SystemProperties; 40 import android.preference.PreferenceManager; 41 import android.provider.Settings; 42 import android.provider.Settings.SettingNotFoundException; 43 import android.telephony.ServiceState; 44 import android.telephony.TelephonyManager; 45 import android.text.TextUtils; 46 import android.util.EventLog; 47 import android.util.Log; 48 49 import com.android.internal.R; 50 import com.android.internal.telephony.DataConnection.FailCause; 51 import com.android.internal.telephony.DctConstants; 52 import com.android.internal.telephony.uicc.UiccController; 53 import com.android.internal.util.AsyncChannel; 54 55 import java.io.FileDescriptor; 56 import java.io.PrintWriter; 57 import java.util.ArrayList; 58 import java.util.HashMap; 59 import java.util.Map.Entry; 60 import java.util.Set; 61 import java.util.concurrent.ConcurrentHashMap; 62 import java.util.concurrent.atomic.AtomicInteger; 63 import java.util.concurrent.atomic.AtomicReference; 64 65 /** 66 * {@hide} 67 */ 68 public abstract class DataConnectionTracker extends Handler { 69 protected static final boolean DBG = true; 70 protected static final boolean VDBG = false; 71 protected static final boolean RADIO_TESTS = false; 72 73 /** 74 * Constants for the data connection activity: 75 * physical link down/up 76 */ 77 protected static final int DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE = 0; 78 protected static final int DATA_CONNECTION_ACTIVE_PH_LINK_DOWN = 1; 79 protected static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2; 80 81 /** Delay between APN attempts. 82 Note the property override mechanism is there just for testing purpose only. */ 83 protected static final int APN_DELAY_MILLIS = 84 SystemProperties.getInt("persist.radio.apn_delay", 5000); 85 86 protected Object mDataEnabledLock = new Object(); 87 88 // responds to the setInternalDataEnabled call - used internally to turn off data 89 // for example during emergency calls 90 protected boolean mInternalDataEnabled = true; 91 92 // responds to public (user) API to enable/disable data use 93 // independent of mInternalDataEnabled and requests for APN access 94 // persisted 95 protected boolean mUserDataEnabled = true; 96 97 // TODO: move away from static state once 5587429 is fixed. 98 protected static boolean sPolicyDataEnabled = true; 99 100 private boolean[] dataEnabled = new boolean[DctConstants.APN_NUM_TYPES]; 101 102 private int enabledCount = 0; 103 104 /* Currently requested APN type (TODO: This should probably be a parameter not a member) */ 105 protected String mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT; 106 107 /** Retry configuration: A doubling of retry times from 5secs to 30minutes */ 108 protected static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000," 109 + "5000,10000,20000,40000,80000:5000,160000:5000," 110 + "320000:5000,640000:5000,1280000:5000,1800000:5000"; 111 112 /** Retry configuration for secondary networks: 4 tries in 20 sec */ 113 protected static final String SECONDARY_DATA_RETRY_CONFIG = 114 "max_retries=3, 5000, 5000, 5000"; 115 116 /** Slow poll when attempting connection recovery. */ 117 protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000; 118 /** Default max failure count before attempting to network re-registration. */ 119 protected static final int DEFAULT_MAX_PDP_RESET_FAIL = 3; 120 121 /** 122 * After detecting a potential connection problem, this is the max number 123 * of subsequent polls before attempting recovery. 124 */ 125 protected static final int NO_RECV_POLL_LIMIT = 24; 126 // 1 sec. default polling interval when screen is on. 127 protected static final int POLL_NETSTAT_MILLIS = 1000; 128 // 10 min. default polling interval when screen is off. 129 protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10; 130 // 2 min for round trip time 131 protected static final int POLL_LONGEST_RTT = 120 * 1000; 132 // Default sent packets without ack which triggers initial recovery steps 133 protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10; 134 // how long to wait before switching back to default APN 135 protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000; 136 // system property that can override the above value 137 protected static final String APN_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; 138 // represents an invalid IP address 139 protected static final String NULL_IP = "0.0.0.0"; 140 141 // Default for the data stall alarm while non-aggressive stall detection 142 protected static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6; 143 // Default for the data stall alarm for aggressive stall detection 144 protected static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60; 145 // If attempt is less than this value we're doing first level recovery 146 protected static final int DATA_STALL_NO_RECV_POLL_LIMIT = 1; 147 // Tag for tracking stale alarms 148 protected static final String DATA_STALL_ALARM_TAG_EXTRA = "data.stall.alram.tag"; 149 150 protected static final boolean DATA_STALL_SUSPECTED = true; 151 protected static final boolean DATA_STALL_NOT_SUSPECTED = false; 152 153 protected String RADIO_RESET_PROPERTY = "gsm.radioreset"; 154 155 156 // TODO: See if we can remove INTENT_RECONNECT_ALARM 157 // having to have different values for GSM and 158 // CDMA. If so we can then remove the need for 159 // getActionIntentReconnectAlarm. 160 protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = 161 "reconnect_alarm_extra_reason"; 162 163 // Used for debugging. Send the INTENT with an optional counter value with the number 164 // of times the setup is to fail before succeeding. If the counter isn't passed the 165 // setup will fail once. Example fail two times with FailCause.SIGNAL_LOST(-3) 166 // adb shell am broadcast \ 167 // -a com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter \ 168 // --ei fail_data_setup_counter 3 --ei fail_data_setup_fail_cause -3 169 protected static final String INTENT_SET_FAIL_DATA_SETUP_COUNTER = 170 "com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter"; 171 protected static final String FAIL_DATA_SETUP_COUNTER = "fail_data_setup_counter"; 172 protected int mFailDataSetupCounter = 0; 173 protected static final String FAIL_DATA_SETUP_FAIL_CAUSE = "fail_data_setup_fail_cause"; 174 protected FailCause mFailDataSetupFailCause = FailCause.ERROR_UNSPECIFIED; 175 176 protected static final String DEFALUT_DATA_ON_BOOT_PROP = "net.def_data_on_boot"; 177 178 // member variables 179 protected PhoneBase mPhone; 180 protected UiccController mUiccController; 181 protected AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>(); 182 protected DctConstants.Activity mActivity = DctConstants.Activity.NONE; 183 protected DctConstants.State mState = DctConstants.State.IDLE; 184 protected Handler mDataConnectionTracker = null; 185 186 protected long mTxPkts; 187 protected long mRxPkts; 188 protected int mNetStatPollPeriod; 189 protected boolean mNetStatPollEnabled = false; 190 191 protected TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0); 192 // Used to track stale data stall alarms. 193 protected int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime(); 194 // The current data stall alarm intent 195 protected PendingIntent mDataStallAlarmIntent = null; 196 // Number of packets sent since the last received packet 197 protected long mSentSinceLastRecv; 198 // Controls when a simple recovery attempt it to be tried 199 protected int mNoRecvPollCount = 0; 200 201 // wifi connection status will be updated by sticky intent 202 protected boolean mIsWifiConnected = false; 203 204 /** Intent sent when the reconnect alarm fires. */ 205 protected PendingIntent mReconnectIntent = null; 206 207 /** CID of active data connection */ 208 protected int mCidActive; 209 210 // When false we will not auto attach and manually attaching is required. 211 protected boolean mAutoAttachOnCreation = false; 212 213 // State of screen 214 // (TODO: Reconsider tying directly to screen, maybe this is 215 // really a lower power mode") 216 protected boolean mIsScreenOn = true; 217 218 /** Allows the generation of unique Id's for DataConnection objects */ 219 protected AtomicInteger mUniqueIdGenerator = new AtomicInteger(0); 220 221 /** The data connections. */ 222 protected HashMap<Integer, DataConnection> mDataConnections = 223 new HashMap<Integer, DataConnection>(); 224 225 /** The data connection async channels */ 226 protected HashMap<Integer, DataConnectionAc> mDataConnectionAsyncChannels = 227 new HashMap<Integer, DataConnectionAc>(); 228 229 /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */ 230 protected HashMap<String, Integer> mApnToDataConnectionId = 231 new HashMap<String, Integer>(); 232 233 /** Phone.APN_TYPE_* ===> ApnContext */ 234 protected ConcurrentHashMap<String, ApnContext> mApnContexts = 235 new ConcurrentHashMap<String, ApnContext>(); 236 237 /* Currently active APN */ 238 protected ApnSetting mActiveApn; 239 240 /** allApns holds all apns */ 241 protected ArrayList<ApnSetting> mAllApns = null; 242 243 /** preferred apn */ 244 protected ApnSetting mPreferredApn = null; 245 246 /** Is packet service restricted by network */ 247 protected boolean mIsPsRestricted = false; 248 249 /* Once disposed dont handle any messages */ 250 protected boolean mIsDisposed = false; 251 252 protected ContentResolver mResolver; 253 254 protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver () 255 { 256 @Override 257 public void onReceive(Context context, Intent intent) 258 { 259 String action = intent.getAction(); 260 if (DBG) log("onReceive: action=" + action); 261 if (action.equals(Intent.ACTION_SCREEN_ON)) { 262 mIsScreenOn = true; 263 stopNetStatPoll(); 264 startNetStatPoll(); 265 restartDataStallAlarm(); 266 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 267 mIsScreenOn = false; 268 stopNetStatPoll(); 269 startNetStatPoll(); 270 restartDataStallAlarm(); 271 } else if (action.startsWith(getActionIntentReconnectAlarm())) { 272 log("Reconnect alarm. Previous state was " + mState); 273 onActionIntentReconnectAlarm(intent); 274 } else if (action.equals(getActionIntentDataStallAlarm())) { 275 onActionIntentDataStallAlarm(intent); 276 } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 277 final android.net.NetworkInfo networkInfo = (NetworkInfo) 278 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 279 mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); 280 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 281 final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 282 WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; 283 284 if (!enabled) { 285 // when WiFi got disabled, the NETWORK_STATE_CHANGED_ACTION 286 // quit and won't report disconnected until next enabling. 287 mIsWifiConnected = false; 288 } 289 } else if (action.equals(INTENT_SET_FAIL_DATA_SETUP_COUNTER)) { 290 mFailDataSetupCounter = intent.getIntExtra(FAIL_DATA_SETUP_COUNTER, 1); 291 mFailDataSetupFailCause = FailCause.fromInt( 292 intent.getIntExtra(FAIL_DATA_SETUP_FAIL_CAUSE, 293 FailCause.ERROR_UNSPECIFIED.getErrorCode())); 294 if (DBG) log("set mFailDataSetupCounter=" + mFailDataSetupCounter + 295 " mFailDataSetupFailCause=" + mFailDataSetupFailCause); 296 } 297 } 298 }; 299 300 private final DataRoamingSettingObserver mDataRoamingSettingObserver; 301 private Runnable mPollNetStat = new Runnable() 302 { 303 @Override 304 public void run() { 305 updateDataActivity(); 306 307 if (mIsScreenOn) { 308 mNetStatPollPeriod = Settings.Global.getInt(mResolver, 309 Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); 310 } else { 311 mNetStatPollPeriod = Settings.Global.getInt(mResolver, 312 Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, 313 POLL_NETSTAT_SCREEN_OFF_MILLIS); 314 } 315 316 if (mNetStatPollEnabled) { 317 mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod); 318 } 319 } 320 }; 321 322 private class DataRoamingSettingObserver extends ContentObserver { 323 public DataRoamingSettingObserver(Handler handler) { 324 super(handler); 325 } 326 327 public void register(Context context) { 328 final ContentResolver resolver = context.getContentResolver(); 329 resolver.registerContentObserver( 330 Settings.Global.getUriFor(Settings.Global.DATA_ROAMING), false, this); 331 } 332 333 public void unregister(Context context) { 334 final ContentResolver resolver = context.getContentResolver(); 335 resolver.unregisterContentObserver(this); 336 } 337 338 @Override 339 public void onChange(boolean selfChange) { 340 // already running on mPhone handler thread 341 handleDataOnRoamingChange(); 342 } 343 } 344 345 /** 346 * Maintian the sum of transmit and receive packets. 347 * 348 * The packet counts are initizlied and reset to -1 and 349 * remain -1 until they can be updated. 350 */ 351 public class TxRxSum { 352 public long txPkts; 353 public long rxPkts; 354 355 public TxRxSum() { 356 reset(); 357 } 358 359 public TxRxSum(long txPkts, long rxPkts) { 360 this.txPkts = txPkts; 361 this.rxPkts = rxPkts; 362 } 363 364 public TxRxSum(TxRxSum sum) { 365 txPkts = sum.txPkts; 366 rxPkts = sum.rxPkts; 367 } 368 369 public void reset() { 370 txPkts = -1; 371 rxPkts = -1; 372 } 373 374 public String toString() { 375 return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}"; 376 } 377 378 public void updateTxRxSum() { 379 this.txPkts = TrafficStats.getMobileTxPackets(); 380 this.rxPkts = TrafficStats.getMobileRxPackets(); 381 } 382 } 383 384 protected boolean isDataSetupCompleteOk(AsyncResult ar) { 385 if (ar.exception != null) { 386 if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result); 387 return false; 388 } 389 if (mFailDataSetupCounter <= 0) { 390 if (DBG) log("isDataSetupCompleteOk return true"); 391 return true; 392 } 393 ar.result = mFailDataSetupFailCause; 394 if (DBG) { 395 log("isDataSetupCompleteOk return false" + 396 " mFailDataSetupCounter=" + mFailDataSetupCounter + 397 " mFailDataSetupFailCause=" + mFailDataSetupFailCause); 398 } 399 mFailDataSetupCounter -= 1; 400 return false; 401 } 402 403 protected void onActionIntentReconnectAlarm(Intent intent) { 404 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); 405 if (mState == DctConstants.State.FAILED) { 406 Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_CONNECTION); 407 msg.arg1 = 0; // tearDown is false 408 msg.arg2 = 0; 409 msg.obj = reason; 410 sendMessage(msg); 411 } 412 sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA)); 413 } 414 415 protected void onActionIntentDataStallAlarm(Intent intent) { 416 if (VDBG) log("onActionIntentDataStallAlarm: action=" + intent.getAction()); 417 Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM, 418 intent.getAction()); 419 msg.arg1 = intent.getIntExtra(DATA_STALL_ALARM_TAG_EXTRA, 0); 420 sendMessage(msg); 421 } 422 423 /** 424 * Default constructor 425 */ 426 protected DataConnectionTracker(PhoneBase phone) { 427 super(); 428 if (DBG) log("DCT.constructor"); 429 mPhone = phone; 430 mUiccController = UiccController.getInstance(); 431 mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null); 432 433 IntentFilter filter = new IntentFilter(); 434 filter.addAction(getActionIntentReconnectAlarm()); 435 filter.addAction(Intent.ACTION_SCREEN_ON); 436 filter.addAction(Intent.ACTION_SCREEN_OFF); 437 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 438 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 439 filter.addAction(INTENT_SET_FAIL_DATA_SETUP_COUNTER); 440 filter.addAction(getActionIntentDataStallAlarm()); 441 442 mUserDataEnabled = Settings.Global.getInt( 443 mPhone.getContext().getContentResolver(), Settings.Global.MOBILE_DATA, 1) == 1; 444 445 // TODO: Why is this registering the phone as the receiver of the intent 446 // and not its own handler? 447 mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone); 448 449 // This preference tells us 1) initial condition for "dataEnabled", 450 // and 2) whether the RIL will setup the baseband to auto-PS attach. 451 452 dataEnabled[DctConstants.APN_DEFAULT_ID] = 453 SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP,true); 454 if (dataEnabled[DctConstants.APN_DEFAULT_ID]) { 455 enabledCount++; 456 } 457 458 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()); 459 mAutoAttachOnCreation = sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false); 460 461 // watch for changes to Settings.Global.DATA_ROAMING 462 mDataRoamingSettingObserver = new DataRoamingSettingObserver(mPhone); 463 mDataRoamingSettingObserver.register(mPhone.getContext()); 464 465 mResolver = mPhone.getContext().getContentResolver(); 466 } 467 468 public void dispose() { 469 if (DBG) log("DCT.dispose"); 470 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 471 dcac.disconnect(); 472 } 473 mDataConnectionAsyncChannels.clear(); 474 mIsDisposed = true; 475 mPhone.getContext().unregisterReceiver(this.mIntentReceiver); 476 mDataRoamingSettingObserver.unregister(mPhone.getContext()); 477 mUiccController.unregisterForIccChanged(this); 478 } 479 480 protected void broadcastMessenger() { 481 Intent intent = new Intent(DctConstants.ACTION_DATA_CONNECTION_TRACKER_MESSENGER); 482 intent.putExtra(DctConstants.EXTRA_MESSENGER, new Messenger(this)); 483 mPhone.getContext().sendBroadcast(intent); 484 } 485 486 public DctConstants.Activity getActivity() { 487 return mActivity; 488 } 489 490 public boolean isApnTypeActive(String type) { 491 // TODO: support simultaneous with List instead 492 if (PhoneConstants.APN_TYPE_DUN.equals(type)) { 493 ApnSetting dunApn = fetchDunApn(); 494 if (dunApn != null) { 495 return ((mActiveApn != null) && (dunApn.toString().equals(mActiveApn.toString()))); 496 } 497 } 498 return mActiveApn != null && mActiveApn.canHandleType(type); 499 } 500 501 protected ApnSetting fetchDunApn() { 502 if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) { 503 log("fetchDunApn: net.tethering.noprovisioning=true ret: null"); 504 return null; 505 } 506 Context c = mPhone.getContext(); 507 String apnData = Settings.Global.getString(c.getContentResolver(), 508 Settings.Global.TETHER_DUN_APN); 509 ApnSetting dunSetting = ApnSetting.fromString(apnData); 510 if (dunSetting != null) { 511 if (VDBG) log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting); 512 return dunSetting; 513 } 514 515 apnData = c.getResources().getString(R.string.config_tether_apndata); 516 dunSetting = ApnSetting.fromString(apnData); 517 if (VDBG) log("fetchDunApn: config_tether_apndata dunSetting=" + dunSetting); 518 return dunSetting; 519 } 520 521 public String[] getActiveApnTypes() { 522 String[] result; 523 if (mActiveApn != null) { 524 result = mActiveApn.types; 525 } else { 526 result = new String[1]; 527 result[0] = PhoneConstants.APN_TYPE_DEFAULT; 528 } 529 return result; 530 } 531 532 /** TODO: See if we can remove */ 533 public String getActiveApnString(String apnType) { 534 String result = null; 535 if (mActiveApn != null) { 536 result = mActiveApn.apn; 537 } 538 return result; 539 } 540 541 /** 542 * Modify {@link Settings.Global#DATA_ROAMING} value. 543 */ 544 public void setDataOnRoamingEnabled(boolean enabled) { 545 if (getDataOnRoamingEnabled() != enabled) { 546 final ContentResolver resolver = mPhone.getContext().getContentResolver(); 547 Settings.Global.putInt(resolver, Settings.Global.DATA_ROAMING, enabled ? 1 : 0); 548 // will trigger handleDataOnRoamingChange() through observer 549 } 550 } 551 552 /** 553 * Return current {@link Settings.Global#DATA_ROAMING} value. 554 */ 555 public boolean getDataOnRoamingEnabled() { 556 try { 557 final ContentResolver resolver = mPhone.getContext().getContentResolver(); 558 return Settings.Global.getInt(resolver, Settings.Global.DATA_ROAMING) != 0; 559 } catch (SettingNotFoundException snfe) { 560 return false; 561 } 562 } 563 564 private void handleDataOnRoamingChange() { 565 if (mPhone.getServiceState().getRoaming()) { 566 if (getDataOnRoamingEnabled()) { 567 resetAllRetryCounts(); 568 } 569 sendMessage(obtainMessage(DctConstants.EVENT_ROAMING_ON)); 570 } 571 } 572 573 // abstract methods 574 protected abstract String getActionIntentReconnectAlarm(); 575 protected abstract String getActionIntentDataStallAlarm(); 576 protected abstract void restartRadio(); 577 protected abstract void log(String s); 578 protected abstract void loge(String s); 579 protected abstract boolean isDataAllowed(); 580 protected abstract boolean isApnTypeAvailable(String type); 581 public abstract DctConstants.State getState(String apnType); 582 protected abstract void setState(DctConstants.State s); 583 protected abstract void gotoIdleAndNotifyDataConnection(String reason); 584 585 protected abstract boolean onTrySetupData(String reason); 586 protected abstract void onRoamingOff(); 587 protected abstract void onRoamingOn(); 588 protected abstract void onRadioAvailable(); 589 protected abstract void onRadioOffOrNotAvailable(); 590 protected abstract void onDataSetupComplete(AsyncResult ar); 591 protected abstract void onDisconnectDone(int connId, AsyncResult ar); 592 protected abstract void onVoiceCallStarted(); 593 protected abstract void onVoiceCallEnded(); 594 protected abstract void onCleanUpConnection(boolean tearDown, int apnId, String reason); 595 protected abstract void onCleanUpAllConnections(String cause); 596 protected abstract boolean isDataPossible(String apnType); 597 protected abstract void onUpdateIcc(); 598 599 @Override 600 public void handleMessage(Message msg) { 601 switch (msg.what) { 602 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 603 log("DISCONNECTED_CONNECTED: msg=" + msg); 604 DataConnectionAc dcac = (DataConnectionAc) msg.obj; 605 mDataConnectionAsyncChannels.remove(dcac.dataConnection.getDataConnectionId()); 606 dcac.disconnected(); 607 break; 608 } 609 case DctConstants.EVENT_ENABLE_NEW_APN: 610 onEnableApn(msg.arg1, msg.arg2); 611 break; 612 613 case DctConstants.EVENT_TRY_SETUP_DATA: 614 String reason = null; 615 if (msg.obj instanceof String) { 616 reason = (String) msg.obj; 617 } 618 onTrySetupData(reason); 619 break; 620 621 case DctConstants.EVENT_DATA_STALL_ALARM: 622 onDataStallAlarm(msg.arg1); 623 break; 624 625 case DctConstants.EVENT_ROAMING_OFF: 626 if (getDataOnRoamingEnabled() == false) { 627 resetAllRetryCounts(); 628 } 629 onRoamingOff(); 630 break; 631 632 case DctConstants.EVENT_ROAMING_ON: 633 onRoamingOn(); 634 break; 635 636 case DctConstants.EVENT_RADIO_AVAILABLE: 637 onRadioAvailable(); 638 break; 639 640 case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE: 641 onRadioOffOrNotAvailable(); 642 break; 643 644 case DctConstants.EVENT_DATA_SETUP_COMPLETE: 645 mCidActive = msg.arg1; 646 onDataSetupComplete((AsyncResult) msg.obj); 647 break; 648 649 case DctConstants.EVENT_DISCONNECT_DONE: 650 log("DataConnectoinTracker.handleMessage: EVENT_DISCONNECT_DONE msg=" + msg); 651 onDisconnectDone(msg.arg1, (AsyncResult) msg.obj); 652 break; 653 654 case DctConstants.EVENT_VOICE_CALL_STARTED: 655 onVoiceCallStarted(); 656 break; 657 658 case DctConstants.EVENT_VOICE_CALL_ENDED: 659 onVoiceCallEnded(); 660 break; 661 662 case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS: { 663 onCleanUpAllConnections((String) msg.obj); 664 break; 665 } 666 case DctConstants.EVENT_CLEAN_UP_CONNECTION: { 667 boolean tearDown = (msg.arg1 == 0) ? false : true; 668 onCleanUpConnection(tearDown, msg.arg2, (String) msg.obj); 669 break; 670 } 671 case DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE: { 672 boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false; 673 onSetInternalDataEnabled(enabled); 674 break; 675 } 676 case DctConstants.EVENT_RESET_DONE: { 677 if (DBG) log("EVENT_RESET_DONE"); 678 onResetDone((AsyncResult) msg.obj); 679 break; 680 } 681 case DctConstants.CMD_SET_USER_DATA_ENABLE: { 682 final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false; 683 if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled); 684 onSetUserDataEnabled(enabled); 685 break; 686 } 687 case DctConstants.CMD_SET_DEPENDENCY_MET: { 688 boolean met = (msg.arg1 == DctConstants.ENABLED) ? true : false; 689 if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met); 690 Bundle bundle = msg.getData(); 691 if (bundle != null) { 692 String apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY); 693 if (apnType != null) { 694 onSetDependencyMet(apnType, met); 695 } 696 } 697 break; 698 } 699 case DctConstants.CMD_SET_POLICY_DATA_ENABLE: { 700 final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false; 701 onSetPolicyDataEnabled(enabled); 702 break; 703 } 704 case DctConstants.EVENT_ICC_CHANGED: 705 onUpdateIcc(); 706 break; 707 708 default: 709 Log.e("DATA", "Unidentified event msg=" + msg); 710 break; 711 } 712 } 713 714 /** 715 * Report on whether data connectivity is enabled 716 * 717 * @return {@code false} if data connectivity has been explicitly disabled, 718 * {@code true} otherwise. 719 */ 720 public boolean getAnyDataEnabled() { 721 final boolean result; 722 synchronized (mDataEnabledLock) { 723 result = (mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled 724 && (enabledCount != 0)); 725 } 726 if (!result && DBG) log("getAnyDataEnabled " + result); 727 return result; 728 } 729 730 protected boolean isEmergency() { 731 final boolean result; 732 synchronized (mDataEnabledLock) { 733 result = mPhone.isInEcm() || mPhone.isInEmergencyCall(); 734 } 735 log("isEmergency: result=" + result); 736 return result; 737 } 738 739 protected int apnTypeToId(String type) { 740 if (TextUtils.equals(type, PhoneConstants.APN_TYPE_DEFAULT)) { 741 return DctConstants.APN_DEFAULT_ID; 742 } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_MMS)) { 743 return DctConstants.APN_MMS_ID; 744 } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_SUPL)) { 745 return DctConstants.APN_SUPL_ID; 746 } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_DUN)) { 747 return DctConstants.APN_DUN_ID; 748 } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_HIPRI)) { 749 return DctConstants.APN_HIPRI_ID; 750 } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_IMS)) { 751 return DctConstants.APN_IMS_ID; 752 } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_FOTA)) { 753 return DctConstants.APN_FOTA_ID; 754 } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_CBS)) { 755 return DctConstants.APN_CBS_ID; 756 } else { 757 return DctConstants.APN_INVALID_ID; 758 } 759 } 760 761 protected String apnIdToType(int id) { 762 switch (id) { 763 case DctConstants.APN_DEFAULT_ID: 764 return PhoneConstants.APN_TYPE_DEFAULT; 765 case DctConstants.APN_MMS_ID: 766 return PhoneConstants.APN_TYPE_MMS; 767 case DctConstants.APN_SUPL_ID: 768 return PhoneConstants.APN_TYPE_SUPL; 769 case DctConstants.APN_DUN_ID: 770 return PhoneConstants.APN_TYPE_DUN; 771 case DctConstants.APN_HIPRI_ID: 772 return PhoneConstants.APN_TYPE_HIPRI; 773 case DctConstants.APN_IMS_ID: 774 return PhoneConstants.APN_TYPE_IMS; 775 case DctConstants.APN_FOTA_ID: 776 return PhoneConstants.APN_TYPE_FOTA; 777 case DctConstants.APN_CBS_ID: 778 return PhoneConstants.APN_TYPE_CBS; 779 default: 780 log("Unknown id (" + id + ") in apnIdToType"); 781 return PhoneConstants.APN_TYPE_DEFAULT; 782 } 783 } 784 785 protected LinkProperties getLinkProperties(String apnType) { 786 int id = apnTypeToId(apnType); 787 788 if (isApnIdEnabled(id)) { 789 // TODO - remove this cdma-only hack and support multiple DCs. 790 DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0); 791 return dcac.getLinkPropertiesSync(); 792 } else { 793 return new LinkProperties(); 794 } 795 } 796 797 protected LinkCapabilities getLinkCapabilities(String apnType) { 798 int id = apnTypeToId(apnType); 799 if (isApnIdEnabled(id)) { 800 // TODO - remove this cdma-only hack and support multiple DCs. 801 DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0); 802 return dcac.getLinkCapabilitiesSync(); 803 } else { 804 return new LinkCapabilities(); 805 } 806 } 807 808 // tell all active apns of the current condition 809 protected void notifyDataConnection(String reason) { 810 for (int id = 0; id < DctConstants.APN_NUM_TYPES; id++) { 811 if (dataEnabled[id]) { 812 mPhone.notifyDataConnection(reason, apnIdToType(id)); 813 } 814 } 815 notifyOffApnsOfAvailability(reason); 816 } 817 818 // a new APN has gone active and needs to send events to catch up with the 819 // current condition 820 private void notifyApnIdUpToCurrent(String reason, int apnId) { 821 switch (mState) { 822 case IDLE: 823 case INITING: 824 break; 825 case CONNECTING: 826 case SCANNING: 827 mPhone.notifyDataConnection(reason, apnIdToType(apnId), 828 PhoneConstants.DataState.CONNECTING); 829 break; 830 case CONNECTED: 831 case DISCONNECTING: 832 mPhone.notifyDataConnection(reason, apnIdToType(apnId), 833 PhoneConstants.DataState.CONNECTING); 834 mPhone.notifyDataConnection(reason, apnIdToType(apnId), 835 PhoneConstants.DataState.CONNECTED); 836 break; 837 } 838 } 839 840 // since we normally don't send info to a disconnected APN, we need to do this specially 841 private void notifyApnIdDisconnected(String reason, int apnId) { 842 mPhone.notifyDataConnection(reason, apnIdToType(apnId), 843 PhoneConstants.DataState.DISCONNECTED); 844 } 845 846 // disabled apn's still need avail/unavail notificiations - send them out 847 protected void notifyOffApnsOfAvailability(String reason) { 848 if (DBG) log("notifyOffApnsOfAvailability - reason= " + reason); 849 for (int id = 0; id < DctConstants.APN_NUM_TYPES; id++) { 850 if (!isApnIdEnabled(id)) { 851 notifyApnIdDisconnected(reason, id); 852 } 853 } 854 } 855 856 public boolean isApnTypeEnabled(String apnType) { 857 if (apnType == null) { 858 return false; 859 } else { 860 return isApnIdEnabled(apnTypeToId(apnType)); 861 } 862 } 863 864 protected synchronized boolean isApnIdEnabled(int id) { 865 if (id != DctConstants.APN_INVALID_ID) { 866 return dataEnabled[id]; 867 } 868 return false; 869 } 870 871 /** 872 * Ensure that we are connected to an APN of the specified type. 873 * 874 * @param type the APN type (currently the only valid values are 875 * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}) 876 * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or 877 * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a 878 * broadcast will be sent by the ConnectivityManager when a 879 * connection to the APN has been established. 880 */ 881 public synchronized int enableApnType(String type) { 882 int id = apnTypeToId(type); 883 if (id == DctConstants.APN_INVALID_ID) { 884 return PhoneConstants.APN_REQUEST_FAILED; 885 } 886 887 if (DBG) { 888 log("enableApnType(" + type + "), isApnTypeActive = " + isApnTypeActive(type) 889 + ", isApnIdEnabled =" + isApnIdEnabled(id) + " and state = " + mState); 890 } 891 892 if (!isApnTypeAvailable(type)) { 893 if (DBG) log("type not available"); 894 return PhoneConstants.APN_TYPE_NOT_AVAILABLE; 895 } 896 897 if (isApnIdEnabled(id)) { 898 return PhoneConstants.APN_ALREADY_ACTIVE; 899 } else { 900 setEnabled(id, true); 901 } 902 return PhoneConstants.APN_REQUEST_STARTED; 903 } 904 905 /** 906 * The APN of the specified type is no longer needed. Ensure that if use of 907 * the default APN has not been explicitly disabled, we are connected to the 908 * default APN. 909 * 910 * @param type the APN type. The only valid values are currently 911 * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}. 912 * @return Success is indicated by {@code PhoneConstants.APN_ALREADY_ACTIVE} or 913 * {@code PhoneConstants.APN_REQUEST_STARTED}. In the latter case, a 914 * broadcast will be sent by the ConnectivityManager when a 915 * connection to the APN has been disconnected. A {@code 916 * PhoneConstants.APN_REQUEST_FAILED} is returned if the type parameter is 917 * invalid or if the apn wasn't enabled. 918 */ 919 public synchronized int disableApnType(String type) { 920 if (DBG) log("disableApnType(" + type + ")"); 921 int id = apnTypeToId(type); 922 if (id == DctConstants.APN_INVALID_ID) { 923 return PhoneConstants.APN_REQUEST_FAILED; 924 } 925 if (isApnIdEnabled(id)) { 926 setEnabled(id, false); 927 if (isApnTypeActive(PhoneConstants.APN_TYPE_DEFAULT)) { 928 if (dataEnabled[DctConstants.APN_DEFAULT_ID]) { 929 return PhoneConstants.APN_ALREADY_ACTIVE; 930 } else { 931 return PhoneConstants.APN_REQUEST_STARTED; 932 } 933 } else { 934 return PhoneConstants.APN_REQUEST_STARTED; 935 } 936 } else { 937 return PhoneConstants.APN_REQUEST_FAILED; 938 } 939 } 940 941 protected void setEnabled(int id, boolean enable) { 942 if (DBG) { 943 log("setEnabled(" + id + ", " + enable + ") with old state = " + dataEnabled[id] 944 + " and enabledCount = " + enabledCount); 945 } 946 Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN); 947 msg.arg1 = id; 948 msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED); 949 sendMessage(msg); 950 } 951 952 protected void onEnableApn(int apnId, int enabled) { 953 if (DBG) { 954 log("EVENT_APN_ENABLE_REQUEST apnId=" + apnId + ", apnType=" + apnIdToType(apnId) + 955 ", enabled=" + enabled + ", dataEnabled = " + dataEnabled[apnId] + 956 ", enabledCount = " + enabledCount + ", isApnTypeActive = " + 957 isApnTypeActive(apnIdToType(apnId))); 958 } 959 if (enabled == DctConstants.ENABLED) { 960 synchronized (this) { 961 if (!dataEnabled[apnId]) { 962 dataEnabled[apnId] = true; 963 enabledCount++; 964 } 965 } 966 String type = apnIdToType(apnId); 967 if (!isApnTypeActive(type)) { 968 mRequestedApnType = type; 969 onEnableNewApn(); 970 } else { 971 notifyApnIdUpToCurrent(Phone.REASON_APN_SWITCHED, apnId); 972 } 973 } else { 974 // disable 975 boolean didDisable = false; 976 synchronized (this) { 977 if (dataEnabled[apnId]) { 978 dataEnabled[apnId] = false; 979 enabledCount--; 980 didDisable = true; 981 } 982 } 983 if (didDisable) { 984 if ((enabledCount == 0) || (apnId == DctConstants.APN_DUN_ID)) { 985 mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT; 986 onCleanUpConnection(true, apnId, Phone.REASON_DATA_DISABLED); 987 } 988 989 // send the disconnect msg manually, since the normal route wont send 990 // it (it's not enabled) 991 notifyApnIdDisconnected(Phone.REASON_DATA_DISABLED, apnId); 992 if (dataEnabled[DctConstants.APN_DEFAULT_ID] == true 993 && !isApnTypeActive(PhoneConstants.APN_TYPE_DEFAULT)) { 994 // TODO - this is an ugly way to restore the default conn - should be done 995 // by a real contention manager and policy that disconnects the lower pri 996 // stuff as enable requests come in and pops them back on as we disable back 997 // down to the lower pri stuff 998 mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT; 999 onEnableNewApn(); 1000 } 1001 } 1002 } 1003 } 1004 1005 /** 1006 * Called when we switch APNs. 1007 * 1008 * mRequestedApnType is set prior to call 1009 * To be overridden. 1010 */ 1011 protected void onEnableNewApn() { 1012 } 1013 1014 /** 1015 * Called when EVENT_RESET_DONE is received so goto 1016 * IDLE state and send notifications to those interested. 1017 * 1018 * TODO - currently unused. Needs to be hooked into DataConnection cleanup 1019 * TODO - needs to pass some notion of which connection is reset.. 1020 */ 1021 protected void onResetDone(AsyncResult ar) { 1022 if (DBG) log("EVENT_RESET_DONE"); 1023 String reason = null; 1024 if (ar.userObj instanceof String) { 1025 reason = (String) ar.userObj; 1026 } 1027 gotoIdleAndNotifyDataConnection(reason); 1028 } 1029 1030 /** 1031 * Prevent mobile data connections from being established, or once again 1032 * allow mobile data connections. If the state toggles, then either tear 1033 * down or set up data, as appropriate to match the new state. 1034 * 1035 * @param enable indicates whether to enable ({@code true}) or disable ( 1036 * {@code false}) data 1037 * @return {@code true} if the operation succeeded 1038 */ 1039 public boolean setInternalDataEnabled(boolean enable) { 1040 if (DBG) 1041 log("setInternalDataEnabled(" + enable + ")"); 1042 1043 Message msg = obtainMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE); 1044 msg.arg1 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED); 1045 sendMessage(msg); 1046 return true; 1047 } 1048 1049 protected void onSetInternalDataEnabled(boolean enabled) { 1050 synchronized (mDataEnabledLock) { 1051 mInternalDataEnabled = enabled; 1052 if (enabled) { 1053 log("onSetInternalDataEnabled: changed to enabled, try to setup data call"); 1054 resetAllRetryCounts(); 1055 onTrySetupData(Phone.REASON_DATA_ENABLED); 1056 } else { 1057 log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections"); 1058 cleanUpAllConnections(null); 1059 } 1060 } 1061 } 1062 1063 public void cleanUpAllConnections(String cause) { 1064 Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS); 1065 msg.obj = cause; 1066 sendMessage(msg); 1067 } 1068 1069 public abstract boolean isDisconnected(); 1070 1071 protected void onSetUserDataEnabled(boolean enabled) { 1072 synchronized (mDataEnabledLock) { 1073 final boolean prevEnabled = getAnyDataEnabled(); 1074 if (mUserDataEnabled != enabled) { 1075 mUserDataEnabled = enabled; 1076 Settings.Global.putInt(mPhone.getContext().getContentResolver(), 1077 Settings.Global.MOBILE_DATA, enabled ? 1 : 0); 1078 if (getDataOnRoamingEnabled() == false && 1079 mPhone.getServiceState().getRoaming() == true) { 1080 if (enabled) { 1081 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON); 1082 } else { 1083 notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED); 1084 } 1085 } 1086 if (prevEnabled != getAnyDataEnabled()) { 1087 if (!prevEnabled) { 1088 resetAllRetryCounts(); 1089 onTrySetupData(Phone.REASON_DATA_ENABLED); 1090 } else { 1091 onCleanUpAllConnections(Phone.REASON_DATA_DISABLED); 1092 } 1093 } 1094 } 1095 } 1096 } 1097 1098 protected void onSetDependencyMet(String apnType, boolean met) { 1099 } 1100 1101 protected void onSetPolicyDataEnabled(boolean enabled) { 1102 synchronized (mDataEnabledLock) { 1103 final boolean prevEnabled = getAnyDataEnabled(); 1104 if (sPolicyDataEnabled != enabled) { 1105 sPolicyDataEnabled = enabled; 1106 if (prevEnabled != getAnyDataEnabled()) { 1107 if (!prevEnabled) { 1108 resetAllRetryCounts(); 1109 onTrySetupData(Phone.REASON_DATA_ENABLED); 1110 } else { 1111 onCleanUpAllConnections(Phone.REASON_DATA_DISABLED); 1112 } 1113 } 1114 } 1115 } 1116 } 1117 1118 protected String getReryConfig(boolean forDefault) { 1119 int nt = mPhone.getServiceState().getNetworkType(); 1120 1121 if ((nt == TelephonyManager.NETWORK_TYPE_CDMA) || 1122 (nt == TelephonyManager.NETWORK_TYPE_1xRTT) || 1123 (nt == TelephonyManager.NETWORK_TYPE_EVDO_0) || 1124 (nt == TelephonyManager.NETWORK_TYPE_EVDO_A) || 1125 (nt == TelephonyManager.NETWORK_TYPE_EVDO_B) || 1126 (nt == TelephonyManager.NETWORK_TYPE_EHRPD)) { 1127 // CDMA variant 1128 return SystemProperties.get("ro.cdma.data_retry_config"); 1129 } else { 1130 // Use GSM varient for all others. 1131 if (forDefault) { 1132 return SystemProperties.get("ro.gsm.data_retry_config"); 1133 } else { 1134 return SystemProperties.get("ro.gsm.2nd_data_retry_config"); 1135 } 1136 } 1137 } 1138 1139 protected void resetAllRetryCounts() { 1140 for (ApnContext ac : mApnContexts.values()) { 1141 ac.setRetryCount(0); 1142 } 1143 for (DataConnection dc : mDataConnections.values()) { 1144 dc.resetRetryCount(); 1145 } 1146 } 1147 1148 protected void resetPollStats() { 1149 mTxPkts = -1; 1150 mRxPkts = -1; 1151 mNetStatPollPeriod = POLL_NETSTAT_MILLIS; 1152 } 1153 1154 protected abstract DctConstants.State getOverallState(); 1155 1156 protected void startNetStatPoll() { 1157 if (getOverallState() == DctConstants.State.CONNECTED && mNetStatPollEnabled == false) { 1158 if (DBG) log("startNetStatPoll"); 1159 resetPollStats(); 1160 mNetStatPollEnabled = true; 1161 mPollNetStat.run(); 1162 } 1163 } 1164 1165 protected void stopNetStatPoll() { 1166 mNetStatPollEnabled = false; 1167 removeCallbacks(mPollNetStat); 1168 if (DBG) log("stopNetStatPoll"); 1169 } 1170 1171 public void updateDataActivity() { 1172 long sent, received; 1173 1174 DctConstants.Activity newActivity; 1175 1176 TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts); 1177 TxRxSum curTxRxSum = new TxRxSum(); 1178 curTxRxSum.updateTxRxSum(); 1179 mTxPkts = curTxRxSum.txPkts; 1180 mRxPkts = curTxRxSum.rxPkts; 1181 1182 if (VDBG) { 1183 log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum); 1184 } 1185 1186 if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) { 1187 sent = mTxPkts - preTxRxSum.txPkts; 1188 received = mRxPkts - preTxRxSum.rxPkts; 1189 1190 if (VDBG) 1191 log("updateDataActivity: sent=" + sent + " received=" + received); 1192 if (sent > 0 && received > 0) { 1193 newActivity = DctConstants.Activity.DATAINANDOUT; 1194 } else if (sent > 0 && received == 0) { 1195 newActivity = DctConstants.Activity.DATAOUT; 1196 } else if (sent == 0 && received > 0) { 1197 newActivity = DctConstants.Activity.DATAIN; 1198 } else { 1199 newActivity = (mActivity == DctConstants.Activity.DORMANT) ? 1200 mActivity : DctConstants.Activity.NONE; 1201 } 1202 1203 if (mActivity != newActivity && mIsScreenOn) { 1204 if (VDBG) 1205 log("updateDataActivity: newActivity=" + newActivity); 1206 mActivity = newActivity; 1207 mPhone.notifyDataActivity(); 1208 } 1209 } 1210 } 1211 1212 // Recovery action taken in case of data stall 1213 protected static class RecoveryAction { 1214 public static final int GET_DATA_CALL_LIST = 0; 1215 public static final int CLEANUP = 1; 1216 public static final int REREGISTER = 2; 1217 public static final int RADIO_RESTART = 3; 1218 public static final int RADIO_RESTART_WITH_PROP = 4; 1219 1220 private static boolean isAggressiveRecovery(int value) { 1221 return ((value == RecoveryAction.CLEANUP) || 1222 (value == RecoveryAction.REREGISTER) || 1223 (value == RecoveryAction.RADIO_RESTART) || 1224 (value == RecoveryAction.RADIO_RESTART_WITH_PROP)); 1225 } 1226 } 1227 1228 public int getRecoveryAction() { 1229 int action = Settings.System.getInt(mPhone.getContext().getContentResolver(), 1230 "radio.data.stall.recovery.action", RecoveryAction.GET_DATA_CALL_LIST); 1231 if (VDBG) log("getRecoveryAction: " + action); 1232 return action; 1233 } 1234 public void putRecoveryAction(int action) { 1235 Settings.System.putInt(mPhone.getContext().getContentResolver(), 1236 "radio.data.stall.recovery.action", action); 1237 if (VDBG) log("putRecoveryAction: " + action); 1238 } 1239 1240 protected boolean isConnected() { 1241 return false; 1242 } 1243 1244 protected void doRecovery() { 1245 if (getOverallState() == DctConstants.State.CONNECTED) { 1246 // Go through a series of recovery steps, each action transitions to the next action 1247 int recoveryAction = getRecoveryAction(); 1248 switch (recoveryAction) { 1249 case RecoveryAction.GET_DATA_CALL_LIST: 1250 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST, 1251 mSentSinceLastRecv); 1252 if (DBG) log("doRecovery() get data call list"); 1253 mPhone.mCM.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED)); 1254 putRecoveryAction(RecoveryAction.CLEANUP); 1255 break; 1256 case RecoveryAction.CLEANUP: 1257 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv); 1258 if (DBG) log("doRecovery() cleanup all connections"); 1259 cleanUpAllConnections(Phone.REASON_PDP_RESET); 1260 putRecoveryAction(RecoveryAction.REREGISTER); 1261 break; 1262 case RecoveryAction.REREGISTER: 1263 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER, 1264 mSentSinceLastRecv); 1265 if (DBG) log("doRecovery() re-register"); 1266 mPhone.getServiceStateTracker().reRegisterNetwork(null); 1267 putRecoveryAction(RecoveryAction.RADIO_RESTART); 1268 break; 1269 case RecoveryAction.RADIO_RESTART: 1270 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART, 1271 mSentSinceLastRecv); 1272 if (DBG) log("restarting radio"); 1273 putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP); 1274 restartRadio(); 1275 break; 1276 case RecoveryAction.RADIO_RESTART_WITH_PROP: 1277 // This is in case radio restart has not recovered the data. 1278 // It will set an additional "gsm.radioreset" property to tell 1279 // RIL or system to take further action. 1280 // The implementation of hard reset recovery action is up to OEM product. 1281 // Once RADIO_RESET property is consumed, it is expected to set back 1282 // to false by RIL. 1283 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1); 1284 if (DBG) log("restarting radio with gsm.radioreset to true"); 1285 SystemProperties.set(RADIO_RESET_PROPERTY, "true"); 1286 // give 1 sec so property change can be notified. 1287 try { 1288 Thread.sleep(1000); 1289 } catch (InterruptedException e) {} 1290 restartRadio(); 1291 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST); 1292 break; 1293 default: 1294 throw new RuntimeException("doRecovery: Invalid recoveryAction=" + 1295 recoveryAction); 1296 } 1297 } 1298 } 1299 1300 private void updateDataStallInfo() { 1301 long sent, received; 1302 1303 TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum); 1304 mDataStallTxRxSum.updateTxRxSum(); 1305 1306 if (VDBG) { 1307 log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum + 1308 " preTxRxSum=" + preTxRxSum); 1309 } 1310 1311 sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts; 1312 received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts; 1313 1314 if (RADIO_TESTS) { 1315 if (SystemProperties.getBoolean("radio.test.data.stall", false)) { 1316 log("updateDataStallInfo: radio.test.data.stall true received = 0;"); 1317 received = 0; 1318 } 1319 } 1320 if ( sent > 0 && received > 0 ) { 1321 if (VDBG) log("updateDataStallInfo: IN/OUT"); 1322 mSentSinceLastRecv = 0; 1323 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST); 1324 } else if (sent > 0 && received == 0) { 1325 if (mPhone.getState() == PhoneConstants.State.IDLE) { 1326 mSentSinceLastRecv += sent; 1327 } else { 1328 mSentSinceLastRecv = 0; 1329 } 1330 if (DBG) { 1331 log("updateDataStallInfo: OUT sent=" + sent + 1332 " mSentSinceLastRecv=" + mSentSinceLastRecv); 1333 } 1334 } else if (sent == 0 && received > 0) { 1335 if (VDBG) log("updateDataStallInfo: IN"); 1336 mSentSinceLastRecv = 0; 1337 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST); 1338 } else { 1339 if (VDBG) log("updateDataStallInfo: NONE"); 1340 } 1341 } 1342 1343 protected void onDataStallAlarm(int tag) { 1344 if (mDataStallAlarmTag != tag) { 1345 if (DBG) { 1346 log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag); 1347 } 1348 return; 1349 } 1350 updateDataStallInfo(); 1351 1352 int hangWatchdogTrigger = Settings.Global.getInt(mResolver, 1353 Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 1354 NUMBER_SENT_PACKETS_OF_HANG); 1355 1356 boolean suspectedStall = DATA_STALL_NOT_SUSPECTED; 1357 if (mSentSinceLastRecv >= hangWatchdogTrigger) { 1358 if (DBG) { 1359 log("onDataStallAlarm: tag=" + tag + " do recovery action=" + getRecoveryAction()); 1360 } 1361 suspectedStall = DATA_STALL_SUSPECTED; 1362 sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY)); 1363 } else { 1364 if (VDBG) { 1365 log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) + 1366 " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger); 1367 } 1368 } 1369 startDataStallAlarm(suspectedStall); 1370 } 1371 1372 protected void startDataStallAlarm(boolean suspectedStall) { 1373 int nextAction = getRecoveryAction(); 1374 int delayInMs; 1375 1376 // If screen is on or data stall is currently suspected, set the alarm 1377 // with an aggresive timeout. 1378 if (mIsScreenOn || suspectedStall || RecoveryAction.isAggressiveRecovery(nextAction)) { 1379 delayInMs = Settings.Global.getInt(mResolver, 1380 Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS, 1381 DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT); 1382 } else { 1383 delayInMs = Settings.Global.getInt(mResolver, 1384 Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS, 1385 DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT); 1386 } 1387 1388 mDataStallAlarmTag += 1; 1389 if (VDBG) { 1390 log("startDataStallAlarm: tag=" + mDataStallAlarmTag + 1391 " delay=" + (delayInMs / 1000) + "s"); 1392 } 1393 AlarmManager am = 1394 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 1395 1396 Intent intent = new Intent(getActionIntentDataStallAlarm()); 1397 intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag); 1398 mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, 1399 PendingIntent.FLAG_UPDATE_CURRENT); 1400 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1401 SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent); 1402 } 1403 1404 protected void stopDataStallAlarm() { 1405 AlarmManager am = 1406 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 1407 1408 if (VDBG) { 1409 log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag + 1410 " mDataStallAlarmIntent=" + mDataStallAlarmIntent); 1411 } 1412 mDataStallAlarmTag += 1; 1413 if (mDataStallAlarmIntent != null) { 1414 am.cancel(mDataStallAlarmIntent); 1415 mDataStallAlarmIntent = null; 1416 } 1417 } 1418 1419 protected void restartDataStallAlarm() { 1420 if (isConnected() == false) return; 1421 // To be called on screen status change. 1422 // Do not cancel the alarm if it is set with aggressive timeout. 1423 int nextAction = getRecoveryAction(); 1424 1425 if (RecoveryAction.isAggressiveRecovery(nextAction)) { 1426 if (DBG) log("data stall recovery action is pending. not resetting the alarm."); 1427 return; 1428 } 1429 stopDataStallAlarm(); 1430 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); 1431 } 1432 1433 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1434 pw.println("DataConnectionTracker:"); 1435 pw.println(" RADIO_TESTS=" + RADIO_TESTS); 1436 pw.println(" mInternalDataEnabled=" + mInternalDataEnabled); 1437 pw.println(" mUserDataEnabled=" + mUserDataEnabled); 1438 pw.println(" sPolicyDataEnabed=" + sPolicyDataEnabled); 1439 pw.println(" dataEnabled:"); 1440 for(int i=0; i < dataEnabled.length; i++) { 1441 pw.printf(" dataEnabled[%d]=%b\n", i, dataEnabled[i]); 1442 } 1443 pw.flush(); 1444 pw.println(" enabledCount=" + enabledCount); 1445 pw.println(" mRequestedApnType=" + mRequestedApnType); 1446 pw.println(" mPhone=" + mPhone.getPhoneName()); 1447 pw.println(" mActivity=" + mActivity); 1448 pw.println(" mState=" + mState); 1449 pw.println(" mTxPkts=" + mTxPkts); 1450 pw.println(" mRxPkts=" + mRxPkts); 1451 pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod); 1452 pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled); 1453 pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum); 1454 pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag); 1455 pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv); 1456 pw.println(" mNoRecvPollCount=" + mNoRecvPollCount); 1457 pw.println(" mResolver=" + mResolver); 1458 pw.println(" mIsWifiConnected=" + mIsWifiConnected); 1459 pw.println(" mReconnectIntent=" + mReconnectIntent); 1460 pw.println(" mCidActive=" + mCidActive); 1461 pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation); 1462 pw.println(" mIsScreenOn=" + mIsScreenOn); 1463 pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator); 1464 pw.flush(); 1465 pw.println(" ***************************************"); 1466 Set<Entry<Integer, DataConnection> > mDcSet = mDataConnections.entrySet(); 1467 pw.println(" mDataConnections: count=" + mDcSet.size()); 1468 for (Entry<Integer, DataConnection> entry : mDcSet) { 1469 pw.printf(" *** mDataConnection[%d] \n", entry.getKey()); 1470 entry.getValue().dump(fd, pw, args); 1471 } 1472 pw.println(" ***************************************"); 1473 pw.flush(); 1474 Set<Entry<String, Integer>> mApnToDcIdSet = mApnToDataConnectionId.entrySet(); 1475 pw.println(" mApnToDataConnectonId size=" + mApnToDcIdSet.size()); 1476 for (Entry<String, Integer> entry : mApnToDcIdSet) { 1477 pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue()); 1478 } 1479 pw.println(" ***************************************"); 1480 pw.flush(); 1481 if (mApnContexts != null) { 1482 Set<Entry<String, ApnContext>> mApnContextsSet = mApnContexts.entrySet(); 1483 pw.println(" mApnContexts size=" + mApnContextsSet.size()); 1484 for (Entry<String, ApnContext> entry : mApnContextsSet) { 1485 entry.getValue().dump(fd, pw, args); 1486 } 1487 pw.println(" ***************************************"); 1488 } else { 1489 pw.println(" mApnContexts=null"); 1490 } 1491 pw.flush(); 1492 pw.println(" mActiveApn=" + mActiveApn); 1493 if (mAllApns != null) { 1494 pw.println(" mAllApns size=" + mAllApns.size()); 1495 for (int i=0; i < mAllApns.size(); i++) { 1496 pw.printf(" mAllApns[%d]: %s\n", i, mAllApns.get(i)); 1497 } 1498 pw.flush(); 1499 } else { 1500 pw.println(" mAllApns=null"); 1501 } 1502 pw.println(" mPreferredApn=" + mPreferredApn); 1503 pw.println(" mIsPsRestricted=" + mIsPsRestricted); 1504 pw.println(" mIsDisposed=" + mIsDisposed); 1505 pw.println(" mIntentReceiver=" + mIntentReceiver); 1506 pw.println(" mDataRoamingSettingObserver=" + mDataRoamingSettingObserver); 1507 pw.flush(); 1508 } 1509 } 1510