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