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