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