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