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