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