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