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