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