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