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