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