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