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