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