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