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