Home | History | Annotate | Download | only in telephony
      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