DcTracker.java revision 4b17d6f820839eb663ee4493b48a0184be7aefd4
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.app.ProgressDialog;
22import android.content.ActivityNotFoundException;
23import android.content.BroadcastReceiver;
24import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.SharedPreferences;
30import android.content.res.Resources;
31import android.database.ContentObserver;
32import android.database.Cursor;
33import android.net.ConnectivityManager;
34import android.net.LinkProperties;
35import android.net.NetworkCapabilities;
36import android.net.NetworkConfig;
37import android.net.NetworkInfo;
38import android.net.NetworkRequest;
39import android.net.NetworkUtils;
40import android.net.ProxyInfo;
41import android.net.TrafficStats;
42import android.net.Uri;
43import android.net.wifi.WifiManager;
44import android.os.AsyncResult;
45import android.os.Build;
46import android.os.Bundle;
47import android.os.Handler;
48import android.os.HandlerThread;
49import android.os.Message;
50import android.os.RegistrantList;
51import android.os.ServiceManager;
52import android.os.SystemClock;
53import android.os.SystemProperties;
54import android.preference.PreferenceManager;
55import android.provider.Settings;
56import android.provider.Settings.SettingNotFoundException;
57import android.provider.Telephony;
58import android.telephony.CellLocation;
59import android.telephony.PcoData;
60import android.telephony.Rlog;
61import android.telephony.ServiceState;
62import android.telephony.SubscriptionManager;
63import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
64import android.telephony.TelephonyManager;
65import android.telephony.cdma.CdmaCellLocation;
66import android.telephony.gsm.GsmCellLocation;
67import android.text.TextUtils;
68import android.util.EventLog;
69import android.util.LocalLog;
70import android.util.Pair;
71import android.util.SparseArray;
72import android.view.WindowManager;
73
74import com.android.internal.R;
75import com.android.internal.annotations.VisibleForTesting;
76import com.android.internal.telephony.DctConstants;
77import com.android.internal.telephony.EventLogTags;
78import com.android.internal.telephony.GsmCdmaPhone;
79import com.android.internal.telephony.ITelephony;
80import com.android.internal.telephony.Phone;
81import com.android.internal.telephony.PhoneConstants;
82import com.android.internal.telephony.RILConstants;
83import com.android.internal.telephony.ServiceStateTracker;
84import com.android.internal.telephony.TelephonyIntents;
85import com.android.internal.telephony.metrics.TelephonyMetrics;
86import com.android.internal.telephony.uicc.IccRecords;
87import com.android.internal.telephony.uicc.UiccController;
88import com.android.internal.util.ArrayUtils;
89import com.android.internal.util.AsyncChannel;
90
91import java.io.FileDescriptor;
92import java.io.PrintWriter;
93import java.util.ArrayList;
94import java.util.Arrays;
95import java.util.Comparator;
96import java.util.HashMap;
97import java.util.HashSet;
98import java.util.List;
99import java.util.Map.Entry;
100import java.util.Objects;
101import java.util.PriorityQueue;
102import java.util.Set;
103
104import java.util.concurrent.ConcurrentHashMap;
105import java.util.concurrent.atomic.AtomicBoolean;
106import java.util.concurrent.atomic.AtomicInteger;
107import java.util.concurrent.atomic.AtomicReference;
108/**
109 * {@hide}
110 */
111public class DcTracker extends Handler {
112    private static final String LOG_TAG = "DCT";
113    private static final boolean DBG = true;
114    private static final boolean VDBG = false; // STOPSHIP if true
115    private static final boolean VDBG_STALL = false; // STOPSHIP if true
116    private static final boolean RADIO_TESTS = false;
117
118    public AtomicBoolean isCleanupRequired = new AtomicBoolean(false);
119
120    private final AlarmManager mAlarmManager;
121
122    /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
123    private String mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
124
125    // All data enabling/disabling related settings
126    private final DataEnabledSettings mDataEnabledSettings = new DataEnabledSettings();
127
128
129    /**
130     * After detecting a potential connection problem, this is the max number
131     * of subsequent polls before attempting recovery.
132     */
133    // 1 sec. default polling interval when screen is on.
134    private static final int POLL_NETSTAT_MILLIS = 1000;
135    // 10 min. default polling interval when screen is off.
136    private static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
137    // Default sent packets without ack which triggers initial recovery steps
138    private static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
139
140    // Default for the data stall alarm while non-aggressive stall detection
141    private static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6;
142    // Default for the data stall alarm for aggressive stall detection
143    private static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60;
144    // Tag for tracking stale alarms
145    private static final String DATA_STALL_ALARM_TAG_EXTRA = "data.stall.alram.tag";
146
147    private static final boolean DATA_STALL_SUSPECTED = true;
148    private static final boolean DATA_STALL_NOT_SUSPECTED = false;
149
150    private String RADIO_RESET_PROPERTY = "gsm.radioreset";
151
152    private static final String INTENT_RECONNECT_ALARM =
153            "com.android.internal.telephony.data-reconnect";
154    private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type";
155    private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON =
156            "reconnect_alarm_extra_reason";
157
158    private static final String INTENT_DATA_STALL_ALARM =
159            "com.android.internal.telephony.data-stall";
160
161    @VisibleForTesting
162    public static class DataAllowFailReason {
163        private HashSet<DataAllowFailReasonType> mDataAllowFailReasonSet = new HashSet<>();
164
165        public void addDataAllowFailReason(DataAllowFailReasonType type) {
166            mDataAllowFailReasonSet.add(type);
167        }
168
169        public String getDataAllowFailReason() {
170            StringBuilder failureReason = new StringBuilder();
171            failureReason.append("isDataAllowed: No");
172            for(DataAllowFailReasonType reason : mDataAllowFailReasonSet) {
173                failureReason.append(reason.mFailReasonStr);
174            }
175            return failureReason.toString();
176        }
177
178        public boolean isFailForSingleReason(DataAllowFailReasonType failReasonType) {
179            return (mDataAllowFailReasonSet.size() == 1) &&
180                    (mDataAllowFailReasonSet.contains(failReasonType));
181        }
182
183        public void clearAllReasons() {
184            mDataAllowFailReasonSet.clear();
185        }
186
187        public boolean isFailed() {
188            return mDataAllowFailReasonSet.size() > 0;
189        }
190    }
191
192    @VisibleForTesting
193    public enum DataAllowFailReasonType {
194        NOT_ATTACHED(" - Not attached"),
195        RECORD_NOT_LOADED(" - SIM not loaded"),
196        ROAMING_DISABLED(" - Roaming and data roaming not enabled"),
197        INVALID_PHONE_STATE(" - PhoneState is not idle"),
198        CONCURRENT_VOICE_DATA_NOT_ALLOWED(" - Concurrent voice and data not allowed"),
199        PS_RESTRICTED(" - mIsPsRestricted= true"),
200        UNDESIRED_POWER_STATE(" - desiredPowerState= false"),
201        INTERNAL_DATA_DISABLED(" - mInternalDataEnabled= false"),
202        DEFAULT_DATA_UNSELECTED(" - defaultDataSelected= false"),
203        RADIO_DISABLED_BY_CARRIER(" - powerStateFromCarrier= false");
204
205        public String mFailReasonStr;
206
207        DataAllowFailReasonType(String reason) {
208            mFailReasonStr = reason;
209        }
210    }
211
212    private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
213    private DcController mDcc;
214
215    /** kept in sync with mApnContexts
216     * Higher numbers are higher priority and sorted so highest priority is first */
217    private final PriorityQueue<ApnContext>mPrioritySortedApnContexts =
218            new PriorityQueue<ApnContext>(5,
219            new Comparator<ApnContext>() {
220                public int compare(ApnContext c1, ApnContext c2) {
221                    return c2.priority - c1.priority;
222                }
223            } );
224
225    /** allApns holds all apns */
226    private ArrayList<ApnSetting> mAllApnSettings = null;
227
228    /** preferred apn */
229    private ApnSetting mPreferredApn = null;
230
231    /** Is packet service restricted by network */
232    private boolean mIsPsRestricted = false;
233
234    /** emergency apn Setting*/
235    private ApnSetting mEmergencyApn = null;
236
237    /* Once disposed dont handle any messages */
238    private boolean mIsDisposed = false;
239
240    private ContentResolver mResolver;
241
242    /* Set to true with CMD_ENABLE_MOBILE_PROVISIONING */
243    private boolean mIsProvisioning = false;
244
245    /* The Url passed as object parameter in CMD_ENABLE_MOBILE_PROVISIONING */
246    private String mProvisioningUrl = null;
247
248    /* Intent for the provisioning apn alarm */
249    private static final String INTENT_PROVISIONING_APN_ALARM =
250            "com.android.internal.telephony.provisioning_apn_alarm";
251
252    /* Tag for tracking stale alarms */
253    private static final String PROVISIONING_APN_ALARM_TAG_EXTRA = "provisioning.apn.alarm.tag";
254
255    /* Debug property for overriding the PROVISIONING_APN_ALARM_DELAY_IN_MS */
256    private static final String DEBUG_PROV_APN_ALARM = "persist.debug.prov_apn_alarm";
257
258    /* Default for the provisioning apn alarm timeout */
259    private static final int PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 15;
260
261    /* The provision apn alarm intent used to disable the provisioning apn */
262    private PendingIntent mProvisioningApnAlarmIntent = null;
263
264    /* Used to track stale provisioning apn alarms */
265    private int mProvisioningApnAlarmTag = (int) SystemClock.elapsedRealtime();
266
267    private AsyncChannel mReplyAc = new AsyncChannel();
268
269    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
270        @Override
271        public void onReceive(Context context, Intent intent) {
272            String action = intent.getAction();
273
274            if (action.equals(Intent.ACTION_SCREEN_ON)) {
275                if (DBG) log("screen on");
276                mIsScreenOn = true;
277                stopNetStatPoll();
278                startNetStatPoll();
279                restartDataStallAlarm();
280            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
281                if (DBG) log("screen off");
282                mIsScreenOn = false;
283                stopNetStatPoll();
284                startNetStatPoll();
285                restartDataStallAlarm();
286            } else if (action.startsWith(INTENT_RECONNECT_ALARM)) {
287                if (DBG) log("Reconnect alarm. Previous state was " + mState);
288                onActionIntentReconnectAlarm(intent);
289            } else if (action.equals(INTENT_DATA_STALL_ALARM)) {
290                if (DBG) log("Data stall alarm");
291                onActionIntentDataStallAlarm(intent);
292            } else if (action.equals(INTENT_PROVISIONING_APN_ALARM)) {
293                if (DBG) log("Provisioning apn alarm");
294                onActionIntentProvisioningApnAlarm(intent);
295            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
296                final android.net.NetworkInfo networkInfo = (NetworkInfo)
297                intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
298                mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
299                if (DBG) log("NETWORK_STATE_CHANGED_ACTION: mIsWifiConnected=" + mIsWifiConnected);
300            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
301                if (DBG) log("Wifi state changed");
302                final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
303                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
304                if (!enabled) {
305                    // when WiFi got disabled, the NETWORK_STATE_CHANGED_ACTION
306                    // quit and won't report disconnected until next enabling.
307                    mIsWifiConnected = false;
308                }
309                if (DBG) {
310                    log("WIFI_STATE_CHANGED_ACTION: enabled=" + enabled
311                            + " mIsWifiConnected=" + mIsWifiConnected);
312                }
313            } else {
314                if (DBG) log("onReceive: Unknown action=" + action);
315            }
316        }
317    };
318
319    private final Runnable mPollNetStat = new Runnable() {
320        @Override
321        public void run() {
322            updateDataActivity();
323
324            if (mIsScreenOn) {
325                mNetStatPollPeriod = Settings.Global.getInt(mResolver,
326                        Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
327            } else {
328                mNetStatPollPeriod = Settings.Global.getInt(mResolver,
329                        Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
330                        POLL_NETSTAT_SCREEN_OFF_MILLIS);
331            }
332
333            if (mNetStatPollEnabled) {
334                mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
335            }
336        }
337    };
338
339    private SubscriptionManager mSubscriptionManager;
340    private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
341            new OnSubscriptionsChangedListener() {
342                public final AtomicInteger mPreviousSubId =
343                        new AtomicInteger(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
344
345                /**
346                 * Callback invoked when there is any change to any SubscriptionInfo. Typically
347                 * this method invokes {@link SubscriptionManager#getActiveSubscriptionInfoList}
348                 */
349                @Override
350                public void onSubscriptionsChanged() {
351                    if (DBG) log("SubscriptionListener.onSubscriptionInfoChanged");
352                    // Set the network type, in case the radio does not restore it.
353                    int subId = mPhone.getSubId();
354                    if (SubscriptionManager.isValidSubscriptionId(subId)) {
355                        registerSettingsObserver();
356                    }
357                    if (mPreviousSubId.getAndSet(subId) != subId &&
358                            SubscriptionManager.isValidSubscriptionId(subId)) {
359                        onRecordsLoadedOrSubIdChanged();
360                    }
361                }
362            };
363
364    private static class SettingsObserver extends ContentObserver {
365        final private HashMap<Uri, Integer> mUriEventMap;
366        final private Context mContext;
367        final private Handler mHandler;
368        final private static String TAG = "DcTracker.SettingsObserver";
369
370        SettingsObserver(Context context, Handler handler) {
371            super(null);
372            mUriEventMap = new HashMap<Uri, Integer>();
373            mContext = context;
374            mHandler = handler;
375        }
376
377        void observe(Uri uri, int what) {
378            mUriEventMap.put(uri, what);
379            final ContentResolver resolver = mContext.getContentResolver();
380            resolver.registerContentObserver(uri, false, this);
381        }
382
383        void unobserve() {
384            final ContentResolver resolver = mContext.getContentResolver();
385            resolver.unregisterContentObserver(this);
386        }
387
388        @Override
389        public void onChange(boolean selfChange) {
390            Rlog.e(TAG, "Should never be reached.");
391        }
392
393        @Override
394        public void onChange(boolean selfChange, Uri uri) {
395            final Integer what = mUriEventMap.get(uri);
396            if (what != null) {
397                mHandler.obtainMessage(what.intValue()).sendToTarget();
398            } else {
399                Rlog.e(TAG, "No matching event to send for URI=" + uri);
400            }
401        }
402    }
403
404    private final SettingsObserver mSettingsObserver;
405
406    private void registerSettingsObserver() {
407        mSettingsObserver.unobserve();
408        String simSuffix = "";
409        if (TelephonyManager.getDefault().getSimCount() > 1) {
410            simSuffix = Integer.toString(mPhone.getSubId());
411        }
412
413        mSettingsObserver.observe(
414                Settings.Global.getUriFor(Settings.Global.DATA_ROAMING + simSuffix),
415                DctConstants.EVENT_ROAMING_ON);
416        mSettingsObserver.observe(
417                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
418                DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
419        mSettingsObserver.observe(
420                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
421                DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
422    }
423
424    /**
425     * Maintain the sum of transmit and receive packets.
426     *
427     * The packet counts are initialized and reset to -1 and
428     * remain -1 until they can be updated.
429     */
430    public static class TxRxSum {
431        public long txPkts;
432        public long rxPkts;
433
434        public TxRxSum() {
435            reset();
436        }
437
438        public TxRxSum(long txPkts, long rxPkts) {
439            this.txPkts = txPkts;
440            this.rxPkts = rxPkts;
441        }
442
443        public TxRxSum(TxRxSum sum) {
444            txPkts = sum.txPkts;
445            rxPkts = sum.rxPkts;
446        }
447
448        public void reset() {
449            txPkts = -1;
450            rxPkts = -1;
451        }
452
453        @Override
454        public String toString() {
455            return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
456        }
457
458        public void updateTxRxSum() {
459            this.txPkts = TrafficStats.getMobileTcpTxPackets();
460            this.rxPkts = TrafficStats.getMobileTcpRxPackets();
461        }
462    }
463
464    private void onActionIntentReconnectAlarm(Intent intent) {
465        String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
466        String apnType = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
467
468        int phoneSubId = mPhone.getSubId();
469        int currSubId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
470                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
471        log("onActionIntentReconnectAlarm: currSubId = " + currSubId + " phoneSubId=" + phoneSubId);
472
473        // Stop reconnect if not current subId is not correct.
474        // FIXME STOPSHIP - phoneSubId is coming up as -1 way after boot and failing this?
475        if (!SubscriptionManager.isValidSubscriptionId(currSubId) || (currSubId != phoneSubId)) {
476            log("receive ReconnectAlarm but subId incorrect, ignore");
477            return;
478        }
479
480        ApnContext apnContext = mApnContexts.get(apnType);
481
482        if (DBG) {
483            log("onActionIntentReconnectAlarm: mState=" + mState + " reason=" + reason +
484                    " apnType=" + apnType + " apnContext=" + apnContext +
485                    " mDataConnectionAsyncChannels=" + mDataConnectionAcHashMap);
486        }
487
488        if ((apnContext != null) && (apnContext.isEnabled())) {
489            apnContext.setReason(reason);
490            DctConstants.State apnContextState = apnContext.getState();
491            if (DBG) {
492                log("onActionIntentReconnectAlarm: apnContext state=" + apnContextState);
493            }
494            if ((apnContextState == DctConstants.State.FAILED)
495                    || (apnContextState == DctConstants.State.IDLE)) {
496                if (DBG) {
497                    log("onActionIntentReconnectAlarm: state is FAILED|IDLE, disassociate");
498                }
499                DcAsyncChannel dcac = apnContext.getDcAc();
500                if (dcac != null) {
501                    if (DBG) {
502                        log("onActionIntentReconnectAlarm: tearDown apnContext=" + apnContext);
503                    }
504                    dcac.tearDown(apnContext, "", null);
505                }
506                apnContext.setDataConnectionAc(null);
507                apnContext.setState(DctConstants.State.IDLE);
508            } else {
509                if (DBG) log("onActionIntentReconnectAlarm: keep associated");
510            }
511            // TODO: IF already associated should we send the EVENT_TRY_SETUP_DATA???
512            sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
513
514            apnContext.setReconnectIntent(null);
515        }
516    }
517
518    private void onActionIntentDataStallAlarm(Intent intent) {
519        if (VDBG_STALL) log("onActionIntentDataStallAlarm: action=" + intent.getAction());
520        Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM,
521                intent.getAction());
522        msg.arg1 = intent.getIntExtra(DATA_STALL_ALARM_TAG_EXTRA, 0);
523        sendMessage(msg);
524    }
525
526    private final ConnectivityManager mCm;
527
528    /**
529     * List of messages that are waiting to be posted, when data call disconnect
530     * is complete
531     */
532    private ArrayList<Message> mDisconnectAllCompleteMsgList = new ArrayList<Message>();
533
534    private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
535
536    // member variables
537    private final Phone mPhone;
538    private final UiccController mUiccController;
539    private final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
540    private DctConstants.Activity mActivity = DctConstants.Activity.NONE;
541    private DctConstants.State mState = DctConstants.State.IDLE;
542    private final Handler mDataConnectionTracker;
543
544    private long mTxPkts;
545    private long mRxPkts;
546    private int mNetStatPollPeriod;
547    private boolean mNetStatPollEnabled = false;
548
549    private TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0);
550    // Used to track stale data stall alarms.
551    private int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime();
552    // The current data stall alarm intent
553    private PendingIntent mDataStallAlarmIntent = null;
554    // Number of packets sent since the last received packet
555    private long mSentSinceLastRecv;
556    // Controls when a simple recovery attempt it to be tried
557    private int mNoRecvPollCount = 0;
558    // Reference counter for enabling fail fast
559    private static int sEnableFailFastRefCounter = 0;
560    // True if data stall detection is enabled
561    private volatile boolean mDataStallDetectionEnabled = true;
562
563    private volatile boolean mFailFast = false;
564
565    // True when in voice call
566    private boolean mInVoiceCall = false;
567
568    // wifi connection status will be updated by sticky intent
569    private boolean mIsWifiConnected = false;
570
571    /** Intent sent when the reconnect alarm fires. */
572    private PendingIntent mReconnectIntent = null;
573
574    // When false we will not auto attach and manually attaching is required.
575    private boolean mAutoAttachOnCreationConfig = false;
576    private AtomicBoolean mAutoAttachOnCreation = new AtomicBoolean(false);
577
578    // State of screen
579    // (TODO: Reconsider tying directly to screen, maybe this is
580    //        really a lower power mode")
581    private boolean mIsScreenOn = true;
582
583    // Indicates if we found mvno-specific APNs in the full APN list.
584    // used to determine if we can accept mno-specific APN for tethering.
585    private boolean mMvnoMatched = false;
586
587    /** Allows the generation of unique Id's for DataConnection objects */
588    private AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
589
590    /** The data connections. */
591    private HashMap<Integer, DataConnection> mDataConnections =
592            new HashMap<Integer, DataConnection>();
593
594    /** The data connection async channels */
595    private HashMap<Integer, DcAsyncChannel> mDataConnectionAcHashMap =
596            new HashMap<Integer, DcAsyncChannel>();
597
598    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
599    private HashMap<String, Integer> mApnToDataConnectionId = new HashMap<String, Integer>();
600
601    /** Phone.APN_TYPE_* ===> ApnContext */
602    private final ConcurrentHashMap<String, ApnContext> mApnContexts =
603            new ConcurrentHashMap<String, ApnContext>();
604
605    private final SparseArray<ApnContext> mApnContextsById = new SparseArray<ApnContext>();
606
607    private int mDisconnectPendingCount = 0;
608
609    /** Indicate if metered APNs are disabled.
610     *  set to block all the metered APNs from continuously sending requests, which causes
611     *  undesired network load */
612    private boolean mMeteredApnDisabled = false;
613
614    /**
615     * int to remember whether has setDataProfiles and with roaming or not.
616     * 0: default, has never set data profile
617     * 1: has set data profile with home protocol
618     * 2: has set data profile with roaming protocol
619     * This is not needed once RIL command is updated to support both home and roaming protocol.
620     */
621    private int mSetDataProfileStatus = 0;
622
623    /**
624     * Handles changes to the APN db.
625     */
626    private class ApnChangeObserver extends ContentObserver {
627        public ApnChangeObserver () {
628            super(mDataConnectionTracker);
629        }
630
631        @Override
632        public void onChange(boolean selfChange) {
633            sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
634        }
635    }
636
637    //***** Instance Variables
638
639    private boolean mReregisterOnReconnectFailure = false;
640
641
642    //***** Constants
643
644    // Used by puppetmaster/*/radio_stress.py
645    private static final String PUPPET_MASTER_RADIO_STRESS_TEST = "gsm.defaultpdpcontext.active";
646
647    private static final int POLL_PDP_MILLIS = 5 * 1000;
648
649    private static final int PROVISIONING_SPINNER_TIMEOUT_MILLIS = 120 * 1000;
650
651    static final Uri PREFERAPN_NO_UPDATE_URI_USING_SUBID =
652                        Uri.parse("content://telephony/carriers/preferapn_no_update/subId/");
653    static final String APN_ID = "apn_id";
654
655    private boolean mCanSetPreferApn = false;
656
657    private AtomicBoolean mAttached = new AtomicBoolean(false);
658
659    /** Watches for changes to the APN db. */
660    private ApnChangeObserver mApnObserver;
661
662    private final String mProvisionActionName;
663    private BroadcastReceiver mProvisionBroadcastReceiver;
664    private ProgressDialog mProvisioningSpinner;
665
666    public boolean mImsRegistrationState = false;
667
668    //***** Constructor
669    public DcTracker(Phone phone) {
670        super();
671        mPhone = phone;
672
673        if (DBG) log("DCT.constructor");
674
675        mResolver = mPhone.getContext().getContentResolver();
676        mUiccController = UiccController.getInstance();
677        mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null);
678        mAlarmManager =
679                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
680        mCm = (ConnectivityManager) mPhone.getContext().getSystemService(
681                Context.CONNECTIVITY_SERVICE);
682
683
684        IntentFilter filter = new IntentFilter();
685        filter.addAction(Intent.ACTION_SCREEN_ON);
686        filter.addAction(Intent.ACTION_SCREEN_OFF);
687        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
688        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
689        filter.addAction(INTENT_DATA_STALL_ALARM);
690        filter.addAction(INTENT_PROVISIONING_APN_ALARM);
691
692        // TODO - redundent with update call below?
693        mDataEnabledSettings.setUserDataEnabled(getDataEnabled());
694
695        mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
696
697        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
698        mAutoAttachOnCreation.set(sp.getBoolean(Phone.DATA_DISABLED_ON_BOOT_KEY, false));
699
700        mSubscriptionManager = SubscriptionManager.from(mPhone.getContext());
701        mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
702
703        HandlerThread dcHandlerThread = new HandlerThread("DcHandlerThread");
704        dcHandlerThread.start();
705        Handler dcHandler = new Handler(dcHandlerThread.getLooper());
706        mDcc = DcController.makeDcc(mPhone, this, dcHandler);
707        mDcTesterFailBringUpAll = new DcTesterFailBringUpAll(mPhone, dcHandler);
708
709        mDataConnectionTracker = this;
710        registerForAllEvents();
711        update();
712        mApnObserver = new ApnChangeObserver();
713        phone.getContext().getContentResolver().registerContentObserver(
714                Telephony.Carriers.CONTENT_URI, true, mApnObserver);
715
716        initApnContexts();
717
718        for (ApnContext apnContext : mApnContexts.values()) {
719            // Register the reconnect and restart actions.
720            filter = new IntentFilter();
721            filter.addAction(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
722            mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
723        }
724
725        // Add Emergency APN to APN setting list by default to support EPDN in sim absent cases
726        initEmergencyApnSetting();
727        addEmergencyApnSetting();
728
729        mProvisionActionName = "com.android.internal.telephony.PROVISION" + phone.getPhoneId();
730
731        mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
732        registerSettingsObserver();
733    }
734
735    @VisibleForTesting
736    public DcTracker() {
737        mAlarmManager = null;
738        mCm = null;
739        mPhone = null;
740        mUiccController = null;
741        mDataConnectionTracker = null;
742        mProvisionActionName = null;
743        mSettingsObserver = new SettingsObserver(null, this);
744    }
745
746    public void registerServiceStateTrackerEvents() {
747        mPhone.getServiceStateTracker().registerForDataConnectionAttached(this,
748                DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
749        mPhone.getServiceStateTracker().registerForDataConnectionDetached(this,
750                DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
751        mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
752                DctConstants.EVENT_ROAMING_ON, null);
753        mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
754                DctConstants.EVENT_ROAMING_OFF, null);
755        mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
756                DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
757        mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
758                DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
759        mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(this,
760                DctConstants.EVENT_DATA_RAT_CHANGED, null);
761    }
762
763    public void unregisterServiceStateTrackerEvents() {
764        mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
765        mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
766        mPhone.getServiceStateTracker().unregisterForDataRoamingOn(this);
767        mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this);
768        mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
769        mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
770        mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(this);
771    }
772
773    private void registerForAllEvents() {
774        mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
775        mPhone.mCi.registerForOffOrNotAvailable(this,
776                DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
777        mPhone.mCi.registerForDataNetworkStateChanged(this,
778                DctConstants.EVENT_DATA_STATE_CHANGED, null);
779        // Note, this is fragile - the Phone is now presenting a merged picture
780        // of PS (volte) & CS and by diving into its internals you're just seeing
781        // the CS data.  This works well for the purposes this is currently used for
782        // but that may not always be the case.  Should probably be redesigned to
783        // accurately reflect what we're really interested in (registerForCSVoiceCallEnded).
784        mPhone.getCallTracker().registerForVoiceCallEnded(this,
785                DctConstants.EVENT_VOICE_CALL_ENDED, null);
786        mPhone.getCallTracker().registerForVoiceCallStarted(this,
787                DctConstants.EVENT_VOICE_CALL_STARTED, null);
788        registerServiceStateTrackerEvents();
789     //   SubscriptionManager.registerForDdsSwitch(this,
790     //          DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
791        mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
792    }
793
794    public void dispose() {
795        if (DBG) log("DCT.dispose");
796
797        if (mProvisionBroadcastReceiver != null) {
798            mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
799            mProvisionBroadcastReceiver = null;
800        }
801        if (mProvisioningSpinner != null) {
802            mProvisioningSpinner.dismiss();
803            mProvisioningSpinner = null;
804        }
805
806        cleanUpAllConnections(true, null);
807
808        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
809            dcac.disconnect();
810        }
811        mDataConnectionAcHashMap.clear();
812        mIsDisposed = true;
813        mPhone.getContext().unregisterReceiver(mIntentReceiver);
814        mUiccController.unregisterForIccChanged(this);
815        mSettingsObserver.unobserve();
816
817        mSubscriptionManager
818                .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
819        mDcc.dispose();
820        mDcTesterFailBringUpAll.dispose();
821
822        mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
823        mApnContexts.clear();
824        mApnContextsById.clear();
825        mPrioritySortedApnContexts.clear();
826        unregisterForAllEvents();
827
828        destroyDataConnections();
829    }
830
831    private void unregisterForAllEvents() {
832         //Unregister for all events
833        mPhone.mCi.unregisterForAvailable(this);
834        mPhone.mCi.unregisterForOffOrNotAvailable(this);
835        IccRecords r = mIccRecords.get();
836        if (r != null) {
837            r.unregisterForRecordsLoaded(this);
838            mIccRecords.set(null);
839        }
840        mPhone.mCi.unregisterForDataNetworkStateChanged(this);
841        mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
842        mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
843        unregisterServiceStateTrackerEvents();
844        //SubscriptionManager.unregisterForDdsSwitch(this);
845        mPhone.mCi.unregisterForPcoData(this);
846    }
847
848    /**
849     * Called when EVENT_RESET_DONE is received so goto
850     * IDLE state and send notifications to those interested.
851     *
852     * TODO - currently unused.  Needs to be hooked into DataConnection cleanup
853     * TODO - needs to pass some notion of which connection is reset..
854     */
855    private void onResetDone(AsyncResult ar) {
856        if (DBG) log("EVENT_RESET_DONE");
857        String reason = null;
858        if (ar.userObj instanceof String) {
859            reason = (String) ar.userObj;
860        }
861        gotoIdleAndNotifyDataConnection(reason);
862    }
863
864    /**
865     * Modify {@link android.provider.Settings.Global#MOBILE_DATA} value.
866     */
867    public void setDataEnabled(boolean enable) {
868        Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
869        msg.arg1 = enable ? 1 : 0;
870        if (DBG) log("setDataEnabled: sendMessage: enable=" + enable);
871        sendMessage(msg);
872    }
873
874    private void onSetUserDataEnabled(boolean enabled) {
875        synchronized (mDataEnabledSettings) {
876            if (mDataEnabledSettings.isUserDataEnabled() != enabled) {
877                mDataEnabledSettings.setUserDataEnabled(enabled);
878
879                //TODO: We should move the followings into DataEnabledSettings class.
880                // For single SIM phones, this is a per phone property.
881                if (TelephonyManager.getDefault().getSimCount() == 1) {
882                    Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
883                } else {
884                    int phoneSubId = mPhone.getSubId();
885                    Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA + phoneSubId,
886                            enabled ? 1 : 0);
887                }
888                if (getDataOnRoamingEnabled() == false &&
889                        mPhone.getServiceState().getDataRoaming() == true) {
890                    if (enabled) {
891                        notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
892                    } else {
893                        notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
894                    }
895                }
896
897                // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
898                // handle the rest from there.
899                if (enabled) {
900                    teardownRestrictedMeteredConnections();
901                    onTrySetupData(Phone.REASON_DATA_ENABLED);
902                } else {
903                    onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
904                }
905            }
906        }
907    }
908
909    /**
910     * Handle reverting restricted networks back to unrestricted.
911     * If we're changing user data to enabled and this makes data
912     * truely enabled (not disabled by other factors) we need to
913     * tear down any metered apn type that was enabled anyway by
914     * a privileged request.  This allows us to reconnect
915     * to it in an unrestricted way.
916     */
917    private void teardownRestrictedMeteredConnections() {
918        if (mDataEnabledSettings.isDataEnabled(true)) {
919            for (ApnContext apnContext : mApnContexts.values()) {
920                if (apnContext.isConnectedOrConnecting() &&
921                        apnContext.getApnSetting().isMetered(mPhone.getContext(),
922                        mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())) {
923
924                    final DcAsyncChannel dataConnectionAc = apnContext.getDcAc();
925                    if (dataConnectionAc != null) {
926                        final NetworkCapabilities nc =
927                                dataConnectionAc.getNetworkCapabilitiesSync();
928                        if (nc != null && nc.hasCapability(NetworkCapabilities.
929                              NET_CAPABILITY_NOT_RESTRICTED)) {
930                            if (DBG) log("not tearing down unrestricted metered net:" + apnContext);
931                            continue;
932                        }
933                    }
934                    if (DBG) log("tearing down restricted metered net: " + apnContext);
935                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
936                    cleanUpConnection(true, apnContext);
937                }
938            }
939        }
940    }
941
942    private void onDeviceProvisionedChange() {
943        if (getDataEnabled()) {
944            mDataEnabledSettings.setUserDataEnabled(true);
945            teardownRestrictedMeteredConnections();
946            onTrySetupData(Phone.REASON_DATA_ENABLED);
947        } else {
948            mDataEnabledSettings.setUserDataEnabled(false);
949            onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
950        }
951    }
952
953
954    public long getSubId() {
955        return mPhone.getSubId();
956    }
957
958    public DctConstants.Activity getActivity() {
959        return mActivity;
960    }
961
962    private void setActivity(DctConstants.Activity activity) {
963        log("setActivity = " + activity);
964        mActivity = activity;
965        mPhone.notifyDataActivity();
966    }
967
968    public void requestNetwork(NetworkRequest networkRequest, LocalLog log) {
969        final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
970        final ApnContext apnContext = mApnContextsById.get(apnId);
971        log.log("DcTracker.requestNetwork for " + networkRequest + " found " + apnContext);
972        if (apnContext != null) apnContext.requestNetwork(networkRequest, log);
973    }
974
975    public void releaseNetwork(NetworkRequest networkRequest, LocalLog log) {
976        final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
977        final ApnContext apnContext = mApnContextsById.get(apnId);
978        log.log("DcTracker.releaseNetwork for " + networkRequest + " found " + apnContext);
979        if (apnContext != null) apnContext.releaseNetwork(networkRequest, log);
980    }
981
982    public boolean isApnSupported(String name) {
983        if (name == null) {
984            loge("isApnSupported: name=null");
985            return false;
986        }
987        ApnContext apnContext = mApnContexts.get(name);
988        if (apnContext == null) {
989            loge("Request for unsupported mobile name: " + name);
990            return false;
991        }
992        return true;
993    }
994
995    public int getApnPriority(String name) {
996        ApnContext apnContext = mApnContexts.get(name);
997        if (apnContext == null) {
998            loge("Request for unsupported mobile name: " + name);
999        }
1000        return apnContext.priority;
1001    }
1002
1003    // Turn telephony radio on or off.
1004    private void setRadio(boolean on) {
1005        final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1006        try {
1007            phone.setRadio(on);
1008        } catch (Exception e) {
1009            // Ignore.
1010        }
1011    }
1012
1013    // Class to handle Intent dispatched with user selects the "Sign-in to network"
1014    // notification.
1015    private class ProvisionNotificationBroadcastReceiver extends BroadcastReceiver {
1016        private final String mNetworkOperator;
1017        // Mobile provisioning URL.  Valid while provisioning notification is up.
1018        // Set prior to notification being posted as URL contains ICCID which
1019        // disappears when radio is off (which is the case when notification is up).
1020        private final String mProvisionUrl;
1021
1022        public ProvisionNotificationBroadcastReceiver(String provisionUrl, String networkOperator) {
1023            mNetworkOperator = networkOperator;
1024            mProvisionUrl = provisionUrl;
1025        }
1026
1027        private void setEnableFailFastMobileData(int enabled) {
1028            sendMessage(obtainMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled, 0));
1029        }
1030
1031        private void enableMobileProvisioning() {
1032            final Message msg = obtainMessage(DctConstants.CMD_ENABLE_MOBILE_PROVISIONING);
1033            msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl));
1034            sendMessage(msg);
1035        }
1036
1037        @Override
1038        public void onReceive(Context context, Intent intent) {
1039            // Turning back on the radio can take time on the order of a minute, so show user a
1040            // spinner so they know something is going on.
1041            log("onReceive : ProvisionNotificationBroadcastReceiver");
1042            mProvisioningSpinner = new ProgressDialog(context);
1043            mProvisioningSpinner.setTitle(mNetworkOperator);
1044            mProvisioningSpinner.setMessage(
1045                    // TODO: Don't borrow "Connecting..." i18n string; give Telephony a version.
1046                    context.getText(com.android.internal.R.string.media_route_status_connecting));
1047            mProvisioningSpinner.setIndeterminate(true);
1048            mProvisioningSpinner.setCancelable(true);
1049            // Allow non-Activity Service Context to create a View.
1050            mProvisioningSpinner.getWindow().setType(
1051                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1052            mProvisioningSpinner.show();
1053            // After timeout, hide spinner so user can at least use their device.
1054            // TODO: Indicate to user that it is taking an unusually long time to connect?
1055            sendMessageDelayed(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
1056                    mProvisioningSpinner), PROVISIONING_SPINNER_TIMEOUT_MILLIS);
1057            // This code is almost identical to the old
1058            // ConnectivityService.handleMobileProvisioningAction code.
1059            setRadio(true);
1060            setEnableFailFastMobileData(DctConstants.ENABLED);
1061            enableMobileProvisioning();
1062        }
1063    }
1064
1065    public boolean isDataPossible(String apnType) {
1066        ApnContext apnContext = mApnContexts.get(apnType);
1067        if (apnContext == null) {
1068            return false;
1069        }
1070        boolean apnContextIsEnabled = apnContext.isEnabled();
1071        DctConstants.State apnContextState = apnContext.getState();
1072        boolean apnTypePossible = !(apnContextIsEnabled &&
1073                (apnContextState == DctConstants.State.FAILED));
1074        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
1075        // Set the emergency APN availability status as TRUE irrespective of conditions checked in
1076        // isDataAllowed() like IN_SERVICE, MOBILE DATA status etc.
1077        boolean dataAllowed = isEmergencyApn || isDataAllowed(null);
1078        boolean possible = dataAllowed && apnTypePossible;
1079
1080        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
1081                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
1082                && (mPhone.getServiceState().getRilDataRadioTechnology()
1083                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
1084            log("Default data call activation not possible in iwlan.");
1085            possible = false;
1086        }
1087
1088        if (VDBG) {
1089            log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
1090                            "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
1091                    apnType, possible, dataAllowed, apnTypePossible,
1092                    apnContextIsEnabled, apnContextState));
1093        }
1094        return possible;
1095    }
1096
1097    @Override
1098    protected void finalize() {
1099        if(DBG && mPhone != null) log("finalize");
1100    }
1101
1102    private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
1103        ApnContext apnContext = new ApnContext(mPhone, type, LOG_TAG, networkConfig, this);
1104        mApnContexts.put(type, apnContext);
1105        mApnContextsById.put(ApnContext.apnIdForApnName(type), apnContext);
1106        mPrioritySortedApnContexts.add(apnContext);
1107        return apnContext;
1108    }
1109
1110    private void initApnContexts() {
1111        log("initApnContexts: E");
1112        // Load device network attributes from resources
1113        String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
1114                com.android.internal.R.array.networkAttributes);
1115        for (String networkConfigString : networkConfigStrings) {
1116            NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
1117            ApnContext apnContext = null;
1118
1119            switch (networkConfig.type) {
1120            case ConnectivityManager.TYPE_MOBILE:
1121                apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT, networkConfig);
1122                break;
1123            case ConnectivityManager.TYPE_MOBILE_MMS:
1124                apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS, networkConfig);
1125                break;
1126            case ConnectivityManager.TYPE_MOBILE_SUPL:
1127                apnContext = addApnContext(PhoneConstants.APN_TYPE_SUPL, networkConfig);
1128                break;
1129            case ConnectivityManager.TYPE_MOBILE_DUN:
1130                apnContext = addApnContext(PhoneConstants.APN_TYPE_DUN, networkConfig);
1131                break;
1132            case ConnectivityManager.TYPE_MOBILE_HIPRI:
1133                apnContext = addApnContext(PhoneConstants.APN_TYPE_HIPRI, networkConfig);
1134                break;
1135            case ConnectivityManager.TYPE_MOBILE_FOTA:
1136                apnContext = addApnContext(PhoneConstants.APN_TYPE_FOTA, networkConfig);
1137                break;
1138            case ConnectivityManager.TYPE_MOBILE_IMS:
1139                apnContext = addApnContext(PhoneConstants.APN_TYPE_IMS, networkConfig);
1140                break;
1141            case ConnectivityManager.TYPE_MOBILE_CBS:
1142                apnContext = addApnContext(PhoneConstants.APN_TYPE_CBS, networkConfig);
1143                break;
1144            case ConnectivityManager.TYPE_MOBILE_IA:
1145                apnContext = addApnContext(PhoneConstants.APN_TYPE_IA, networkConfig);
1146                break;
1147            case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
1148                apnContext = addApnContext(PhoneConstants.APN_TYPE_EMERGENCY, networkConfig);
1149                break;
1150            default:
1151                log("initApnContexts: skipping unknown type=" + networkConfig.type);
1152                continue;
1153            }
1154            log("initApnContexts: apnContext=" + apnContext);
1155        }
1156
1157        if (VDBG) log("initApnContexts: X mApnContexts=" + mApnContexts);
1158    }
1159
1160    public LinkProperties getLinkProperties(String apnType) {
1161        ApnContext apnContext = mApnContexts.get(apnType);
1162        if (apnContext != null) {
1163            DcAsyncChannel dcac = apnContext.getDcAc();
1164            if (dcac != null) {
1165                if (DBG) log("return link properites for " + apnType);
1166                return dcac.getLinkPropertiesSync();
1167            }
1168        }
1169        if (DBG) log("return new LinkProperties");
1170        return new LinkProperties();
1171    }
1172
1173    public NetworkCapabilities getNetworkCapabilities(String apnType) {
1174        ApnContext apnContext = mApnContexts.get(apnType);
1175        if (apnContext!=null) {
1176            DcAsyncChannel dataConnectionAc = apnContext.getDcAc();
1177            if (dataConnectionAc != null) {
1178                if (DBG) {
1179                    log("get active pdp is not null, return NetworkCapabilities for " + apnType);
1180                }
1181                return dataConnectionAc.getNetworkCapabilitiesSync();
1182            }
1183        }
1184        if (DBG) log("return new NetworkCapabilities");
1185        return new NetworkCapabilities();
1186    }
1187
1188    // Return all active apn types
1189    public String[] getActiveApnTypes() {
1190        if (DBG) log("get all active apn types");
1191        ArrayList<String> result = new ArrayList<String>();
1192
1193        for (ApnContext apnContext : mApnContexts.values()) {
1194            if (mAttached.get() && apnContext.isReady()) {
1195                result.add(apnContext.getApnType());
1196            }
1197        }
1198
1199        return result.toArray(new String[0]);
1200    }
1201
1202    // Return active apn of specific apn type
1203    public String getActiveApnString(String apnType) {
1204        if (VDBG) log( "get active apn string for type:" + apnType);
1205        ApnContext apnContext = mApnContexts.get(apnType);
1206        if (apnContext != null) {
1207            ApnSetting apnSetting = apnContext.getApnSetting();
1208            if (apnSetting != null) {
1209                return apnSetting.apn;
1210            }
1211        }
1212        return null;
1213    }
1214
1215    // Return state of specific apn type
1216    public DctConstants.State getState(String apnType) {
1217        ApnContext apnContext = mApnContexts.get(apnType);
1218        if (apnContext != null) {
1219            return apnContext.getState();
1220        }
1221        return DctConstants.State.FAILED;
1222    }
1223
1224    // Return if apn type is a provisioning apn.
1225    private boolean isProvisioningApn(String apnType) {
1226        ApnContext apnContext = mApnContexts.get(apnType);
1227        if (apnContext != null) {
1228            return apnContext.isProvisioningApn();
1229        }
1230        return false;
1231    }
1232
1233    // Return state of overall
1234    public DctConstants.State getOverallState() {
1235        boolean isConnecting = false;
1236        boolean isFailed = true; // All enabled Apns should be FAILED.
1237        boolean isAnyEnabled = false;
1238
1239        for (ApnContext apnContext : mApnContexts.values()) {
1240            if (apnContext.isEnabled()) {
1241                isAnyEnabled = true;
1242                switch (apnContext.getState()) {
1243                case CONNECTED:
1244                case DISCONNECTING:
1245                    if (VDBG) log("overall state is CONNECTED");
1246                    return DctConstants.State.CONNECTED;
1247                case RETRYING:
1248                case CONNECTING:
1249                    isConnecting = true;
1250                    isFailed = false;
1251                    break;
1252                case IDLE:
1253                case SCANNING:
1254                    isFailed = false;
1255                    break;
1256                default:
1257                    isAnyEnabled = true;
1258                    break;
1259                }
1260            }
1261        }
1262
1263        if (!isAnyEnabled) { // Nothing enabled. return IDLE.
1264            if (VDBG) log( "overall state is IDLE");
1265            return DctConstants.State.IDLE;
1266        }
1267
1268        if (isConnecting) {
1269            if (VDBG) log( "overall state is CONNECTING");
1270            return DctConstants.State.CONNECTING;
1271        } else if (!isFailed) {
1272            if (VDBG) log( "overall state is IDLE");
1273            return DctConstants.State.IDLE;
1274        } else {
1275            if (VDBG) log( "overall state is FAILED");
1276            return DctConstants.State.FAILED;
1277        }
1278    }
1279
1280    /**
1281     * Report on whether data connectivity is enabled for any APN.
1282     * @return {@code false} if data connectivity has been explicitly disabled,
1283     * {@code true} otherwise.
1284     */
1285    public boolean getAnyDataEnabled() {
1286        if (!mDataEnabledSettings.isDataEnabled(true)) return false;
1287        DataAllowFailReason failureReason = new DataAllowFailReason();
1288        if (!isDataAllowed(failureReason)) {
1289            if (DBG) log(failureReason.getDataAllowFailReason());
1290            return false;
1291        }
1292        for (ApnContext apnContext : mApnContexts.values()) {
1293            // Make sure we don't have a context that is going down
1294            // and is explicitly disabled.
1295            if (isDataAllowedForApn(apnContext)) {
1296                return true;
1297            }
1298        }
1299        return false;
1300    }
1301
1302    @VisibleForTesting
1303    public boolean isDataEnabled(boolean checkUserDataEnabled) {
1304        return mDataEnabledSettings.isDataEnabled(checkUserDataEnabled);
1305    }
1306
1307    private boolean isDataAllowedForApn(ApnContext apnContext) {
1308        //If RAT is iwlan then dont allow default/IA PDP at all.
1309        //Rest of APN types can be evaluated for remaining conditions.
1310        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
1311                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
1312                && (mPhone.getServiceState().getRilDataRadioTechnology()
1313                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
1314            log("Default data call activation not allowed in iwlan.");
1315            return false;
1316        }
1317
1318        return apnContext.isReady();
1319    }
1320
1321    //****** Called from ServiceStateTracker
1322    /**
1323     * Invoked when ServiceStateTracker observes a transition from GPRS
1324     * attach to detach.
1325     */
1326    private void onDataConnectionDetached() {
1327        /*
1328         * We presently believe it is unnecessary to tear down the PDP context
1329         * when GPRS detaches, but we should stop the network polling.
1330         */
1331        if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
1332        stopNetStatPoll();
1333        stopDataStallAlarm();
1334        notifyDataConnection(Phone.REASON_DATA_DETACHED);
1335        mAttached.set(false);
1336    }
1337
1338    private void onDataConnectionAttached() {
1339        if (DBG) log("onDataConnectionAttached");
1340        mAttached.set(true);
1341        if (getOverallState() == DctConstants.State.CONNECTED) {
1342            if (DBG) log("onDataConnectionAttached: start polling notify attached");
1343            startNetStatPoll();
1344            startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1345            notifyDataConnection(Phone.REASON_DATA_ATTACHED);
1346        } else {
1347            // update APN availability so that APN can be enabled.
1348            notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
1349        }
1350        if (mAutoAttachOnCreationConfig) {
1351            mAutoAttachOnCreation.set(true);
1352        }
1353        setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
1354    }
1355
1356    private boolean isDataAllowed(DataAllowFailReason failureReason) {
1357        final boolean internalDataEnabled;
1358        internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();
1359
1360        boolean attachedState = mAttached.get();
1361        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
1362        boolean radioStateFromCarrier = mPhone.getServiceStateTracker().getPowerStateFromCarrier();
1363        int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
1364        if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
1365            desiredPowerState = true;
1366            radioStateFromCarrier = true;
1367        }
1368
1369        IccRecords r = mIccRecords.get();
1370        boolean recordsLoaded = false;
1371        if (r != null) {
1372            recordsLoaded = r.getRecordsLoaded();
1373            if (DBG && !recordsLoaded) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
1374        }
1375
1376        int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
1377        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(dataSub);
1378
1379        PhoneConstants.State state = PhoneConstants.State.IDLE;
1380        // Note this is explicitly not using mPhone.getState.  See b/19090488.
1381        // mPhone.getState reports the merge of CS and PS (volte) voice call state
1382        // but we only care about CS calls here for data/voice concurrency issues.
1383        // Calling getCallTracker currently gives you just the CS side where the
1384        // ImsCallTracker is held internally where applicable.
1385        // This should be redesigned to ask explicitly what we want:
1386        // voiceCallStateAllowDataCall, or dataCallAllowed or something similar.
1387        if (mPhone.getCallTracker() != null) {
1388            state = mPhone.getCallTracker().getState();
1389        }
1390
1391        if (failureReason != null) failureReason.clearAllReasons();
1392        if (!(attachedState || mAutoAttachOnCreation.get())) {
1393            if(failureReason == null) return false;
1394            failureReason.addDataAllowFailReason(DataAllowFailReasonType.NOT_ATTACHED);
1395        }
1396        if (!recordsLoaded) {
1397            if(failureReason == null) return false;
1398            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RECORD_NOT_LOADED);
1399        }
1400        if (state != PhoneConstants.State.IDLE &&
1401                !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1402            if(failureReason == null) return false;
1403            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INVALID_PHONE_STATE);
1404            failureReason.addDataAllowFailReason(
1405                    DataAllowFailReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
1406        }
1407        if (!internalDataEnabled) {
1408            if(failureReason == null) return false;
1409            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INTERNAL_DATA_DISABLED);
1410        }
1411        if (!defaultDataSelected) {
1412            if(failureReason == null) return false;
1413            failureReason.addDataAllowFailReason(
1414                    DataAllowFailReasonType.DEFAULT_DATA_UNSELECTED);
1415        }
1416        if (mPhone.getServiceState().getDataRoaming() && !getDataOnRoamingEnabled()) {
1417            if(failureReason == null) return false;
1418            failureReason.addDataAllowFailReason(DataAllowFailReasonType.ROAMING_DISABLED);
1419        }
1420        if (mIsPsRestricted) {
1421            if(failureReason == null) return false;
1422            failureReason.addDataAllowFailReason(DataAllowFailReasonType.PS_RESTRICTED);
1423        }
1424        if (!desiredPowerState) {
1425            if(failureReason == null) return false;
1426            failureReason.addDataAllowFailReason(DataAllowFailReasonType.UNDESIRED_POWER_STATE);
1427        }
1428        if (!radioStateFromCarrier) {
1429            if(failureReason == null) return false;
1430            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RADIO_DISABLED_BY_CARRIER);
1431        }
1432
1433        return failureReason == null || !failureReason.isFailed();
1434    }
1435
1436    // arg for setupDataOnConnectableApns
1437    private enum RetryFailures {
1438        // retry failed networks always (the old default)
1439        ALWAYS,
1440        // retry only when a substantial change has occurred.  Either:
1441        // 1) we were restricted by voice/data concurrency and aren't anymore
1442        // 2) our apn list has change
1443        ONLY_ON_CHANGE
1444    };
1445
1446    private void setupDataOnConnectableApns(String reason) {
1447        setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
1448    }
1449
1450    private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
1451        if (VDBG) log("setupDataOnConnectableApns: " + reason);
1452
1453        if (DBG && !VDBG) {
1454            StringBuilder sb = new StringBuilder(120);
1455            for (ApnContext apnContext : mPrioritySortedApnContexts) {
1456                sb.append(apnContext.getApnType());
1457                sb.append(":[state=");
1458                sb.append(apnContext.getState());
1459                sb.append(",enabled=");
1460                sb.append(apnContext.isEnabled());
1461                sb.append("] ");
1462            }
1463            log("setupDataOnConnectableApns: " + reason + " " + sb);
1464        }
1465
1466        for (ApnContext apnContext : mPrioritySortedApnContexts) {
1467            ArrayList<ApnSetting> waitingApns = null;
1468
1469            if (VDBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
1470
1471            if (apnContext.getState() == DctConstants.State.FAILED
1472                    || apnContext.getState() == DctConstants.State.SCANNING) {
1473                if (retryFailures == RetryFailures.ALWAYS) {
1474                    apnContext.releaseDataConnection(reason);
1475                } else if (apnContext.isConcurrentVoiceAndDataAllowed() == false &&
1476                        mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1477                    // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
1478                    apnContext.releaseDataConnection(reason);
1479                } else {
1480                    // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed
1481                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
1482                    ArrayList<ApnSetting> originalApns = apnContext.getWaitingApns();
1483                    if (originalApns != null && originalApns.isEmpty() == false) {
1484                        waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
1485                        if (originalApns.size() != waitingApns.size() ||
1486                                originalApns.containsAll(waitingApns) == false) {
1487                            apnContext.releaseDataConnection(reason);
1488                        } else {
1489                            continue;
1490                        }
1491                    } else {
1492                        continue;
1493                    }
1494                }
1495            }
1496            if (apnContext.isConnectable()) {
1497                log("isConnectable() call trySetupData");
1498                apnContext.setReason(reason);
1499                trySetupData(apnContext, waitingApns);
1500            }
1501        }
1502    }
1503
1504    boolean isEmergency() {
1505        final boolean result = mPhone.isInEcm() || mPhone.isInEmergencyCall();
1506        log("isEmergency: result=" + result);
1507        return result;
1508    }
1509
1510    private boolean trySetupData(ApnContext apnContext) {
1511        return trySetupData(apnContext, null);
1512    }
1513
1514    private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns) {
1515        if (DBG) {
1516            log("trySetupData for type:" + apnContext.getApnType() +
1517                    " due to " + apnContext.getReason() + ", mIsPsRestricted=" + mIsPsRestricted);
1518        }
1519        apnContext.requestLog("trySetupData due to " + apnContext.getReason());
1520
1521        if (mPhone.getSimulatedRadioControl() != null) {
1522            // Assume data is connected on the simulator
1523            // FIXME  this can be improved
1524            apnContext.setState(DctConstants.State.CONNECTED);
1525            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1526
1527            log("trySetupData: X We're on the simulator; assuming connected retValue=true");
1528            return true;
1529        }
1530
1531        // Allow SETUP_DATA request for E-APN to be completed during emergency call
1532        // and MOBILE DATA On/Off cases as well.
1533        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
1534        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
1535
1536        // set to false if apn type is non-metered or if we have a restricted (priveleged)
1537        // request for the network.
1538        // TODO - may want restricted requests to only apply to carrier-limited data access
1539        //        rather than applying to user limited as well.
1540        // Exclude DUN for the purposes of the override until we get finer grained
1541        // intention in NetworkRequests
1542        boolean checkUserDataEnabled =
1543                ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
1544                        mPhone.getSubId(), mPhone.getServiceState().getDataRoaming()) &&
1545                apnContext.hasNoRestrictedRequests(true /*exclude DUN */);
1546
1547        DataAllowFailReason failureReason = new DataAllowFailReason();
1548
1549        // allow data if currently in roaming service, roaming setting disabled
1550        // and requested apn type is non-metered for roaming.
1551        boolean isDataAllowed = isDataAllowed(failureReason) ||
1552                (failureReason.isFailForSingleReason(DataAllowFailReasonType.ROAMING_DISABLED) &&
1553                !(ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
1554                mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())));
1555
1556        if (apnContext.isConnectable() && (isEmergencyApn ||
1557                (isDataAllowed && isDataAllowedForApn(apnContext) &&
1558                        mDataEnabledSettings.isDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
1559            if (apnContext.getState() == DctConstants.State.FAILED) {
1560                String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
1561                if (DBG) log(str);
1562                apnContext.requestLog(str);
1563                apnContext.setState(DctConstants.State.IDLE);
1564            }
1565            int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
1566            apnContext.setConcurrentVoiceAndDataAllowed(sst.isConcurrentVoiceAndDataAllowed());
1567            if (apnContext.getState() == DctConstants.State.IDLE) {
1568                if (waitingApns == null) {
1569                    waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
1570                }
1571                if (waitingApns.isEmpty()) {
1572                    notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
1573                    notifyOffApnsOfAvailability(apnContext.getReason());
1574                    String str = "trySetupData: X No APN found retValue=false";
1575                    if (DBG) log(str);
1576                    apnContext.requestLog(str);
1577                    return false;
1578                } else {
1579                    apnContext.setWaitingApns(waitingApns);
1580                    if (DBG) {
1581                        log ("trySetupData: Create from mAllApnSettings : "
1582                                    + apnListToString(mAllApnSettings));
1583                    }
1584                }
1585            }
1586
1587            boolean retValue = setupData(apnContext, radioTech);
1588            notifyOffApnsOfAvailability(apnContext.getReason());
1589
1590            if (DBG) log("trySetupData: X retValue=" + retValue);
1591            return retValue;
1592        } else {
1593            if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
1594                    && apnContext.isConnectable()) {
1595                mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1596            }
1597            notifyOffApnsOfAvailability(apnContext.getReason());
1598
1599            StringBuilder str = new StringBuilder();
1600
1601            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType() +
1602                    ", mState=" + apnContext.getState() + ", mDataEnabled=" +
1603                    apnContext.isEnabled() + ", mDependencyMet=" +
1604                    apnContext.getDependencyMet() + "] ");
1605
1606            if (!apnContext.isConnectable()) {
1607                str.append("isConnectable = false. ");
1608            }
1609            if (!isDataAllowed) {
1610                str.append("data not allowed: " + failureReason.getDataAllowFailReason() + ". ");
1611            }
1612            if (!isDataAllowedForApn(apnContext)) {
1613                str.append("isDataAllowedForApn = false. RAT = " +
1614                        mPhone.getServiceState().getRilDataRadioTechnology());
1615            }
1616            if (!mDataEnabledSettings.isDataEnabled(checkUserDataEnabled)) {
1617                str.append("isDataEnabled(" + checkUserDataEnabled + ") = false. " +
1618                        "isInternalDataEnabled = " + mDataEnabledSettings.isInternalDataEnabled() +
1619                        ", userDataEnabled = " + mDataEnabledSettings.isUserDataEnabled() +
1620                        ", isPolicyDataEnabled = " + mDataEnabledSettings.isPolicyDataEnabled() +
1621                        ", isCarrierDataEnabled = " +
1622                        mDataEnabledSettings.isCarrierDataEnabled());
1623            }
1624            if (isEmergency()) {
1625                str.append("emergency = true");
1626            }
1627
1628            if (DBG) log(str.toString());
1629            apnContext.requestLog(str.toString());
1630
1631            return false;
1632        }
1633    }
1634
1635    // Disabled apn's still need avail/unavail notifications - send them out
1636    private void notifyOffApnsOfAvailability(String reason) {
1637        if (DBG) {
1638            DataAllowFailReason failureReason = new DataAllowFailReason();
1639            if (!isDataAllowed(failureReason)) {
1640                log(failureReason.getDataAllowFailReason());
1641            }
1642        }
1643        for (ApnContext apnContext : mApnContexts.values()) {
1644            if (!mAttached.get() || !apnContext.isReady()) {
1645                if (VDBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
1646                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
1647                                            apnContext.getApnType(),
1648                                            PhoneConstants.DataState.DISCONNECTED);
1649            } else {
1650                if (VDBG) {
1651                    log("notifyOffApnsOfAvailability skipped apn due to attached && isReady " +
1652                            apnContext.toString());
1653                }
1654            }
1655        }
1656    }
1657
1658    /**
1659     * If tearDown is true, this only tears down a CONNECTED session. Presently,
1660     * there is no mechanism for abandoning an CONNECTING session,
1661     * but would likely involve cancelling pending async requests or
1662     * setting a flag or new state to ignore them when they came in
1663     * @param tearDown true if the underlying DataConnection should be
1664     * disconnected.
1665     * @param reason reason for the clean up.
1666     * @return boolean - true if we did cleanup any connections, false if they
1667     *                   were already all disconnected.
1668     */
1669    private boolean cleanUpAllConnections(boolean tearDown, String reason) {
1670        if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
1671        boolean didDisconnect = false;
1672        boolean disableMeteredOnly = false;
1673
1674        // reasons that only metered apn will be torn down
1675        if (!TextUtils.isEmpty(reason)) {
1676            disableMeteredOnly = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED) ||
1677                    reason.equals(Phone.REASON_ROAMING_ON) ||
1678                    reason.equals(Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
1679        }
1680
1681        for (ApnContext apnContext : mApnContexts.values()) {
1682            if (apnContext.isDisconnected() == false) didDisconnect = true;
1683            if (disableMeteredOnly) {
1684                // Use ApnSetting to decide metered or non-metered.
1685                // Tear down all metered data connections.
1686                ApnSetting apnSetting = apnContext.getApnSetting();
1687                if (apnSetting != null && apnSetting.isMetered(mPhone.getContext(),
1688                        mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())) {
1689                    if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
1690                    apnContext.setReason(reason);
1691                    cleanUpConnection(tearDown, apnContext);
1692                }
1693            } else {
1694                // TODO - only do cleanup if not disconnected
1695                apnContext.setReason(reason);
1696                cleanUpConnection(tearDown, apnContext);
1697            }
1698        }
1699
1700        stopNetStatPoll();
1701        stopDataStallAlarm();
1702
1703        // TODO: Do we need mRequestedApnType?
1704        mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
1705
1706        log("cleanUpConnection: mDisconnectPendingCount = " + mDisconnectPendingCount);
1707        if (tearDown && mDisconnectPendingCount == 0) {
1708            notifyDataDisconnectComplete();
1709            notifyAllDataDisconnected();
1710        }
1711
1712        return didDisconnect;
1713    }
1714
1715    /**
1716     * Cleanup all connections.
1717     *
1718     * TODO: Cleanup only a specified connection passed as a parameter.
1719     *       Also, make sure when you clean up a conn, if it is last apply
1720     *       logic as though it is cleanupAllConnections
1721     *
1722     * @param cause for the clean up.
1723     */
1724    private void onCleanUpAllConnections(String cause) {
1725        cleanUpAllConnections(true, cause);
1726    }
1727
1728    void sendCleanUpConnection(boolean tearDown, ApnContext apnContext) {
1729        if (DBG) log("sendCleanUpConnection: tearDown=" + tearDown + " apnContext=" + apnContext);
1730        Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_CONNECTION);
1731        msg.arg1 = tearDown ? 1 : 0;
1732        msg.arg2 = 0;
1733        msg.obj = apnContext;
1734        sendMessage(msg);
1735    }
1736
1737    private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
1738        if (apnContext == null) {
1739            if (DBG) log("cleanUpConnection: apn context is null");
1740            return;
1741        }
1742
1743        DcAsyncChannel dcac = apnContext.getDcAc();
1744        String str = "cleanUpConnection: tearDown=" + tearDown + " reason=" +
1745                apnContext.getReason();
1746        if (VDBG) log(str + " apnContext=" + apnContext);
1747        apnContext.requestLog(str);
1748        if (tearDown) {
1749            if (apnContext.isDisconnected()) {
1750                // The request is tearDown and but ApnContext is not connected.
1751                // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
1752                apnContext.setState(DctConstants.State.IDLE);
1753                if (!apnContext.isReady()) {
1754                    if (dcac != null) {
1755                        str = "cleanUpConnection: teardown, disconnected, !ready";
1756                        if (DBG) log(str + " apnContext=" + apnContext);
1757                        apnContext.requestLog(str);
1758                        dcac.tearDown(apnContext, "", null);
1759                    }
1760                    apnContext.setDataConnectionAc(null);
1761                }
1762            } else {
1763                // Connection is still there. Try to clean up.
1764                if (dcac != null) {
1765                    if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
1766                        boolean disconnectAll = false;
1767                        if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
1768                            // CAF_MSIM is this below condition required.
1769                            // if (PhoneConstants.APN_TYPE_DUN.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
1770                            if (teardownForDun()) {
1771                                if (DBG) {
1772                                    log("cleanUpConnection: disconnectAll DUN connection");
1773                                }
1774                                // we need to tear it down - we brought it up just for dun and
1775                                // other people are camped on it and now dun is done.  We need
1776                                // to stop using it and let the normal apn list get used to find
1777                                // connections for the remaining desired connections
1778                                disconnectAll = true;
1779                            }
1780                        }
1781                        final int generation = apnContext.getConnectionGeneration();
1782                        str = "cleanUpConnection: tearing down" + (disconnectAll ? " all" : "") +
1783                                " using gen#" + generation;
1784                        if (DBG) log(str + "apnContext=" + apnContext);
1785                        apnContext.requestLog(str);
1786                        Pair<ApnContext, Integer> pair =
1787                                new Pair<ApnContext, Integer>(apnContext, generation);
1788                        Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair);
1789                        if (disconnectAll) {
1790                            apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
1791                        } else {
1792                            apnContext.getDcAc()
1793                                .tearDown(apnContext, apnContext.getReason(), msg);
1794                        }
1795                        apnContext.setState(DctConstants.State.DISCONNECTING);
1796                        mDisconnectPendingCount++;
1797                    }
1798                } else {
1799                    // apn is connected but no reference to dcac.
1800                    // Should not be happen, but reset the state in case.
1801                    apnContext.setState(DctConstants.State.IDLE);
1802                    apnContext.requestLog("cleanUpConnection: connected, bug no DCAC");
1803                    mPhone.notifyDataConnection(apnContext.getReason(),
1804                                                apnContext.getApnType());
1805                }
1806            }
1807        } else {
1808            // force clean up the data connection.
1809            if (dcac != null) dcac.reqReset();
1810            apnContext.setState(DctConstants.State.IDLE);
1811            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1812            apnContext.setDataConnectionAc(null);
1813        }
1814
1815        // Make sure reconnection alarm is cleaned up if there is no ApnContext
1816        // associated to the connection.
1817        if (dcac != null) {
1818            cancelReconnectAlarm(apnContext);
1819        }
1820        str = "cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason();
1821        if (DBG) log(str + " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
1822        apnContext.requestLog(str);
1823    }
1824
1825    ApnSetting fetchDunApn() {
1826        if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
1827            log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
1828            return null;
1829        }
1830        int bearer = mPhone.getServiceState().getRilDataRadioTechnology();
1831        ApnSetting retDunSetting = null;
1832        String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
1833        List<ApnSetting> dunSettings = ApnSetting.arrayFromString(apnData);
1834        IccRecords r = mIccRecords.get();
1835        for (ApnSetting dunSetting : dunSettings) {
1836            String operator = (r != null) ? r.getOperatorNumeric() : "";
1837            if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
1838            if (dunSetting.numeric.equals(operator)) {
1839                if (dunSetting.hasMvnoParams()) {
1840                    if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
1841                            dunSetting.mvnoMatchData)) {
1842                        if (VDBG) {
1843                            log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
1844                        }
1845                        return dunSetting;
1846                    }
1847                } else if (mMvnoMatched == false) {
1848                    if (VDBG) log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
1849                    return dunSetting;
1850                }
1851            }
1852        }
1853
1854        Context c = mPhone.getContext();
1855        String[] apnArrayData = c.getResources().getStringArray(R.array.config_tether_apndata);
1856        for (String apn : apnArrayData) {
1857            ApnSetting dunSetting = ApnSetting.fromString(apn);
1858            if (dunSetting != null) {
1859                if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
1860                if (dunSetting.hasMvnoParams()) {
1861                    if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
1862                            dunSetting.mvnoMatchData)) {
1863                        if (VDBG) {
1864                            log("fetchDunApn: config_tether_apndata mvno dunSetting=" + dunSetting);
1865                        }
1866                        return dunSetting;
1867                    }
1868                } else if (mMvnoMatched == false) {
1869                    retDunSetting = dunSetting;
1870                }
1871            }
1872        }
1873
1874        if (VDBG) log("fetchDunApn: config_tether_apndata dunSetting=" + retDunSetting);
1875        return retDunSetting;
1876    }
1877
1878    public boolean hasMatchedTetherApnSetting() {
1879        ApnSetting matched = fetchDunApn();
1880        log("hasMatchedTetherApnSetting: APN=" + matched);
1881        return matched != null;
1882    }
1883
1884    /**
1885     * Determine if DUN connection is special and we need to teardown on start/stop
1886     */
1887    private boolean teardownForDun() {
1888        // CDMA always needs to do this the profile id is correct
1889        final int rilRat = mPhone.getServiceState().getRilDataRadioTechnology();
1890        if (ServiceState.isCdma(rilRat)) return true;
1891
1892        return (fetchDunApn() != null);
1893    }
1894
1895    /**
1896     * Cancels the alarm associated with apnContext.
1897     *
1898     * @param apnContext on which the alarm should be stopped.
1899     */
1900    private void cancelReconnectAlarm(ApnContext apnContext) {
1901        if (apnContext == null) return;
1902
1903        PendingIntent intent = apnContext.getReconnectIntent();
1904
1905        if (intent != null) {
1906                AlarmManager am =
1907                    (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1908                am.cancel(intent);
1909                apnContext.setReconnectIntent(null);
1910        }
1911    }
1912
1913    /**
1914     * @param types comma delimited list of APN types
1915     * @return array of APN types
1916     */
1917    private String[] parseTypes(String types) {
1918        String[] result;
1919        // If unset, set to DEFAULT.
1920        if (types == null || types.equals("")) {
1921            result = new String[1];
1922            result[0] = PhoneConstants.APN_TYPE_ALL;
1923        } else {
1924            result = types.split(",");
1925        }
1926        return result;
1927    }
1928
1929    boolean isPermanentFail(DcFailCause dcFailCause) {
1930        return (dcFailCause.isPermanentFail() &&
1931                (mAttached.get() == false || dcFailCause != DcFailCause.SIGNAL_LOST));
1932    }
1933
1934    private ApnSetting makeApnSetting(Cursor cursor) {
1935        String[] types = parseTypes(
1936                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
1937        ApnSetting apn = new ApnSetting(
1938                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
1939                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
1940                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
1941                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
1942                NetworkUtils.trimV4AddrZeros(
1943                        cursor.getString(
1944                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
1945                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
1946                NetworkUtils.trimV4AddrZeros(
1947                        cursor.getString(
1948                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
1949                NetworkUtils.trimV4AddrZeros(
1950                        cursor.getString(
1951                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
1952                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
1953                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
1954                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
1955                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
1956                types,
1957                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
1958                cursor.getString(cursor.getColumnIndexOrThrow(
1959                        Telephony.Carriers.ROAMING_PROTOCOL)),
1960                cursor.getInt(cursor.getColumnIndexOrThrow(
1961                        Telephony.Carriers.CARRIER_ENABLED)) == 1,
1962                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
1963                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER_BITMASK)),
1964                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
1965                cursor.getInt(cursor.getColumnIndexOrThrow(
1966                        Telephony.Carriers.MODEM_COGNITIVE)) == 1,
1967                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
1968                cursor.getInt(cursor.getColumnIndexOrThrow(
1969                        Telephony.Carriers.WAIT_TIME)),
1970                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
1971                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
1972                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
1973                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)));
1974        return apn;
1975    }
1976
1977    private ArrayList<ApnSetting> createApnList(Cursor cursor) {
1978        ArrayList<ApnSetting> mnoApns = new ArrayList<ApnSetting>();
1979        ArrayList<ApnSetting> mvnoApns = new ArrayList<ApnSetting>();
1980        IccRecords r = mIccRecords.get();
1981
1982        if (cursor.moveToFirst()) {
1983            do {
1984                ApnSetting apn = makeApnSetting(cursor);
1985                if (apn == null) {
1986                    continue;
1987                }
1988
1989                if (apn.hasMvnoParams()) {
1990                    if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
1991                        mvnoApns.add(apn);
1992                    }
1993                } else {
1994                    mnoApns.add(apn);
1995                }
1996            } while (cursor.moveToNext());
1997        }
1998
1999        ArrayList<ApnSetting> result;
2000        if (mvnoApns.isEmpty()) {
2001            result = mnoApns;
2002            mMvnoMatched = false;
2003        } else {
2004            result = mvnoApns;
2005            mMvnoMatched = true;
2006        }
2007        if (DBG) log("createApnList: X result=" + result);
2008        return result;
2009    }
2010
2011    private boolean dataConnectionNotInUse(DcAsyncChannel dcac) {
2012        if (DBG) log("dataConnectionNotInUse: check if dcac is inuse dcac=" + dcac);
2013        for (ApnContext apnContext : mApnContexts.values()) {
2014            if (apnContext.getDcAc() == dcac) {
2015                if (DBG) log("dataConnectionNotInUse: in use by apnContext=" + apnContext);
2016                return false;
2017            }
2018        }
2019        // TODO: Fix retry handling so free DataConnections have empty apnlists.
2020        // Probably move retry handling into DataConnections and reduce complexity
2021        // of DCT.
2022        if (DBG) log("dataConnectionNotInUse: tearDownAll");
2023        dcac.tearDownAll("No connection", null);
2024        if (DBG) log("dataConnectionNotInUse: not in use return true");
2025        return true;
2026    }
2027
2028    private DcAsyncChannel findFreeDataConnection() {
2029        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
2030            if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
2031                if (DBG) {
2032                    log("findFreeDataConnection: found free DataConnection=" +
2033                        " dcac=" + dcac);
2034                }
2035                return dcac;
2036            }
2037        }
2038        log("findFreeDataConnection: NO free DataConnection");
2039        return null;
2040    }
2041
2042    private boolean setupData(ApnContext apnContext, int radioTech) {
2043        if (DBG) log("setupData: apnContext=" + apnContext);
2044        apnContext.requestLog("setupData");
2045        ApnSetting apnSetting;
2046        DcAsyncChannel dcac = null;
2047
2048        apnSetting = apnContext.getNextApnSetting();
2049
2050        if (apnSetting == null) {
2051            if (DBG) log("setupData: return for no apn found!");
2052            return false;
2053        }
2054
2055        int profileId = apnSetting.profileId;
2056        if (profileId == 0) {
2057            profileId = getApnProfileID(apnContext.getApnType());
2058        }
2059
2060        // On CDMA, if we're explicitly asking for DUN, we need have
2061        // a dun-profiled connection so we can't share an existing one
2062        // On GSM/LTE we can share existing apn connections provided they support
2063        // this type.
2064        if (apnContext.getApnType() != PhoneConstants.APN_TYPE_DUN ||
2065                teardownForDun() == false) {
2066            dcac = checkForCompatibleConnectedApnContext(apnContext);
2067            if (dcac != null) {
2068                // Get the dcacApnSetting for the connection we want to share.
2069                ApnSetting dcacApnSetting = dcac.getApnSettingSync();
2070                if (dcacApnSetting != null) {
2071                    // Setting is good, so use it.
2072                    apnSetting = dcacApnSetting;
2073                }
2074            }
2075        }
2076        if (dcac == null) {
2077            if (isOnlySingleDcAllowed(radioTech)) {
2078                if (isHigherPriorityApnContextActive(apnContext)) {
2079                    if (DBG) {
2080                        log("setupData: Higher priority ApnContext active.  Ignoring call");
2081                    }
2082                    return false;
2083                }
2084
2085                // Only lower priority calls left.  Disconnect them all in this single PDP case
2086                // so that we can bring up the requested higher priority call (once we receive
2087                // response for deactivate request for the calls we are about to disconnect
2088                if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
2089                    // If any call actually requested to be disconnected, means we can't
2090                    // bring up this connection yet as we need to wait for those data calls
2091                    // to be disconnected.
2092                    if (DBG) log("setupData: Some calls are disconnecting first.  Wait and retry");
2093                    return false;
2094                }
2095
2096                // No other calls are active, so proceed
2097                if (DBG) log("setupData: Single pdp. Continue setting up data call.");
2098            }
2099
2100            dcac = findFreeDataConnection();
2101
2102            if (dcac == null) {
2103                dcac = createDataConnection();
2104            }
2105
2106            if (dcac == null) {
2107                if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
2108                return false;
2109            }
2110        }
2111        final int generation = apnContext.incAndGetConnectionGeneration();
2112        if (DBG) {
2113            log("setupData: dcac=" + dcac + " apnSetting=" + apnSetting + " gen#=" + generation);
2114        }
2115
2116        apnContext.setDataConnectionAc(dcac);
2117        apnContext.setApnSetting(apnSetting);
2118        apnContext.setState(DctConstants.State.CONNECTING);
2119        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
2120
2121        Message msg = obtainMessage();
2122        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
2123        msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
2124        dcac.bringUp(apnContext, profileId, radioTech, msg, generation);
2125
2126        if (DBG) log("setupData: initing!");
2127        return true;
2128    }
2129
2130    private void setInitialAttachApn() {
2131        ApnSetting iaApnSetting = null;
2132        ApnSetting defaultApnSetting = null;
2133        ApnSetting firstApnSetting = null;
2134
2135        log("setInitialApn: E mPreferredApn=" + mPreferredApn);
2136
2137        if (mPreferredApn != null && mPreferredApn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
2138              iaApnSetting = mPreferredApn;
2139        } else if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
2140            firstApnSetting = mAllApnSettings.get(0);
2141            log("setInitialApn: firstApnSetting=" + firstApnSetting);
2142
2143            // Search for Initial APN setting and the first apn that can handle default
2144            for (ApnSetting apn : mAllApnSettings) {
2145                if (apn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
2146                    // The Initial Attach APN is highest priority so use it if there is one
2147                    log("setInitialApn: iaApnSetting=" + apn);
2148                    iaApnSetting = apn;
2149                    break;
2150                } else if ((defaultApnSetting == null)
2151                        && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {
2152                    // Use the first default apn if no better choice
2153                    log("setInitialApn: defaultApnSetting=" + apn);
2154                    defaultApnSetting = apn;
2155                }
2156            }
2157        }
2158
2159        // The priority of apn candidates from highest to lowest is:
2160        //   1) APN_TYPE_IA (Initial Attach)
2161        //   2) mPreferredApn, i.e. the current preferred apn
2162        //   3) The first apn that than handle APN_TYPE_DEFAULT
2163        //   4) The first APN we can find.
2164
2165        ApnSetting initialAttachApnSetting = null;
2166        if (iaApnSetting != null) {
2167            if (DBG) log("setInitialAttachApn: using iaApnSetting");
2168            initialAttachApnSetting = iaApnSetting;
2169        } else if (mPreferredApn != null) {
2170            if (DBG) log("setInitialAttachApn: using mPreferredApn");
2171            initialAttachApnSetting = mPreferredApn;
2172        } else if (defaultApnSetting != null) {
2173            if (DBG) log("setInitialAttachApn: using defaultApnSetting");
2174            initialAttachApnSetting = defaultApnSetting;
2175        } else if (firstApnSetting != null) {
2176            if (DBG) log("setInitialAttachApn: using firstApnSetting");
2177            initialAttachApnSetting = firstApnSetting;
2178        }
2179
2180        if (initialAttachApnSetting == null) {
2181            if (DBG) log("setInitialAttachApn: X There in no available apn");
2182        } else {
2183            if (DBG) log("setInitialAttachApn: X selected Apn=" + initialAttachApnSetting);
2184
2185            mPhone.mCi.setInitialAttachApn(initialAttachApnSetting.apn,
2186                    initialAttachApnSetting.protocol, initialAttachApnSetting.authType,
2187                    initialAttachApnSetting.user, initialAttachApnSetting.password, null);
2188        }
2189    }
2190
2191    /**
2192     * Handles changes to the APN database.
2193     */
2194    private void onApnChanged() {
2195        DctConstants.State overallState = getOverallState();
2196        boolean isDisconnected = (overallState == DctConstants.State.IDLE ||
2197                overallState == DctConstants.State.FAILED);
2198
2199        if (mPhone instanceof GsmCdmaPhone) {
2200            // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
2201            ((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider();
2202        }
2203
2204        // TODO: It'd be nice to only do this if the changed entrie(s)
2205        // match the current operator.
2206        if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
2207        createAllApnList();
2208        setInitialAttachApn();
2209        cleanUpConnectionsOnUpdatedApns(!isDisconnected);
2210
2211        // FIXME: See bug 17426028 maybe no conditional is needed.
2212        if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
2213            setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
2214        }
2215    }
2216
2217    /**
2218     * @param cid Connection id provided from RIL.
2219     * @return DataConnectionAc associated with specified cid.
2220     */
2221    private DcAsyncChannel findDataConnectionAcByCid(int cid) {
2222        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
2223            if (dcac.getCidSync() == cid) {
2224                return dcac;
2225            }
2226        }
2227        return null;
2228    }
2229
2230    // TODO: For multiple Active APNs not exactly sure how to do this.
2231    private void gotoIdleAndNotifyDataConnection(String reason) {
2232        if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
2233        notifyDataConnection(reason);
2234    }
2235
2236    /**
2237     * "Active" here means ApnContext isEnabled() and not in FAILED state
2238     * @param apnContext to compare with
2239     * @return true if higher priority active apn found
2240     */
2241    private boolean isHigherPriorityApnContextActive(ApnContext apnContext) {
2242        for (ApnContext otherContext : mPrioritySortedApnContexts) {
2243            if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
2244            if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) {
2245                return true;
2246            }
2247        }
2248        return false;
2249    }
2250
2251    /**
2252     * Reports if we support multiple connections or not.
2253     * This is a combination of factors, based on carrier and RAT.
2254     * @param rilRadioTech the RIL Radio Tech currently in use
2255     * @return true if only single DataConnection is allowed
2256     */
2257    private boolean isOnlySingleDcAllowed(int rilRadioTech) {
2258        int[] singleDcRats = mPhone.getContext().getResources().getIntArray(
2259                com.android.internal.R.array.config_onlySingleDcAllowed);
2260        boolean onlySingleDcAllowed = false;
2261        if (Build.IS_DEBUGGABLE &&
2262                SystemProperties.getBoolean("persist.telephony.test.singleDc", false)) {
2263            onlySingleDcAllowed = true;
2264        }
2265        if (singleDcRats != null) {
2266            for (int i=0; i < singleDcRats.length && onlySingleDcAllowed == false; i++) {
2267                if (rilRadioTech == singleDcRats[i]) onlySingleDcAllowed = true;
2268            }
2269        }
2270
2271        if (DBG) log("isOnlySingleDcAllowed(" + rilRadioTech + "): " + onlySingleDcAllowed);
2272        return onlySingleDcAllowed;
2273    }
2274
2275    void sendRestartRadio() {
2276        if (DBG)log("sendRestartRadio:");
2277        Message msg = obtainMessage(DctConstants.EVENT_RESTART_RADIO);
2278        sendMessage(msg);
2279    }
2280
2281    private void restartRadio() {
2282        if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
2283        cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
2284        mPhone.getServiceStateTracker().powerOffRadioSafely(this);
2285        /* Note: no need to call setRadioPower(true).  Assuming the desired
2286         * radio power state is still ON (as tracked by ServiceStateTracker),
2287         * ServiceStateTracker will call setRadioPower when it receives the
2288         * RADIO_STATE_CHANGED notification for the power off.  And if the
2289         * desired power state has changed in the interim, we don't want to
2290         * override it with an unconditional power on.
2291         */
2292
2293        int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
2294        SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset + 1));
2295    }
2296
2297    /**
2298     * Return true if data connection need to be setup after disconnected due to
2299     * reason.
2300     *
2301     * @param apnContext APN context
2302     * @return true if try setup data connection is need for this reason
2303     */
2304    private boolean retryAfterDisconnected(ApnContext apnContext) {
2305        boolean retry = true;
2306        String reason = apnContext.getReason();
2307
2308        if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ||
2309                (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())
2310                 && isHigherPriorityApnContextActive(apnContext))) {
2311            retry = false;
2312        }
2313        return retry;
2314    }
2315
2316    private void startAlarmForReconnect(long delay, ApnContext apnContext) {
2317        String apnType = apnContext.getApnType();
2318
2319        Intent intent = new Intent(INTENT_RECONNECT_ALARM + "." + apnType);
2320        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
2321        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnType);
2322        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
2323
2324        // Get current sub id.
2325        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
2326        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
2327
2328        if (DBG) {
2329            log("startAlarmForReconnect: delay=" + delay + " action=" + intent.getAction()
2330                    + " apn=" + apnContext);
2331        }
2332
2333        PendingIntent alarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0,
2334                                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
2335        apnContext.setReconnectIntent(alarmIntent);
2336
2337        // Use the exact timer instead of the inexact one to provide better user experience.
2338        // In some extreme cases, we saw the retry was delayed for few minutes.
2339        // Note that if the stated trigger time is in the past, the alarm will be triggered
2340        // immediately.
2341        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2342                SystemClock.elapsedRealtime() + delay, alarmIntent);
2343    }
2344
2345    private void notifyNoData(DcFailCause lastFailCauseCode,
2346                              ApnContext apnContext) {
2347        if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
2348        if (isPermanentFail(lastFailCauseCode)
2349            && (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT))) {
2350            mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
2351        }
2352    }
2353
2354    public boolean getAutoAttachOnCreation() {
2355        return mAutoAttachOnCreation.get();
2356    }
2357
2358    private void onRecordsLoadedOrSubIdChanged() {
2359        if (DBG) log("onRecordsLoadedOrSubIdChanged: createAllApnList");
2360        mAutoAttachOnCreationConfig = mPhone.getContext().getResources()
2361                .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
2362
2363        createAllApnList();
2364        setInitialAttachApn();
2365        if (mPhone.mCi.getRadioState().isOn()) {
2366            if (DBG) log("onRecordsLoadedOrSubIdChanged: notifying data availability");
2367            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
2368        }
2369        setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
2370    }
2371
2372    public void setApnsEnabledByCarrier(boolean enabled) {
2373        Message msg = obtainMessage(DctConstants.EVENT_SET_CARRIER_DATA_ENABLED);
2374        msg.arg1 = (enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
2375        sendMessage(msg);
2376    }
2377
2378    /**
2379     * Action set from carrier signalling broadcast receivers to enable/disable metered apns.
2380     */
2381    private void onSetCarrierDataEnabled(boolean enabled) {
2382        synchronized (mDataEnabledSettings) {
2383            if (enabled != mDataEnabledSettings.isCarrierDataEnabled()) {
2384                if (DBG) {
2385                    log("carrier Action: set metered apns enabled: " + enabled);
2386                }
2387
2388                // Disable/enable all metered apns
2389                mDataEnabledSettings.setCarrierDataEnabled(enabled);
2390
2391                if (!enabled) {
2392                    // Send otasp_sim_unprovisioned so that SuW is able to proceed and notify users
2393                    mPhone.notifyOtaspChanged(ServiceStateTracker.OTASP_SIM_UNPROVISIONED);
2394                    // Tear down all metered apns
2395                    cleanUpAllConnections(true, Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
2396                } else {
2397                    // Re-evauluate Otasp state
2398                    int otaspState = mPhone.getServiceStateTracker().getOtasp();
2399                    mPhone.notifyOtaspChanged(otaspState);
2400
2401                    teardownRestrictedMeteredConnections();
2402                    setupDataOnConnectableApns(Phone.REASON_DATA_ENABLED);
2403                }
2404            }
2405        }
2406    }
2407
2408    /**
2409     * Action set from carrier signalling broadcast receivers to enable/disable radio
2410     */
2411    public void carrierActionSetRadioEnabled(boolean enabled) {
2412        if (DBG) {
2413            log("carrier Action: set radio enabled: " + enabled);
2414        }
2415        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
2416        sst.setRadioPowerFromCarrier(enabled);
2417    }
2418
2419    private void onSimNotReady() {
2420        if (DBG) log("onSimNotReady");
2421
2422        cleanUpAllConnections(true, Phone.REASON_SIM_NOT_READY);
2423        mAllApnSettings = null;
2424        mAutoAttachOnCreationConfig = false;
2425    }
2426
2427    private void onSetDependencyMet(String apnType, boolean met) {
2428        // don't allow users to tweak hipri to work around default dependency not met
2429        if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
2430
2431        ApnContext apnContext = mApnContexts.get(apnType);
2432        if (apnContext == null) {
2433            loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
2434                    apnType + ", " + met + ")");
2435            return;
2436        }
2437        applyNewState(apnContext, apnContext.isEnabled(), met);
2438        if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
2439            // tie actions on default to similar actions on HIPRI regarding dependencyMet
2440            apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
2441            if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
2442        }
2443    }
2444
2445    public void setPolicyDataEnabled(boolean enabled) {
2446        if (DBG) log("setPolicyDataEnabled: " + enabled);
2447        Message msg = obtainMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE);
2448        msg.arg1 = (enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
2449        sendMessage(msg);
2450    }
2451
2452    private void onSetPolicyDataEnabled(boolean enabled) {
2453        synchronized (mDataEnabledSettings) {
2454            final boolean prevEnabled = getAnyDataEnabled();
2455            if (mDataEnabledSettings.isPolicyDataEnabled() != enabled) {
2456                mDataEnabledSettings.setPolicyDataEnabled(enabled);
2457                // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
2458                // handle the rest from there.
2459                if (prevEnabled != getAnyDataEnabled()) {
2460                    if (!prevEnabled) {
2461                        teardownRestrictedMeteredConnections();
2462                        onTrySetupData(Phone.REASON_DATA_ENABLED);
2463                    } else {
2464                        onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
2465                    }
2466                }
2467            }
2468        }
2469    }
2470
2471    private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
2472        boolean cleanup = false;
2473        boolean trySetup = false;
2474        String str ="applyNewState(" + apnContext.getApnType() + ", " + enabled +
2475                "(" + apnContext.isEnabled() + "), " + met + "(" +
2476                apnContext.getDependencyMet() +"))";
2477        if (DBG) log(str);
2478        apnContext.requestLog(str);
2479
2480        if (apnContext.isReady()) {
2481            cleanup = true;
2482            if (enabled && met) {
2483                DctConstants.State state = apnContext.getState();
2484                switch(state) {
2485                    case CONNECTING:
2486                    case SCANNING:
2487                    case CONNECTED:
2488                    case DISCONNECTING:
2489                        // We're "READY" and active so just return
2490                        if (DBG) log("applyNewState: 'ready' so return");
2491                        apnContext.requestLog("applyNewState state=" + state + ", so return");
2492                        return;
2493                    case IDLE:
2494                        // fall through: this is unexpected but if it happens cleanup and try setup
2495                    case FAILED:
2496                    case RETRYING: {
2497                        // We're "READY" but not active so disconnect (cleanup = true) and
2498                        // connect (trySetup = true) to be sure we retry the connection.
2499                        trySetup = true;
2500                        apnContext.setReason(Phone.REASON_DATA_ENABLED);
2501                        break;
2502                    }
2503                }
2504            } else if (met) {
2505                apnContext.setReason(Phone.REASON_DATA_DISABLED);
2506                // If ConnectivityService has disabled this network, stop trying to bring
2507                // it up, but do not tear it down - ConnectivityService will do that
2508                // directly by talking with the DataConnection.
2509                //
2510                // This doesn't apply to DUN, however.  Those connections have special
2511                // requirements from carriers and we need stop using them when the dun
2512                // request goes away.  This applies to both CDMA and GSM because they both
2513                // can declare the DUN APN sharable by default traffic, thus still satisfying
2514                // those requests and not torn down organically.
2515                if (apnContext.getApnType() == PhoneConstants.APN_TYPE_DUN && teardownForDun()) {
2516                    cleanup = true;
2517                } else {
2518                    cleanup = false;
2519                }
2520            } else {
2521                apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
2522            }
2523        } else {
2524            if (enabled && met) {
2525                if (apnContext.isEnabled()) {
2526                    apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
2527                } else {
2528                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
2529                }
2530                if (apnContext.getState() == DctConstants.State.FAILED) {
2531                    apnContext.setState(DctConstants.State.IDLE);
2532                }
2533                trySetup = true;
2534            }
2535        }
2536        apnContext.setEnabled(enabled);
2537        apnContext.setDependencyMet(met);
2538        if (cleanup) cleanUpConnection(true, apnContext);
2539        if (trySetup) {
2540            apnContext.resetErrorCodeRetries();
2541            trySetupData(apnContext);
2542        }
2543    }
2544
2545    private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
2546        String apnType = apnContext.getApnType();
2547        ApnSetting dunSetting = null;
2548
2549        if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
2550            dunSetting = fetchDunApn();
2551        }
2552        if (DBG) {
2553            log("checkForCompatibleConnectedApnContext: apnContext=" + apnContext );
2554        }
2555
2556        DcAsyncChannel potentialDcac = null;
2557        ApnContext potentialApnCtx = null;
2558        for (ApnContext curApnCtx : mApnContexts.values()) {
2559            DcAsyncChannel curDcac = curApnCtx.getDcAc();
2560            if (curDcac != null) {
2561                ApnSetting apnSetting = curApnCtx.getApnSetting();
2562                log("apnSetting: " + apnSetting);
2563                if (dunSetting != null) {
2564                    if (dunSetting.equals(apnSetting)) {
2565                        switch (curApnCtx.getState()) {
2566                            case CONNECTED:
2567                                if (DBG) {
2568                                    log("checkForCompatibleConnectedApnContext:"
2569                                            + " found dun conn=" + curDcac
2570                                            + " curApnCtx=" + curApnCtx);
2571                                }
2572                                return curDcac;
2573                            case RETRYING:
2574                            case CONNECTING:
2575                                potentialDcac = curDcac;
2576                                potentialApnCtx = curApnCtx;
2577                            default:
2578                                // Not connected, potential unchanged
2579                                break;
2580                        }
2581                    }
2582                } else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
2583                    switch (curApnCtx.getState()) {
2584                        case CONNECTED:
2585                            if (DBG) {
2586                                log("checkForCompatibleConnectedApnContext:"
2587                                        + " found canHandle conn=" + curDcac
2588                                        + " curApnCtx=" + curApnCtx);
2589                            }
2590                            return curDcac;
2591                        case RETRYING:
2592                        case CONNECTING:
2593                            potentialDcac = curDcac;
2594                            potentialApnCtx = curApnCtx;
2595                        default:
2596                            // Not connected, potential unchanged
2597                            break;
2598                    }
2599                }
2600            } else {
2601                if (VDBG) {
2602                    log("checkForCompatibleConnectedApnContext: not conn curApnCtx=" + curApnCtx);
2603                }
2604            }
2605        }
2606        if (potentialDcac != null) {
2607            if (DBG) {
2608                log("checkForCompatibleConnectedApnContext: found potential conn=" + potentialDcac
2609                        + " curApnCtx=" + potentialApnCtx);
2610            }
2611            return potentialDcac;
2612        }
2613
2614        if (DBG) log("checkForCompatibleConnectedApnContext: NO conn apnContext=" + apnContext);
2615        return null;
2616    }
2617
2618    public void setEnabled(int id, boolean enable) {
2619        Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN);
2620        msg.arg1 = id;
2621        msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
2622        sendMessage(msg);
2623    }
2624
2625    private void onEnableApn(int apnId, int enabled) {
2626        ApnContext apnContext = mApnContextsById.get(apnId);
2627        if (apnContext == null) {
2628            loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
2629            return;
2630        }
2631        // TODO change our retry manager to use the appropriate numbers for the new APN
2632        if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
2633        applyNewState(apnContext, enabled == DctConstants.ENABLED, apnContext.getDependencyMet());
2634
2635        if ((enabled == DctConstants.DISABLED) &&
2636            isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology()) &&
2637            !isHigherPriorityApnContextActive(apnContext)) {
2638
2639            if(DBG) log("onEnableApn: isOnlySingleDcAllowed true & higher priority APN disabled");
2640            // If the highest priority APN is disabled and only single
2641            // data call is allowed, try to setup data call on other connectable APN.
2642            setupDataOnConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION);
2643        }
2644    }
2645
2646    // TODO: We shouldnt need this.
2647    private boolean onTrySetupData(String reason) {
2648        if (DBG) log("onTrySetupData: reason=" + reason);
2649        setupDataOnConnectableApns(reason);
2650        return true;
2651    }
2652
2653    private boolean onTrySetupData(ApnContext apnContext) {
2654        if (DBG) log("onTrySetupData: apnContext=" + apnContext);
2655        return trySetupData(apnContext);
2656    }
2657
2658    /**
2659     * Return current {@link android.provider.Settings.Global#MOBILE_DATA} value.
2660     */
2661    //TODO: Merge this into DataSettings. And probably should rename to getUserDataEnabled().
2662    public boolean getDataEnabled() {
2663        final int device_provisioned =
2664                Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0);
2665
2666        boolean retVal = "true".equalsIgnoreCase(SystemProperties.get(
2667                "ro.com.android.mobiledata", "true"));
2668        if (TelephonyManager.getDefault().getSimCount() == 1) {
2669            retVal = Settings.Global.getInt(mResolver, Settings.Global.MOBILE_DATA,
2670                    retVal ? 1 : 0) != 0;
2671        } else {
2672            int phoneSubId = mPhone.getSubId();
2673            try {
2674                retVal = TelephonyManager.getIntWithSubId(mResolver,
2675                        Settings.Global.MOBILE_DATA, phoneSubId) != 0;
2676            } catch (SettingNotFoundException e) {
2677                // use existing retVal
2678            }
2679        }
2680        if (VDBG) log("getDataEnabled: retVal=" + retVal);
2681        if (device_provisioned == 0) {
2682            // device is still getting provisioned - use whatever setting they
2683            // want during this process
2684            //
2685            // use the normal data_enabled setting (retVal, determined above)
2686            // as the default if nothing else is set
2687            final String prov_property = SystemProperties.get("ro.com.android.prov_mobiledata",
2688                  retVal ? "true" : "false");
2689            retVal = "true".equalsIgnoreCase(prov_property);
2690
2691            final int prov_mobile_data = Settings.Global.getInt(mResolver,
2692                    Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
2693                    retVal ? 1 : 0);
2694            retVal = prov_mobile_data != 0;
2695            log("getDataEnabled during provisioning retVal=" + retVal + " - (" + prov_property +
2696                    ", " + prov_mobile_data + ")");
2697        }
2698
2699        return retVal;
2700    }
2701
2702    /**
2703     * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value.
2704     */
2705    public void setDataOnRoamingEnabled(boolean enabled) {
2706        final int phoneSubId = mPhone.getSubId();
2707        if (getDataOnRoamingEnabled() != enabled) {
2708            int roaming = enabled ? 1 : 0;
2709
2710            // For single SIM phones, this is a per phone property.
2711            if (TelephonyManager.getDefault().getSimCount() == 1) {
2712                Settings.Global.putInt(mResolver, Settings.Global.DATA_ROAMING, roaming);
2713            } else {
2714                Settings.Global.putInt(mResolver, Settings.Global.DATA_ROAMING +
2715                         phoneSubId, roaming);
2716            }
2717
2718            mSubscriptionManager.setDataRoaming(roaming, phoneSubId);
2719            // will trigger handleDataOnRoamingChange() through observer
2720            if (DBG) {
2721               log("setDataOnRoamingEnabled: set phoneSubId=" + phoneSubId
2722                       + " isRoaming=" + enabled);
2723            }
2724        } else {
2725            if (DBG) {
2726                log("setDataOnRoamingEnabled: unchanged phoneSubId=" + phoneSubId
2727                        + " isRoaming=" + enabled);
2728             }
2729        }
2730    }
2731
2732    /**
2733     * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value.
2734     */
2735    public boolean getDataOnRoamingEnabled() {
2736        boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get(
2737                "ro.com.android.dataroaming", "false"));
2738        final int phoneSubId = mPhone.getSubId();
2739
2740        try {
2741            // For single SIM phones, this is a per phone property.
2742            if (TelephonyManager.getDefault().getSimCount() == 1) {
2743                isDataRoamingEnabled = Settings.Global.getInt(mResolver,
2744                        Settings.Global.DATA_ROAMING, isDataRoamingEnabled ? 1 : 0) != 0;
2745            } else {
2746                isDataRoamingEnabled = TelephonyManager.getIntWithSubId(mResolver,
2747                        Settings.Global.DATA_ROAMING, phoneSubId) != 0;
2748            }
2749        } catch (SettingNotFoundException snfe) {
2750            if (DBG) log("getDataOnRoamingEnabled: SettingNofFoundException snfe=" + snfe);
2751        }
2752        if (VDBG) {
2753            log("getDataOnRoamingEnabled: phoneSubId=" + phoneSubId +
2754                    " isDataRoamingEnabled=" + isDataRoamingEnabled);
2755        }
2756        return isDataRoamingEnabled;
2757    }
2758
2759    private void onRoamingOff() {
2760        if (DBG) log("onRoamingOff");
2761
2762        if (!mDataEnabledSettings.isUserDataEnabled()) return;
2763
2764        // Note onRoamingOff will be called immediately when DcTracker registerForDataRoamingOff,
2765        // and will be called again if SST calls mDataRoamingOffRegistrants.notify().
2766        setDataProfilesAsNeeded();
2767
2768        if (getDataOnRoamingEnabled() == false) {
2769            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
2770            setupDataOnConnectableApns(Phone.REASON_ROAMING_OFF);
2771        } else {
2772            notifyDataConnection(Phone.REASON_ROAMING_OFF);
2773        }
2774    }
2775
2776    private void onRoamingOn() {
2777        if (DBG) log("onRoamingOn");
2778
2779        if (!mDataEnabledSettings.isUserDataEnabled()) {
2780            if (DBG) log("data not enabled by user");
2781            return;
2782        }
2783
2784        // Check if the device is actually data roaming
2785        if (!mPhone.getServiceState().getDataRoaming()) {
2786            if (DBG) log("device is not roaming. ignored the request.");
2787            return;
2788        }
2789
2790        if (getDataOnRoamingEnabled()) {
2791            if (DBG) log("onRoamingOn: setup data on roaming");
2792            setupDataOnConnectableApns(Phone.REASON_ROAMING_ON);
2793            notifyDataConnection(Phone.REASON_ROAMING_ON);
2794        } else {
2795            if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
2796            cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
2797            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
2798        }
2799    }
2800
2801    private void onRadioAvailable() {
2802        if (DBG) log("onRadioAvailable");
2803        if (mPhone.getSimulatedRadioControl() != null) {
2804            // Assume data is connected on the simulator
2805            // FIXME  this can be improved
2806            // setState(DctConstants.State.CONNECTED);
2807            notifyDataConnection(null);
2808
2809            log("onRadioAvailable: We're on the simulator; assuming data is connected");
2810        }
2811
2812        IccRecords r = mIccRecords.get();
2813        if (r != null && r.getRecordsLoaded()) {
2814            notifyOffApnsOfAvailability(null);
2815        }
2816
2817        if (getOverallState() != DctConstants.State.IDLE) {
2818            cleanUpConnection(true, null);
2819        }
2820    }
2821
2822    private void onRadioOffOrNotAvailable() {
2823        // Make sure our reconnect delay starts at the initial value
2824        // next time the radio comes on
2825
2826        mReregisterOnReconnectFailure = false;
2827
2828        if (mPhone.getSimulatedRadioControl() != null) {
2829            // Assume data is connected on the simulator
2830            // FIXME  this can be improved
2831            log("We're on the simulator; assuming radio off is meaningless");
2832        } else {
2833            if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
2834            cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
2835        }
2836        notifyOffApnsOfAvailability(null);
2837    }
2838
2839    private void completeConnection(ApnContext apnContext) {
2840
2841        if (DBG) log("completeConnection: successful, notify the world apnContext=" + apnContext);
2842
2843        if (mIsProvisioning && !TextUtils.isEmpty(mProvisioningUrl)) {
2844            if (DBG) {
2845                log("completeConnection: MOBILE_PROVISIONING_ACTION url="
2846                        + mProvisioningUrl);
2847            }
2848            Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
2849                    Intent.CATEGORY_APP_BROWSER);
2850            newIntent.setData(Uri.parse(mProvisioningUrl));
2851            newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
2852                    Intent.FLAG_ACTIVITY_NEW_TASK);
2853            try {
2854                mPhone.getContext().startActivity(newIntent);
2855            } catch (ActivityNotFoundException e) {
2856                loge("completeConnection: startActivityAsUser failed" + e);
2857            }
2858        }
2859        mIsProvisioning = false;
2860        mProvisioningUrl = null;
2861        if (mProvisioningSpinner != null) {
2862            sendMessage(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
2863                    mProvisioningSpinner));
2864        }
2865
2866        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
2867        startNetStatPoll();
2868        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
2869    }
2870
2871    /**
2872     * A SETUP (aka bringUp) has completed, possibly with an error. If
2873     * there is an error this method will call {@link #onDataSetupCompleteError}.
2874     */
2875    private void onDataSetupComplete(AsyncResult ar) {
2876
2877        DcFailCause cause = DcFailCause.UNKNOWN;
2878        boolean handleError = false;
2879        ApnContext apnContext = getValidApnContext(ar, "onDataSetupComplete");
2880
2881        if (apnContext == null) return;
2882
2883        if (ar.exception == null) {
2884            DcAsyncChannel dcac = apnContext.getDcAc();
2885
2886            if (RADIO_TESTS) {
2887                // Note: To change radio.test.onDSC.null.dcac from command line you need to
2888                // adb root and adb remount and from the command line you can only change the
2889                // value to 1 once. To change it a second time you can reboot or execute
2890                // adb shell stop and then adb shell start. The command line to set the value is:
2891                // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');"
2892                ContentResolver cr = mPhone.getContext().getContentResolver();
2893                String radioTestProperty = "radio.test.onDSC.null.dcac";
2894                if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) {
2895                    log("onDataSetupComplete: " + radioTestProperty +
2896                            " is true, set dcac to null and reset property to false");
2897                    dcac = null;
2898                    Settings.System.putInt(cr, radioTestProperty, 0);
2899                    log("onDataSetupComplete: " + radioTestProperty + "=" +
2900                            Settings.System.getInt(mPhone.getContext().getContentResolver(),
2901                                    radioTestProperty, -1));
2902                }
2903            }
2904            if (dcac == null) {
2905                log("onDataSetupComplete: no connection to DC, handle as error");
2906                cause = DcFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
2907                handleError = true;
2908            } else {
2909                ApnSetting apn = apnContext.getApnSetting();
2910                if (DBG) {
2911                    log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
2912                }
2913                if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
2914                    try {
2915                        String port = apn.port;
2916                        if (TextUtils.isEmpty(port)) port = "8080";
2917                        ProxyInfo proxy = new ProxyInfo(apn.proxy,
2918                                Integer.parseInt(port), null);
2919                        dcac.setLinkPropertiesHttpProxySync(proxy);
2920                    } catch (NumberFormatException e) {
2921                        loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
2922                                apn.port + "): " + e);
2923                    }
2924                }
2925
2926                // everything is setup
2927                if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
2928                    try {
2929                        SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");
2930                    } catch (RuntimeException ex) {
2931                        log("Failed to set PUPPET_MASTER_RADIO_STRESS_TEST to true");
2932                    }
2933                    if (mCanSetPreferApn && mPreferredApn == null) {
2934                        if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
2935                        mPreferredApn = apn;
2936                        if (mPreferredApn != null) {
2937                            setPreferredApn(mPreferredApn.id);
2938                        }
2939                    }
2940                } else {
2941                    try {
2942                        SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
2943                    } catch (RuntimeException ex) {
2944                        log("Failed to set PUPPET_MASTER_RADIO_STRESS_TEST to false");
2945                    }
2946                }
2947
2948                // A connection is setup
2949                apnContext.setState(DctConstants.State.CONNECTED);
2950
2951                boolean isProvApn = apnContext.isProvisioningApn();
2952                final ConnectivityManager cm = ConnectivityManager.from(mPhone.getContext());
2953                if (mProvisionBroadcastReceiver != null) {
2954                    mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
2955                    mProvisionBroadcastReceiver = null;
2956                }
2957                if ((!isProvApn) || mIsProvisioning) {
2958                    // Hide any provisioning notification.
2959                    cm.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_MOBILE,
2960                            mProvisionActionName);
2961                    // Complete the connection normally notifying the world we're connected.
2962                    // We do this if this isn't a special provisioning apn or if we've been
2963                    // told its time to provision.
2964                    completeConnection(apnContext);
2965                } else {
2966                    // This is a provisioning APN that we're reporting as connected. Later
2967                    // when the user desires to upgrade this to a "default" connection,
2968                    // mIsProvisioning == true, we'll go through the code path above.
2969                    // mIsProvisioning becomes true when CMD_ENABLE_MOBILE_PROVISIONING
2970                    // is sent to the DCT.
2971                    if (DBG) {
2972                        log("onDataSetupComplete: successful, BUT send connected to prov apn as"
2973                                + " mIsProvisioning:" + mIsProvisioning + " == false"
2974                                + " && (isProvisioningApn:" + isProvApn + " == true");
2975                    }
2976
2977                    // While radio is up, grab provisioning URL.  The URL contains ICCID which
2978                    // disappears when radio is off.
2979                    mProvisionBroadcastReceiver = new ProvisionNotificationBroadcastReceiver(
2980                            cm.getMobileProvisioningUrl(),
2981                            TelephonyManager.getDefault().getNetworkOperatorName());
2982                    mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver,
2983                            new IntentFilter(mProvisionActionName));
2984                    // Put up user notification that sign-in is required.
2985                    cm.setProvisioningNotificationVisible(true, ConnectivityManager.TYPE_MOBILE,
2986                            mProvisionActionName);
2987                    // Turn off radio to save battery and avoid wasting carrier resources.
2988                    // The network isn't usable and network validation will just fail anyhow.
2989                    setRadio(false);
2990                }
2991                if (DBG) {
2992                    log("onDataSetupComplete: SETUP complete type=" + apnContext.getApnType()
2993                        + ", reason:" + apnContext.getReason());
2994                }
2995                if (Build.IS_DEBUGGABLE) {
2996                    // adb shell setprop persist.radio.test.pco [pco_val]
2997                    String radioTestProperty = "persist.radio.test.pco";
2998                    int pcoVal = SystemProperties.getInt(radioTestProperty, -1);
2999                    if (pcoVal != -1) {
3000                        log("PCO testing: read pco value from persist.radio.test.pco " + pcoVal);
3001                        final byte[] value = new byte[1];
3002                        value[0] = (byte) pcoVal;
3003                        final Intent intent =
3004                                new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE);
3005                        intent.putExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY, "default");
3006                        intent.putExtra(TelephonyIntents.EXTRA_APN_PROTO_KEY, "IPV4V6");
3007                        intent.putExtra(TelephonyIntents.EXTRA_PCO_ID_KEY, 0xFF00);
3008                        intent.putExtra(TelephonyIntents.EXTRA_PCO_VALUE_KEY, value);
3009                        mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
3010                    }
3011                }
3012            }
3013        } else {
3014            cause = (DcFailCause) (ar.result);
3015            if (DBG) {
3016                ApnSetting apn = apnContext.getApnSetting();
3017                log(String.format("onDataSetupComplete: error apn=%s cause=%s",
3018                        (apn == null ? "unknown" : apn.apn), cause));
3019            }
3020            if (cause.isEventLoggable()) {
3021                // Log this failure to the Event Logs.
3022                int cid = getCellLocationId();
3023                EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
3024                        cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType());
3025            }
3026            ApnSetting apn = apnContext.getApnSetting();
3027            mPhone.notifyPreciseDataConnectionFailed(apnContext.getReason(),
3028                    apnContext.getApnType(), apn != null ? apn.apn : "unknown", cause.toString());
3029
3030            // Compose broadcast intent send to the specific carrier signaling receivers
3031            Intent intent = new Intent(TelephonyIntents
3032                    .ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
3033            intent.putExtra(TelephonyIntents.EXTRA_ERROR_CODE_KEY, cause.getErrorCode());
3034            intent.putExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY, apnContext.getApnType());
3035            mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
3036
3037            if (cause.isRestartRadioFail() || apnContext.restartOnError(cause.getErrorCode())) {
3038                if (DBG) log("Modem restarted.");
3039                sendRestartRadio();
3040            }
3041
3042            // If the data call failure cause is a permanent failure, we mark the APN as permanent
3043            // failed.
3044            if (isPermanentFail(cause)) {
3045                log("cause = " + cause + ", mark apn as permanent failed. apn = " + apn);
3046                apnContext.markApnPermanentFailed(apn);
3047            }
3048
3049            handleError = true;
3050        }
3051
3052        if (handleError) {
3053            onDataSetupCompleteError(ar);
3054        }
3055
3056        /* If flag is set to false after SETUP_DATA_CALL is invoked, we need
3057         * to clean data connections.
3058         */
3059        if (!mDataEnabledSettings.isInternalDataEnabled()) {
3060            cleanUpAllConnections(Phone.REASON_DATA_DISABLED);
3061        }
3062
3063    }
3064
3065    /**
3066     * check for obsolete messages.  Return ApnContext if valid, null if not
3067     */
3068    private ApnContext getValidApnContext(AsyncResult ar, String logString) {
3069        if (ar != null && ar.userObj instanceof Pair) {
3070            Pair<ApnContext, Integer>pair = (Pair<ApnContext, Integer>)ar.userObj;
3071            ApnContext apnContext = pair.first;
3072            if (apnContext != null) {
3073                final int generation = apnContext.getConnectionGeneration();
3074                if (DBG) {
3075                    log("getValidApnContext (" + logString + ") on " + apnContext + " got " +
3076                            generation + " vs " + pair.second);
3077                }
3078                if (generation == pair.second) {
3079                    return apnContext;
3080                } else {
3081                    log("ignoring obsolete " + logString);
3082                    return null;
3083                }
3084            }
3085        }
3086        throw new RuntimeException(logString + ": No apnContext");
3087    }
3088
3089    /**
3090     * Error has occurred during the SETUP {aka bringUP} request and the DCT
3091     * should either try the next waiting APN or start over from the
3092     * beginning if the list is empty. Between each SETUP request there will
3093     * be a delay defined by {@link #getApnDelay()}.
3094     */
3095    private void onDataSetupCompleteError(AsyncResult ar) {
3096
3097        ApnContext apnContext = getValidApnContext(ar, "onDataSetupCompleteError");
3098
3099        if (apnContext == null) return;
3100
3101        long delay = apnContext.getDelayForNextApn(mFailFast);
3102
3103        // Check if we need to retry or not.
3104        if (delay >= 0) {
3105            if (DBG) log("onDataSetupCompleteError: Try next APN. delay = " + delay);
3106            apnContext.setState(DctConstants.State.SCANNING);
3107            // Wait a bit before trying the next APN, so that
3108            // we're not tying up the RIL command channel
3109            startAlarmForReconnect(delay, apnContext);
3110        } else {
3111            // If we are not going to retry any APN, set this APN context to failed state.
3112            // This would be the final state of a data connection.
3113            apnContext.setState(DctConstants.State.FAILED);
3114            mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
3115            apnContext.setDataConnectionAc(null);
3116            log("onDataSetupCompleteError: Stop retrying APNs.");
3117        }
3118    }
3119
3120    /**
3121     * Called when EVENT_REDIRECTION_DETECTED is received.
3122     */
3123    private void onDataConnectionRedirected(String redirectUrl) {
3124        if (!TextUtils.isEmpty(redirectUrl)) {
3125            Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
3126            intent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY, redirectUrl);
3127            if(mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent)) {
3128                log("Notify carrier signal receivers with redirectUrl: " + redirectUrl);
3129            }
3130        }
3131    }
3132
3133    /**
3134     * Called when EVENT_DISCONNECT_DONE is received.
3135     */
3136    private void onDisconnectDone(AsyncResult ar) {
3137        ApnContext apnContext = getValidApnContext(ar, "onDisconnectDone");
3138        if (apnContext == null) return;
3139
3140        if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
3141        apnContext.setState(DctConstants.State.IDLE);
3142
3143        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
3144
3145        // if all data connection are gone, check whether Airplane mode request was
3146        // pending.
3147        if (isDisconnected()) {
3148            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
3149                if (DBG) log("onDisconnectDone: radio will be turned off, no retries");
3150                // Radio will be turned off. No need to retry data setup
3151                apnContext.setApnSetting(null);
3152                apnContext.setDataConnectionAc(null);
3153
3154                // Need to notify disconnect as well, in the case of switching Airplane mode.
3155                // Otherwise, it would cause 30s delayed to turn on Airplane mode.
3156                if (mDisconnectPendingCount > 0) {
3157                    mDisconnectPendingCount--;
3158                }
3159
3160                if (mDisconnectPendingCount == 0) {
3161                    notifyDataDisconnectComplete();
3162                    notifyAllDataDisconnected();
3163                }
3164                return;
3165            }
3166        }
3167        // If APN is still enabled, try to bring it back up automatically
3168        if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
3169            try {
3170                SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
3171            } catch (RuntimeException ex) {
3172                log("Failed to set PUPPET_MASTER_RADIO_STRESS_TEST to false");
3173            }
3174            // Wait a bit before trying the next APN, so that
3175            // we're not tying up the RIL command channel.
3176            // This also helps in any external dependency to turn off the context.
3177            if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
3178            long delay = apnContext.getInterApnDelay(mFailFast);
3179            if (delay > 0) {
3180                // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
3181                // the waiting APN list, which will also reset/reconfigure the retry manager.
3182                startAlarmForReconnect(delay, apnContext);
3183            }
3184        } else {
3185            boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean(
3186                    com.android.internal.R.bool.config_restartRadioAfterProvisioning);
3187
3188            if (apnContext.isProvisioningApn() && restartRadioAfterProvisioning) {
3189                log("onDisconnectDone: restartRadio after provisioning");
3190                restartRadio();
3191            }
3192            apnContext.setApnSetting(null);
3193            apnContext.setDataConnectionAc(null);
3194            if (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())) {
3195                if(DBG) log("onDisconnectDone: isOnlySigneDcAllowed true so setup single apn");
3196                setupDataOnConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION);
3197            } else {
3198                if(DBG) log("onDisconnectDone: not retrying");
3199            }
3200        }
3201
3202        if (mDisconnectPendingCount > 0)
3203            mDisconnectPendingCount--;
3204
3205        if (mDisconnectPendingCount == 0) {
3206            apnContext.setConcurrentVoiceAndDataAllowed(
3207                    mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed());
3208            notifyDataDisconnectComplete();
3209            notifyAllDataDisconnected();
3210        }
3211
3212    }
3213
3214    /**
3215     * Called when EVENT_DISCONNECT_DC_RETRYING is received.
3216     */
3217    private void onDisconnectDcRetrying(AsyncResult ar) {
3218        // We could just do this in DC!!!
3219        ApnContext apnContext = getValidApnContext(ar, "onDisconnectDcRetrying");
3220        if (apnContext == null) return;
3221
3222        apnContext.setState(DctConstants.State.RETRYING);
3223        if(DBG) log("onDisconnectDcRetrying: apnContext=" + apnContext);
3224
3225        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
3226    }
3227
3228    private void onVoiceCallStarted() {
3229        if (DBG) log("onVoiceCallStarted");
3230        mInVoiceCall = true;
3231        if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
3232            if (DBG) log("onVoiceCallStarted stop polling");
3233            stopNetStatPoll();
3234            stopDataStallAlarm();
3235            notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
3236        }
3237    }
3238
3239    private void onVoiceCallEnded() {
3240        if (DBG) log("onVoiceCallEnded");
3241        mInVoiceCall = false;
3242        if (isConnected()) {
3243            if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
3244                startNetStatPoll();
3245                startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
3246                notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
3247            } else {
3248                // clean slate after call end.
3249                resetPollStats();
3250            }
3251        }
3252        // reset reconnect timer
3253        setupDataOnConnectableApns(Phone.REASON_VOICE_CALL_ENDED);
3254    }
3255
3256    private void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
3257        if (DBG) log("onCleanUpConnection");
3258        ApnContext apnContext = mApnContextsById.get(apnId);
3259        if (apnContext != null) {
3260            apnContext.setReason(reason);
3261            cleanUpConnection(tearDown, apnContext);
3262        }
3263    }
3264
3265    private boolean isConnected() {
3266        for (ApnContext apnContext : mApnContexts.values()) {
3267            if (apnContext.getState() == DctConstants.State.CONNECTED) {
3268                // At least one context is connected, return true
3269                return true;
3270            }
3271        }
3272        // There are not any contexts connected, return false
3273        return false;
3274    }
3275
3276    public boolean isDisconnected() {
3277        for (ApnContext apnContext : mApnContexts.values()) {
3278            if (!apnContext.isDisconnected()) {
3279                // At least one context was not disconnected return false
3280                return false;
3281            }
3282        }
3283        // All contexts were disconnected so return true
3284        return true;
3285    }
3286
3287    private void notifyDataConnection(String reason) {
3288        if (DBG) log("notifyDataConnection: reason=" + reason);
3289        for (ApnContext apnContext : mApnContexts.values()) {
3290            if (mAttached.get() && apnContext.isReady()) {
3291                if (DBG) log("notifyDataConnection: type:" + apnContext.getApnType());
3292                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
3293                        apnContext.getApnType());
3294            }
3295        }
3296        notifyOffApnsOfAvailability(reason);
3297    }
3298
3299    private void setDataProfilesAsNeeded() {
3300        if (DBG) log("setDataProfilesAsNeeded mSetDataProfileStatus: " + mSetDataProfileStatus);
3301        if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
3302            ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
3303            // Note getDataRoaming is also false if data not registered
3304            boolean isRoaming = mPhone.getServiceState().getDataRoaming();
3305            // If has set profile with home, and isRoaming is also false, no need to resend
3306            // Also skip if has set profile with roaming and isRoaming is true.
3307            if ((mSetDataProfileStatus == 1 && !isRoaming) ||
3308                (mSetDataProfileStatus == 2 && isRoaming)) {
3309                return;
3310            }
3311            for (ApnSetting apn : mAllApnSettings) {
3312                if (apn.modemCognitive) {
3313                    DataProfile dp = new DataProfile(apn, isRoaming);
3314                    // ArrayList.contains will call object.equals
3315                    if (!dps.contains(dp)) {
3316                        dps.add(dp);
3317                    }
3318                }
3319            }
3320            if(dps.size() > 0) {
3321                mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]), null);
3322                mSetDataProfileStatus = isRoaming ? 2 : 1;
3323            }
3324        }
3325    }
3326
3327    /**
3328     * Based on the sim operator numeric, create a list for all possible
3329     * Data Connections and setup the preferredApn.
3330     */
3331    private void createAllApnList() {
3332        mMvnoMatched = false;
3333        mAllApnSettings = new ArrayList<ApnSetting>();
3334        IccRecords r = mIccRecords.get();
3335        String operator = (r != null) ? r.getOperatorNumeric() : "";
3336        if (operator != null) {
3337            String selection = "numeric = '" + operator + "'";
3338            String orderBy = "_id";
3339            // query only enabled apn.
3340            // carrier_enabled : 1 means enabled apn, 0 disabled apn.
3341            // selection += " and carrier_enabled = 1";
3342            if (DBG) log("createAllApnList: selection=" + selection);
3343
3344            Cursor cursor = mPhone.getContext().getContentResolver().query(
3345                    Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);
3346
3347            if (cursor != null) {
3348                if (cursor.getCount() > 0) {
3349                    mAllApnSettings = createApnList(cursor);
3350                }
3351                cursor.close();
3352            }
3353        }
3354
3355        addEmergencyApnSetting();
3356
3357        dedupeApnSettings();
3358
3359        if (mAllApnSettings.isEmpty()) {
3360            if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
3361            mPreferredApn = null;
3362            // TODO: What is the right behavior?
3363            //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
3364        } else {
3365            mPreferredApn = getPreferredApn();
3366            if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
3367                mPreferredApn = null;
3368                setPreferredApn(-1);
3369            }
3370            if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
3371        }
3372        if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
3373
3374        setDataProfilesAsNeeded();
3375    }
3376
3377    private void dedupeApnSettings() {
3378        ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();
3379
3380        // coalesce APNs if they are similar enough to prevent
3381        // us from bringing up two data calls with the same interface
3382        int i = 0;
3383        while (i < mAllApnSettings.size() - 1) {
3384            ApnSetting first = mAllApnSettings.get(i);
3385            ApnSetting second = null;
3386            int j = i + 1;
3387            while (j < mAllApnSettings.size()) {
3388                second = mAllApnSettings.get(j);
3389                if (apnsSimilar(first, second)) {
3390                    ApnSetting newApn = mergeApns(first, second);
3391                    mAllApnSettings.set(i, newApn);
3392                    first = newApn;
3393                    mAllApnSettings.remove(j);
3394                } else {
3395                    j++;
3396                }
3397            }
3398            i++;
3399        }
3400    }
3401
3402    //check whether the types of two APN same (even only one type of each APN is same)
3403    private boolean apnTypeSameAny(ApnSetting first, ApnSetting second) {
3404        if(VDBG) {
3405            StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
3406            for(int index1 = 0; index1 < first.types.length; index1++) {
3407                apnType1.append(first.types[index1]);
3408                apnType1.append(",");
3409            }
3410
3411            StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
3412            for(int index1 = 0; index1 < second.types.length; index1++) {
3413                apnType2.append(second.types[index1]);
3414                apnType2.append(",");
3415            }
3416            log("APN1: is " + apnType1);
3417            log("APN2: is " + apnType2);
3418        }
3419
3420        for(int index1 = 0; index1 < first.types.length; index1++) {
3421            for(int index2 = 0; index2 < second.types.length; index2++) {
3422                if(first.types[index1].equals(PhoneConstants.APN_TYPE_ALL) ||
3423                        second.types[index2].equals(PhoneConstants.APN_TYPE_ALL) ||
3424                        first.types[index1].equals(second.types[index2])) {
3425                    if(VDBG)log("apnTypeSameAny: return true");
3426                    return true;
3427                }
3428            }
3429        }
3430
3431        if(VDBG)log("apnTypeSameAny: return false");
3432        return false;
3433    }
3434
3435    // Check if neither mention DUN and are substantially similar
3436    private boolean apnsSimilar(ApnSetting first, ApnSetting second) {
3437        return (first.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
3438                second.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
3439                Objects.equals(first.apn, second.apn) &&
3440                !apnTypeSameAny(first, second) &&
3441                xorEquals(first.proxy, second.proxy) &&
3442                xorEquals(first.port, second.port) &&
3443                first.carrierEnabled == second.carrierEnabled &&
3444                first.bearerBitmask == second.bearerBitmask &&
3445                first.profileId == second.profileId &&
3446                Objects.equals(first.mvnoType, second.mvnoType) &&
3447                Objects.equals(first.mvnoMatchData, second.mvnoMatchData) &&
3448                xorEquals(first.mmsc, second.mmsc) &&
3449                xorEquals(first.mmsProxy, second.mmsProxy) &&
3450                xorEquals(first.mmsPort, second.mmsPort));
3451    }
3452
3453    // equal or one is not specified
3454    private boolean xorEquals(String first, String second) {
3455        return (Objects.equals(first, second) ||
3456                TextUtils.isEmpty(first) ||
3457                TextUtils.isEmpty(second));
3458    }
3459
3460    private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
3461        int id = dest.id;
3462        ArrayList<String> resultTypes = new ArrayList<String>();
3463        resultTypes.addAll(Arrays.asList(dest.types));
3464        for (String srcType : src.types) {
3465            if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
3466            if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
3467        }
3468        String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
3469        String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
3470        String mmsPort = (TextUtils.isEmpty(dest.mmsPort) ? src.mmsPort : dest.mmsPort);
3471        String proxy = (TextUtils.isEmpty(dest.proxy) ? src.proxy : dest.proxy);
3472        String port = (TextUtils.isEmpty(dest.port) ? src.port : dest.port);
3473        String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
3474        String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
3475                dest.roamingProtocol;
3476        int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0) ?
3477                0 : (dest.bearerBitmask | src.bearerBitmask);
3478
3479        return new ApnSetting(id, dest.numeric, dest.carrier, dest.apn,
3480                proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
3481                dest.authType, resultTypes.toArray(new String[0]), protocol,
3482                roamingProtocol, dest.carrierEnabled, 0, bearerBitmask, dest.profileId,
3483                (dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
3484                dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
3485    }
3486
3487    /** Return the DC AsyncChannel for the new data connection */
3488    private DcAsyncChannel createDataConnection() {
3489        if (DBG) log("createDataConnection E");
3490
3491        int id = mUniqueIdGenerator.getAndIncrement();
3492        DataConnection conn = DataConnection.makeDataConnection(mPhone, id,
3493                                                this, mDcTesterFailBringUpAll, mDcc);
3494        mDataConnections.put(id, conn);
3495        DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
3496        int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
3497        if (status == AsyncChannel.STATUS_SUCCESSFUL) {
3498            mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac);
3499        } else {
3500            loge("createDataConnection: Could not connect to dcac=" + dcac + " status=" + status);
3501        }
3502
3503        if (DBG) log("createDataConnection() X id=" + id + " dc=" + conn);
3504        return dcac;
3505    }
3506
3507    private void destroyDataConnections() {
3508        if(mDataConnections != null) {
3509            if (DBG) log("destroyDataConnections: clear mDataConnectionList");
3510            mDataConnections.clear();
3511        } else {
3512            if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
3513        }
3514    }
3515
3516    /**
3517     * Build a list of APNs to be used to create PDP's.
3518     *
3519     * @param requestedApnType
3520     * @return waitingApns list to be used to create PDP
3521     *          error when waitingApns.isEmpty()
3522     */
3523    private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, int radioTech) {
3524        if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
3525        ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
3526
3527        if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
3528            ApnSetting dun = fetchDunApn();
3529            if (dun != null) {
3530                apnList.add(dun);
3531                if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
3532                return apnList;
3533            }
3534        }
3535
3536        IccRecords r = mIccRecords.get();
3537        String operator = (r != null) ? r.getOperatorNumeric() : "";
3538
3539        // This is a workaround for a bug (7305641) where we don't failover to other
3540        // suitable APNs if our preferred APN fails.  On prepaid ATT sims we need to
3541        // failover to a provisioning APN, but once we've used their default data
3542        // connection we are locked to it for life.  This change allows ATT devices
3543        // to say they don't want to use preferred at all.
3544        boolean usePreferred = true;
3545        try {
3546            usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
3547                    internal.R.bool.config_dontPreferApn);
3548        } catch (Resources.NotFoundException e) {
3549            if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
3550            usePreferred = true;
3551        }
3552        if (usePreferred) {
3553            mPreferredApn = getPreferredApn();
3554        }
3555        if (DBG) {
3556            log("buildWaitingApns: usePreferred=" + usePreferred
3557                    + " canSetPreferApn=" + mCanSetPreferApn
3558                    + " mPreferredApn=" + mPreferredApn
3559                    + " operator=" + operator + " radioTech=" + radioTech
3560                    + " IccRecords r=" + r);
3561        }
3562
3563        if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
3564                mPreferredApn.canHandleType(requestedApnType)) {
3565            if (DBG) {
3566                log("buildWaitingApns: Preferred APN:" + operator + ":"
3567                        + mPreferredApn.numeric + ":" + mPreferredApn);
3568            }
3569            if (mPreferredApn.numeric.equals(operator)) {
3570                if (ServiceState.bitmaskHasTech(mPreferredApn.bearerBitmask, radioTech)) {
3571                    apnList.add(mPreferredApn);
3572                    if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
3573                    return apnList;
3574                } else {
3575                    if (DBG) log("buildWaitingApns: no preferred APN");
3576                    setPreferredApn(-1);
3577                    mPreferredApn = null;
3578                }
3579            } else {
3580                if (DBG) log("buildWaitingApns: no preferred APN");
3581                setPreferredApn(-1);
3582                mPreferredApn = null;
3583            }
3584        }
3585        if (mAllApnSettings != null) {
3586            if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
3587            for (ApnSetting apn : mAllApnSettings) {
3588                if (apn.canHandleType(requestedApnType)) {
3589                    if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {
3590                        if (DBG) log("buildWaitingApns: adding apn=" + apn);
3591                        apnList.add(apn);
3592                    } else {
3593                        if (DBG) {
3594                            log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask + " does " +
3595                                    "not include radioTech:" + radioTech);
3596                        }
3597                    }
3598                } else if (DBG) {
3599                    log("buildWaitingApns: couldn't handle requested ApnType="
3600                            + requestedApnType);
3601                }
3602            }
3603        } else {
3604            loge("mAllApnSettings is null!");
3605        }
3606        if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList);
3607        return apnList;
3608    }
3609
3610    private String apnListToString (ArrayList<ApnSetting> apns) {
3611        StringBuilder result = new StringBuilder();
3612        for (int i = 0, size = apns.size(); i < size; i++) {
3613            result.append('[')
3614                  .append(apns.get(i).toString())
3615                  .append(']');
3616        }
3617        return result.toString();
3618    }
3619
3620    private void setPreferredApn(int pos) {
3621        if (!mCanSetPreferApn) {
3622            log("setPreferredApn: X !canSEtPreferApn");
3623            return;
3624        }
3625
3626        String subId = Long.toString(mPhone.getSubId());
3627        Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
3628        log("setPreferredApn: delete");
3629        ContentResolver resolver = mPhone.getContext().getContentResolver();
3630        resolver.delete(uri, null, null);
3631
3632        if (pos >= 0) {
3633            log("setPreferredApn: insert");
3634            ContentValues values = new ContentValues();
3635            values.put(APN_ID, pos);
3636            resolver.insert(uri, values);
3637        }
3638    }
3639
3640    private ApnSetting getPreferredApn() {
3641        if (mAllApnSettings == null || mAllApnSettings.isEmpty()) {
3642            log("getPreferredApn: mAllApnSettings is " + ((mAllApnSettings == null)?"null":"empty"));
3643            return null;
3644        }
3645
3646        String subId = Long.toString(mPhone.getSubId());
3647        Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
3648        Cursor cursor = mPhone.getContext().getContentResolver().query(
3649                uri, new String[] { "_id", "name", "apn" },
3650                null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
3651
3652        if (cursor != null) {
3653            mCanSetPreferApn = true;
3654        } else {
3655            mCanSetPreferApn = false;
3656        }
3657        log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
3658                + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));
3659
3660        if (mCanSetPreferApn && cursor.getCount() > 0) {
3661            int pos;
3662            cursor.moveToFirst();
3663            pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
3664            for(ApnSetting p : mAllApnSettings) {
3665                log("getPreferredApn: apnSetting=" + p);
3666                if (p.id == pos && p.canHandleType(mRequestedApnType)) {
3667                    log("getPreferredApn: X found apnSetting" + p);
3668                    cursor.close();
3669                    return p;
3670                }
3671            }
3672        }
3673
3674        if (cursor != null) {
3675            cursor.close();
3676        }
3677
3678        log("getPreferredApn: X not found");
3679        return null;
3680    }
3681
3682    @Override
3683    public void handleMessage (Message msg) {
3684        if (VDBG) log("handleMessage msg=" + msg);
3685
3686        switch (msg.what) {
3687            case DctConstants.EVENT_RECORDS_LOADED:
3688                // If onRecordsLoadedOrSubIdChanged() is not called here, it should be called on
3689                // onSubscriptionsChanged() when a valid subId is available.
3690                int subId = mPhone.getSubId();
3691                if (SubscriptionManager.isValidSubscriptionId(subId)) {
3692                    onRecordsLoadedOrSubIdChanged();
3693                } else {
3694                    log("Ignoring EVENT_RECORDS_LOADED as subId is not valid: " + subId);
3695                }
3696                break;
3697
3698            case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
3699                onDataConnectionDetached();
3700                break;
3701
3702            case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
3703                onDataConnectionAttached();
3704                break;
3705
3706            case DctConstants.EVENT_DO_RECOVERY:
3707                doRecovery();
3708                break;
3709
3710            case DctConstants.EVENT_APN_CHANGED:
3711                onApnChanged();
3712                break;
3713
3714            case DctConstants.EVENT_PS_RESTRICT_ENABLED:
3715                /**
3716                 * We don't need to explicitly to tear down the PDP context
3717                 * when PS restricted is enabled. The base band will deactive
3718                 * PDP context and notify us with PDP_CONTEXT_CHANGED.
3719                 * But we should stop the network polling and prevent reset PDP.
3720                 */
3721                if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
3722                stopNetStatPoll();
3723                stopDataStallAlarm();
3724                mIsPsRestricted = true;
3725                break;
3726
3727            case DctConstants.EVENT_PS_RESTRICT_DISABLED:
3728                /**
3729                 * When PS restrict is removed, we need setup PDP connection if
3730                 * PDP connection is down.
3731                 */
3732                if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
3733                mIsPsRestricted  = false;
3734                if (isConnected()) {
3735                    startNetStatPoll();
3736                    startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
3737                } else {
3738                    // TODO: Should all PDN states be checked to fail?
3739                    if (mState == DctConstants.State.FAILED) {
3740                        cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
3741                        mReregisterOnReconnectFailure = false;
3742                    }
3743                    ApnContext apnContext = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
3744                    if (apnContext != null) {
3745                        apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
3746                        trySetupData(apnContext);
3747                    } else {
3748                        loge("**** Default ApnContext not found ****");
3749                        if (Build.IS_DEBUGGABLE) {
3750                            throw new RuntimeException("Default ApnContext not found");
3751                        }
3752                    }
3753                }
3754                break;
3755
3756            case DctConstants.EVENT_TRY_SETUP_DATA:
3757                if (msg.obj instanceof ApnContext) {
3758                    onTrySetupData((ApnContext)msg.obj);
3759                } else if (msg.obj instanceof String) {
3760                    onTrySetupData((String)msg.obj);
3761                } else {
3762                    loge("EVENT_TRY_SETUP request w/o apnContext or String");
3763                }
3764                break;
3765
3766            case DctConstants.EVENT_CLEAN_UP_CONNECTION:
3767                boolean tearDown = (msg.arg1 == 0) ? false : true;
3768                if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
3769                if (msg.obj instanceof ApnContext) {
3770                    cleanUpConnection(tearDown, (ApnContext)msg.obj);
3771                } else {
3772                    onCleanUpConnection(tearDown, msg.arg2, (String) msg.obj);
3773                }
3774                break;
3775            case DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE: {
3776                final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
3777                onSetInternalDataEnabled(enabled, (Message) msg.obj);
3778                break;
3779            }
3780            case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS:
3781                if ((msg.obj != null) && (msg.obj instanceof String == false)) {
3782                    msg.obj = null;
3783                }
3784                onCleanUpAllConnections((String) msg.obj);
3785                break;
3786
3787            case DctConstants.EVENT_DATA_RAT_CHANGED:
3788                //May new Network allow setupData, so try it here
3789                setupDataOnConnectableApns(Phone.REASON_NW_TYPE_CHANGED,
3790                        RetryFailures.ONLY_ON_CHANGE);
3791                break;
3792
3793            case DctConstants.CMD_CLEAR_PROVISIONING_SPINNER:
3794                // Check message sender intended to clear the current spinner.
3795                if (mProvisioningSpinner == msg.obj) {
3796                    mProvisioningSpinner.dismiss();
3797                    mProvisioningSpinner = null;
3798                }
3799                break;
3800            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
3801                log("DISCONNECTED_CONNECTED: msg=" + msg);
3802                DcAsyncChannel dcac = (DcAsyncChannel) msg.obj;
3803                mDataConnectionAcHashMap.remove(dcac.getDataConnectionIdSync());
3804                dcac.disconnected();
3805                break;
3806            }
3807            case DctConstants.EVENT_ENABLE_NEW_APN:
3808                onEnableApn(msg.arg1, msg.arg2);
3809                break;
3810
3811            case DctConstants.EVENT_DATA_STALL_ALARM:
3812                onDataStallAlarm(msg.arg1);
3813                break;
3814
3815            case DctConstants.EVENT_ROAMING_OFF:
3816                onRoamingOff();
3817                break;
3818
3819            case DctConstants.EVENT_ROAMING_ON:
3820                onRoamingOn();
3821                break;
3822
3823            case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE:
3824                onDeviceProvisionedChange();
3825                break;
3826
3827            case DctConstants.EVENT_REDIRECTION_DETECTED:
3828                String url = (String) msg.obj;
3829                log("dataConnectionTracker.handleMessage: EVENT_REDIRECTION_DETECTED=" + url);
3830                onDataConnectionRedirected(url);
3831
3832            case DctConstants.EVENT_RADIO_AVAILABLE:
3833                onRadioAvailable();
3834                break;
3835
3836            case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
3837                onRadioOffOrNotAvailable();
3838                break;
3839
3840            case DctConstants.EVENT_DATA_SETUP_COMPLETE:
3841                onDataSetupComplete((AsyncResult) msg.obj);
3842                break;
3843
3844            case DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR:
3845                onDataSetupCompleteError((AsyncResult) msg.obj);
3846                break;
3847
3848            case DctConstants.EVENT_DISCONNECT_DONE:
3849                log("DataConnectionTracker.handleMessage: EVENT_DISCONNECT_DONE msg=" + msg);
3850                onDisconnectDone((AsyncResult) msg.obj);
3851                break;
3852
3853            case DctConstants.EVENT_DISCONNECT_DC_RETRYING:
3854                log("DataConnectionTracker.handleMessage: EVENT_DISCONNECT_DC_RETRYING msg=" + msg);
3855                onDisconnectDcRetrying((AsyncResult) msg.obj);
3856                break;
3857
3858            case DctConstants.EVENT_VOICE_CALL_STARTED:
3859                onVoiceCallStarted();
3860                break;
3861
3862            case DctConstants.EVENT_VOICE_CALL_ENDED:
3863                onVoiceCallEnded();
3864                break;
3865
3866            case DctConstants.EVENT_RESET_DONE: {
3867                if (DBG) log("EVENT_RESET_DONE");
3868                onResetDone((AsyncResult) msg.obj);
3869                break;
3870            }
3871            case DctConstants.CMD_SET_USER_DATA_ENABLE: {
3872                final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
3873                if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
3874                onSetUserDataEnabled(enabled);
3875                break;
3876            }
3877            // TODO - remove
3878            case DctConstants.CMD_SET_DEPENDENCY_MET: {
3879                boolean met = (msg.arg1 == DctConstants.ENABLED) ? true : false;
3880                if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
3881                Bundle bundle = msg.getData();
3882                if (bundle != null) {
3883                    String apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
3884                    if (apnType != null) {
3885                        onSetDependencyMet(apnType, met);
3886                    }
3887                }
3888                break;
3889            }
3890            case DctConstants.CMD_SET_POLICY_DATA_ENABLE: {
3891                final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
3892                onSetPolicyDataEnabled(enabled);
3893                break;
3894            }
3895            case DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: {
3896                sEnableFailFastRefCounter += (msg.arg1 == DctConstants.ENABLED) ? 1 : -1;
3897                if (DBG) {
3898                    log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: "
3899                            + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter);
3900                }
3901                if (sEnableFailFastRefCounter < 0) {
3902                    final String s = "CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: "
3903                            + "sEnableFailFastRefCounter:" + sEnableFailFastRefCounter + " < 0";
3904                    loge(s);
3905                    sEnableFailFastRefCounter = 0;
3906                }
3907                final boolean enabled = sEnableFailFastRefCounter > 0;
3908                if (DBG) {
3909                    log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: enabled=" + enabled
3910                            + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter);
3911                }
3912                if (mFailFast != enabled) {
3913                    mFailFast = enabled;
3914
3915                    mDataStallDetectionEnabled = !enabled;
3916                    if (mDataStallDetectionEnabled
3917                            && (getOverallState() == DctConstants.State.CONNECTED)
3918                            && (!mInVoiceCall ||
3919                                    mPhone.getServiceStateTracker()
3920                                        .isConcurrentVoiceAndDataAllowed())) {
3921                        if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: start data stall");
3922                        stopDataStallAlarm();
3923                        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
3924                    } else {
3925                        if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: stop data stall");
3926                        stopDataStallAlarm();
3927                    }
3928                }
3929
3930                break;
3931            }
3932            case DctConstants.CMD_ENABLE_MOBILE_PROVISIONING: {
3933                Bundle bundle = msg.getData();
3934                if (bundle != null) {
3935                    try {
3936                        mProvisioningUrl = (String)bundle.get(DctConstants.PROVISIONING_URL_KEY);
3937                    } catch(ClassCastException e) {
3938                        loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url not a string" + e);
3939                        mProvisioningUrl = null;
3940                    }
3941                }
3942                if (TextUtils.isEmpty(mProvisioningUrl)) {
3943                    loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url is empty, ignoring");
3944                    mIsProvisioning = false;
3945                    mProvisioningUrl = null;
3946                } else {
3947                    loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioningUrl=" + mProvisioningUrl);
3948                    mIsProvisioning = true;
3949                    startProvisioningApnAlarm();
3950                }
3951                break;
3952            }
3953            case DctConstants.EVENT_PROVISIONING_APN_ALARM: {
3954                if (DBG) log("EVENT_PROVISIONING_APN_ALARM");
3955                ApnContext apnCtx = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
3956                if (apnCtx.isProvisioningApn() && apnCtx.isConnectedOrConnecting()) {
3957                    if (mProvisioningApnAlarmTag == msg.arg1) {
3958                        if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Disconnecting");
3959                        mIsProvisioning = false;
3960                        mProvisioningUrl = null;
3961                        stopProvisioningApnAlarm();
3962                        sendCleanUpConnection(true, apnCtx);
3963                    } else {
3964                        if (DBG) {
3965                            log("EVENT_PROVISIONING_APN_ALARM: ignore stale tag,"
3966                                    + " mProvisioningApnAlarmTag:" + mProvisioningApnAlarmTag
3967                                    + " != arg1:" + msg.arg1);
3968                        }
3969                    }
3970                } else {
3971                    if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Not connected ignore");
3972                }
3973                break;
3974            }
3975            case DctConstants.CMD_IS_PROVISIONING_APN: {
3976                if (DBG) log("CMD_IS_PROVISIONING_APN");
3977                boolean isProvApn;
3978                try {
3979                    String apnType = null;
3980                    Bundle bundle = msg.getData();
3981                    if (bundle != null) {
3982                        apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
3983                    }
3984                    if (TextUtils.isEmpty(apnType)) {
3985                        loge("CMD_IS_PROVISIONING_APN: apnType is empty");
3986                        isProvApn = false;
3987                    } else {
3988                        isProvApn = isProvisioningApn(apnType);
3989                    }
3990                } catch (ClassCastException e) {
3991                    loge("CMD_IS_PROVISIONING_APN: NO provisioning url ignoring");
3992                    isProvApn = false;
3993                }
3994                if (DBG) log("CMD_IS_PROVISIONING_APN: ret=" + isProvApn);
3995                mReplyAc.replyToMessage(msg, DctConstants.CMD_IS_PROVISIONING_APN,
3996                        isProvApn ? DctConstants.ENABLED : DctConstants.DISABLED);
3997                break;
3998            }
3999            case DctConstants.EVENT_ICC_CHANGED: {
4000                onUpdateIcc();
4001                break;
4002            }
4003            case DctConstants.EVENT_RESTART_RADIO: {
4004                restartRadio();
4005                break;
4006            }
4007            case DctConstants.CMD_NET_STAT_POLL: {
4008                if (msg.arg1 == DctConstants.ENABLED) {
4009                    handleStartNetStatPoll((DctConstants.Activity)msg.obj);
4010                } else if (msg.arg1 == DctConstants.DISABLED) {
4011                    handleStopNetStatPoll((DctConstants.Activity)msg.obj);
4012                }
4013                break;
4014            }
4015            case DctConstants.EVENT_DATA_STATE_CHANGED: {
4016                // no longer do anything, but still registered - clean up log
4017                // TODO - why are we still registering?
4018                break;
4019            }
4020            case DctConstants.EVENT_PCO_DATA_RECEIVED: {
4021                handlePcoData((AsyncResult)msg.obj);
4022                break;
4023            }
4024            case DctConstants.EVENT_SET_CARRIER_DATA_ENABLED:
4025                onSetCarrierDataEnabled(msg.arg1 == DctConstants.ENABLED);
4026                break;
4027            default:
4028                Rlog.e("DcTracker", "Unhandled event=" + msg);
4029                break;
4030
4031        }
4032    }
4033
4034    private int getApnProfileID(String apnType) {
4035        if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
4036            return RILConstants.DATA_PROFILE_IMS;
4037        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_FOTA)) {
4038            return RILConstants.DATA_PROFILE_FOTA;
4039        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_CBS)) {
4040            return RILConstants.DATA_PROFILE_CBS;
4041        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IA)) {
4042            return RILConstants.DATA_PROFILE_DEFAULT; // DEFAULT for now
4043        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_DUN)) {
4044            return RILConstants.DATA_PROFILE_TETHERED;
4045        } else {
4046            return RILConstants.DATA_PROFILE_DEFAULT;
4047        }
4048    }
4049
4050    private int getCellLocationId() {
4051        int cid = -1;
4052        CellLocation loc = mPhone.getCellLocation();
4053
4054        if (loc != null) {
4055            if (loc instanceof GsmCellLocation) {
4056                cid = ((GsmCellLocation)loc).getCid();
4057            } else if (loc instanceof CdmaCellLocation) {
4058                cid = ((CdmaCellLocation)loc).getBaseStationId();
4059            }
4060        }
4061        return cid;
4062    }
4063
4064    private IccRecords getUiccRecords(int appFamily) {
4065        return mUiccController.getIccRecords(mPhone.getPhoneId(), appFamily);
4066    }
4067
4068
4069    private void onUpdateIcc() {
4070        if (mUiccController == null ) {
4071            return;
4072        }
4073
4074        IccRecords newIccRecords = getUiccRecords(UiccController.APP_FAM_3GPP);
4075
4076        IccRecords r = mIccRecords.get();
4077        if (r != newIccRecords) {
4078            if (r != null) {
4079                log("Removing stale icc objects.");
4080                r.unregisterForRecordsLoaded(this);
4081                mIccRecords.set(null);
4082            }
4083            if (newIccRecords != null) {
4084                if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
4085                    log("New records found.");
4086                    mIccRecords.set(newIccRecords);
4087                    newIccRecords.registerForRecordsLoaded(
4088                            this, DctConstants.EVENT_RECORDS_LOADED, null);
4089                    // reset carrier actions on sim loaded
4090                    final ServiceStateTracker sst = mPhone.getServiceStateTracker();
4091                    sst.setRadioPowerFromCarrier(true);
4092                    mDataEnabledSettings.setCarrierDataEnabled(true);
4093                    mPhone.getCarrierSignalAgent().reset();
4094                }
4095            } else {
4096                onSimNotReady();
4097            }
4098        }
4099    }
4100
4101    public void update() {
4102        log("update sub = " + mPhone.getSubId());
4103        log("update(): Active DDS, register for all events now!");
4104        onUpdateIcc();
4105
4106        mDataEnabledSettings.setUserDataEnabled(getDataEnabled());
4107        mAutoAttachOnCreation.set(false);
4108
4109        ((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider();
4110    }
4111
4112    public void cleanUpAllConnections(String cause) {
4113        cleanUpAllConnections(cause, null);
4114    }
4115
4116    public void updateRecords() {
4117        onUpdateIcc();
4118    }
4119
4120    public void cleanUpAllConnections(String cause, Message disconnectAllCompleteMsg) {
4121        log("cleanUpAllConnections");
4122        if (disconnectAllCompleteMsg != null) {
4123            mDisconnectAllCompleteMsgList.add(disconnectAllCompleteMsg);
4124        }
4125
4126        Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS);
4127        msg.obj = cause;
4128        sendMessage(msg);
4129    }
4130
4131    private void notifyDataDisconnectComplete() {
4132        log("notifyDataDisconnectComplete");
4133        for (Message m: mDisconnectAllCompleteMsgList) {
4134            m.sendToTarget();
4135        }
4136        mDisconnectAllCompleteMsgList.clear();
4137    }
4138
4139
4140    private void notifyAllDataDisconnected() {
4141        sEnableFailFastRefCounter = 0;
4142        mFailFast = false;
4143        mAllDataDisconnectedRegistrants.notifyRegistrants();
4144    }
4145
4146    public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
4147        mAllDataDisconnectedRegistrants.addUnique(h, what, obj);
4148
4149        if (isDisconnected()) {
4150            log("notify All Data Disconnected");
4151            notifyAllDataDisconnected();
4152        }
4153    }
4154
4155    public void unregisterForAllDataDisconnected(Handler h) {
4156        mAllDataDisconnectedRegistrants.remove(h);
4157    }
4158
4159    public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
4160        mDataEnabledSettings.registerForDataEnabledChanged(h, what, obj);
4161    }
4162
4163    public void unregisterForDataEnabledChanged(Handler h) {
4164        mDataEnabledSettings.unregisterForDataEnabledChanged(h);
4165    }
4166
4167    private void onSetInternalDataEnabled(boolean enabled, Message onCompleteMsg) {
4168        synchronized (mDataEnabledSettings) {
4169            if (DBG) log("onSetInternalDataEnabled: enabled=" + enabled);
4170            boolean sendOnComplete = true;
4171
4172            mDataEnabledSettings.setInternalDataEnabled(enabled);
4173            if (enabled) {
4174                log("onSetInternalDataEnabled: changed to enabled, try to setup data call");
4175                onTrySetupData(Phone.REASON_DATA_ENABLED);
4176            } else {
4177                sendOnComplete = false;
4178                log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections");
4179                cleanUpAllConnections(Phone.REASON_DATA_DISABLED, onCompleteMsg);
4180            }
4181
4182            if (sendOnComplete) {
4183                if (onCompleteMsg != null) {
4184                    onCompleteMsg.sendToTarget();
4185                }
4186            }
4187        }
4188    }
4189
4190    public boolean setInternalDataEnabled(boolean enable) {
4191        return setInternalDataEnabled(enable, null);
4192    }
4193
4194    public boolean setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
4195        if (DBG) log("setInternalDataEnabled(" + enable + ")");
4196
4197        Message msg = obtainMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE, onCompleteMsg);
4198        msg.arg1 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
4199        sendMessage(msg);
4200        return true;
4201    }
4202
4203    private void log(String s) {
4204        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
4205    }
4206
4207    private void loge(String s) {
4208        Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
4209    }
4210
4211    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4212        pw.println("DcTracker:");
4213        pw.println(" RADIO_TESTS=" + RADIO_TESTS);
4214        pw.println(" isInternalDataEnabled=" + mDataEnabledSettings.isInternalDataEnabled());
4215        pw.println(" isUserDataEnabled=" + mDataEnabledSettings.isUserDataEnabled());
4216        pw.println(" isPolicyDataEnabled=" + mDataEnabledSettings.isPolicyDataEnabled());
4217        pw.flush();
4218        pw.println(" mRequestedApnType=" + mRequestedApnType);
4219        pw.println(" mPhone=" + mPhone.getPhoneName());
4220        pw.println(" mActivity=" + mActivity);
4221        pw.println(" mState=" + mState);
4222        pw.println(" mTxPkts=" + mTxPkts);
4223        pw.println(" mRxPkts=" + mRxPkts);
4224        pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod);
4225        pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled);
4226        pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum);
4227        pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag);
4228        pw.println(" mDataStallDetectionEnabled=" + mDataStallDetectionEnabled);
4229        pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
4230        pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
4231        pw.println(" mResolver=" + mResolver);
4232        pw.println(" mIsWifiConnected=" + mIsWifiConnected);
4233        pw.println(" mReconnectIntent=" + mReconnectIntent);
4234        pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation.get());
4235        pw.println(" mIsScreenOn=" + mIsScreenOn);
4236        pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
4237        pw.println(" mSetDataProfileStatus=" + mSetDataProfileStatus);
4238        pw.flush();
4239        pw.println(" ***************************************");
4240        DcController dcc = mDcc;
4241        if (dcc != null) {
4242            dcc.dump(fd, pw, args);
4243        } else {
4244            pw.println(" mDcc=null");
4245        }
4246        pw.println(" ***************************************");
4247        HashMap<Integer, DataConnection> dcs = mDataConnections;
4248        if (dcs != null) {
4249            Set<Entry<Integer, DataConnection> > mDcSet = mDataConnections.entrySet();
4250            pw.println(" mDataConnections: count=" + mDcSet.size());
4251            for (Entry<Integer, DataConnection> entry : mDcSet) {
4252                pw.printf(" *** mDataConnection[%d] \n", entry.getKey());
4253                entry.getValue().dump(fd, pw, args);
4254            }
4255        } else {
4256            pw.println("mDataConnections=null");
4257        }
4258        pw.println(" ***************************************");
4259        pw.flush();
4260        HashMap<String, Integer> apnToDcId = mApnToDataConnectionId;
4261        if (apnToDcId != null) {
4262            Set<Entry<String, Integer>> apnToDcIdSet = apnToDcId.entrySet();
4263            pw.println(" mApnToDataConnectonId size=" + apnToDcIdSet.size());
4264            for (Entry<String, Integer> entry : apnToDcIdSet) {
4265                pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue());
4266            }
4267        } else {
4268            pw.println("mApnToDataConnectionId=null");
4269        }
4270        pw.println(" ***************************************");
4271        pw.flush();
4272        ConcurrentHashMap<String, ApnContext> apnCtxs = mApnContexts;
4273        if (apnCtxs != null) {
4274            Set<Entry<String, ApnContext>> apnCtxsSet = apnCtxs.entrySet();
4275            pw.println(" mApnContexts size=" + apnCtxsSet.size());
4276            for (Entry<String, ApnContext> entry : apnCtxsSet) {
4277                entry.getValue().dump(fd, pw, args);
4278            }
4279            pw.println(" ***************************************");
4280        } else {
4281            pw.println(" mApnContexts=null");
4282        }
4283        pw.flush();
4284        ArrayList<ApnSetting> apnSettings = mAllApnSettings;
4285        if (apnSettings != null) {
4286            pw.println(" mAllApnSettings size=" + apnSettings.size());
4287            for (int i=0; i < apnSettings.size(); i++) {
4288                pw.printf(" mAllApnSettings[%d]: %s\n", i, apnSettings.get(i));
4289            }
4290            pw.flush();
4291        } else {
4292            pw.println(" mAllApnSettings=null");
4293        }
4294        pw.println(" mPreferredApn=" + mPreferredApn);
4295        pw.println(" mIsPsRestricted=" + mIsPsRestricted);
4296        pw.println(" mIsDisposed=" + mIsDisposed);
4297        pw.println(" mIntentReceiver=" + mIntentReceiver);
4298        pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
4299        pw.println(" canSetPreferApn=" + mCanSetPreferApn);
4300        pw.println(" mApnObserver=" + mApnObserver);
4301        pw.println(" getOverallState=" + getOverallState());
4302        pw.println(" mDataConnectionAsyncChannels=%s\n" + mDataConnectionAcHashMap);
4303        pw.println(" mAttached=" + mAttached.get());
4304        pw.flush();
4305    }
4306
4307    public String[] getPcscfAddress(String apnType) {
4308        log("getPcscfAddress()");
4309        ApnContext apnContext = null;
4310
4311        if(apnType == null){
4312            log("apnType is null, return null");
4313            return null;
4314        }
4315
4316        if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_EMERGENCY)) {
4317            apnContext = mApnContextsById.get(DctConstants.APN_EMERGENCY_ID);
4318        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
4319            apnContext = mApnContextsById.get(DctConstants.APN_IMS_ID);
4320        } else {
4321            log("apnType is invalid, return null");
4322            return null;
4323        }
4324
4325        if (apnContext == null) {
4326            log("apnContext is null, return null");
4327            return null;
4328        }
4329
4330        DcAsyncChannel dcac = apnContext.getDcAc();
4331        String[] result = null;
4332
4333        if (dcac != null) {
4334            result = dcac.getPcscfAddr();
4335
4336            for (int i = 0; i < result.length; i++) {
4337                log("Pcscf[" + i + "]: " + result[i]);
4338            }
4339            return result;
4340        }
4341        return null;
4342    }
4343
4344    /**
4345     * Read APN configuration from Telephony.db for Emergency APN
4346     * All opertors recognize the connection request for EPDN based on APN type
4347     * PLMN name,APN name are not mandatory parameters
4348     */
4349    private void initEmergencyApnSetting() {
4350        // Operator Numeric is not available when sim records are not loaded.
4351        // Query Telephony.db with APN type as EPDN request does not
4352        // require APN name, plmn and all operators support same APN config.
4353        // DB will contain only one entry for Emergency APN
4354        String selection = "type=\"emergency\"";
4355        Cursor cursor = mPhone.getContext().getContentResolver().query(
4356                Telephony.Carriers.CONTENT_URI, null, selection, null, null);
4357
4358        if (cursor != null) {
4359            if (cursor.getCount() > 0) {
4360                if (cursor.moveToFirst()) {
4361                    mEmergencyApn = makeApnSetting(cursor);
4362                }
4363            }
4364            cursor.close();
4365        }
4366    }
4367
4368    /**
4369     * Add the Emergency APN settings to APN settings list
4370     */
4371    private void addEmergencyApnSetting() {
4372        if(mEmergencyApn != null) {
4373            if(mAllApnSettings == null) {
4374                mAllApnSettings = new ArrayList<ApnSetting>();
4375            } else {
4376                boolean hasEmergencyApn = false;
4377                for (ApnSetting apn : mAllApnSettings) {
4378                    if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_EMERGENCY)) {
4379                        hasEmergencyApn = true;
4380                        break;
4381                    }
4382                }
4383
4384                if(hasEmergencyApn == false) {
4385                    mAllApnSettings.add(mEmergencyApn);
4386                } else {
4387                    log("addEmergencyApnSetting - E-APN setting is already present");
4388                }
4389            }
4390        }
4391    }
4392
4393    private void cleanUpConnectionsOnUpdatedApns(boolean tearDown) {
4394        if (DBG) log("cleanUpConnectionsOnUpdatedApns: tearDown=" + tearDown);
4395        if (mAllApnSettings.isEmpty()) {
4396            cleanUpAllConnections(tearDown, Phone.REASON_APN_CHANGED);
4397        } else {
4398            for (ApnContext apnContext : mApnContexts.values()) {
4399                if (VDBG) log("cleanUpConnectionsOnUpdatedApns for "+ apnContext);
4400
4401                boolean cleanUpApn = true;
4402                ArrayList<ApnSetting> currentWaitingApns = apnContext.getWaitingApns();
4403
4404                if ((currentWaitingApns != null) && (!apnContext.isDisconnected())) {
4405                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
4406                    ArrayList<ApnSetting> waitingApns = buildWaitingApns(
4407                            apnContext.getApnType(), radioTech);
4408                    if (VDBG) log("new waitingApns:" + waitingApns);
4409                    if (waitingApns.size() == currentWaitingApns.size()) {
4410                        cleanUpApn = false;
4411                        for (int i = 0; i < waitingApns.size(); i++) {
4412                            if (!currentWaitingApns.get(i).equals(waitingApns.get(i))) {
4413                                if (VDBG) log("new waiting apn is different at " + i);
4414                                cleanUpApn = true;
4415                                apnContext.setWaitingApns(waitingApns);
4416                                break;
4417                            }
4418                        }
4419                    }
4420                }
4421
4422                if (cleanUpApn) {
4423                    apnContext.setReason(Phone.REASON_APN_CHANGED);
4424                    cleanUpConnection(true, apnContext);
4425                }
4426            }
4427        }
4428
4429        if (!isConnected()) {
4430            stopNetStatPoll();
4431            stopDataStallAlarm();
4432        }
4433
4434        mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
4435
4436        if (DBG) log("mDisconnectPendingCount = " + mDisconnectPendingCount);
4437        if (tearDown && mDisconnectPendingCount == 0) {
4438            notifyDataDisconnectComplete();
4439            notifyAllDataDisconnected();
4440        }
4441    }
4442
4443    /**
4444     * Polling stuff
4445     */
4446    private void resetPollStats() {
4447        mTxPkts = -1;
4448        mRxPkts = -1;
4449        mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
4450    }
4451
4452    private void startNetStatPoll() {
4453        if (getOverallState() == DctConstants.State.CONNECTED
4454                && mNetStatPollEnabled == false) {
4455            if (DBG) {
4456                log("startNetStatPoll");
4457            }
4458            resetPollStats();
4459            mNetStatPollEnabled = true;
4460            mPollNetStat.run();
4461        }
4462        if (mPhone != null) {
4463            mPhone.notifyDataActivity();
4464        }
4465    }
4466
4467    private void stopNetStatPoll() {
4468        mNetStatPollEnabled = false;
4469        removeCallbacks(mPollNetStat);
4470        if (DBG) {
4471            log("stopNetStatPoll");
4472        }
4473
4474        // To sync data activity icon in the case of switching data connection to send MMS.
4475        if (mPhone != null) {
4476            mPhone.notifyDataActivity();
4477        }
4478    }
4479
4480    public void sendStartNetStatPoll(DctConstants.Activity activity) {
4481        Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL);
4482        msg.arg1 = DctConstants.ENABLED;
4483        msg.obj = activity;
4484        sendMessage(msg);
4485    }
4486
4487    private void handleStartNetStatPoll(DctConstants.Activity activity) {
4488        startNetStatPoll();
4489        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
4490        setActivity(activity);
4491    }
4492
4493    public void sendStopNetStatPoll(DctConstants.Activity activity) {
4494        Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL);
4495        msg.arg1 = DctConstants.DISABLED;
4496        msg.obj = activity;
4497        sendMessage(msg);
4498    }
4499
4500    private void handleStopNetStatPoll(DctConstants.Activity activity) {
4501        stopNetStatPoll();
4502        stopDataStallAlarm();
4503        setActivity(activity);
4504    }
4505
4506    private void updateDataActivity() {
4507        long sent, received;
4508
4509        DctConstants.Activity newActivity;
4510
4511        TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
4512        TxRxSum curTxRxSum = new TxRxSum();
4513        curTxRxSum.updateTxRxSum();
4514        mTxPkts = curTxRxSum.txPkts;
4515        mRxPkts = curTxRxSum.rxPkts;
4516
4517        if (VDBG) {
4518            log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum);
4519        }
4520
4521        if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) {
4522            sent = mTxPkts - preTxRxSum.txPkts;
4523            received = mRxPkts - preTxRxSum.rxPkts;
4524
4525            if (VDBG)
4526                log("updateDataActivity: sent=" + sent + " received=" + received);
4527            if (sent > 0 && received > 0) {
4528                newActivity = DctConstants.Activity.DATAINANDOUT;
4529            } else if (sent > 0 && received == 0) {
4530                newActivity = DctConstants.Activity.DATAOUT;
4531            } else if (sent == 0 && received > 0) {
4532                newActivity = DctConstants.Activity.DATAIN;
4533            } else {
4534                newActivity = (mActivity == DctConstants.Activity.DORMANT) ?
4535                        mActivity : DctConstants.Activity.NONE;
4536            }
4537
4538            if (mActivity != newActivity && mIsScreenOn) {
4539                if (VDBG)
4540                    log("updateDataActivity: newActivity=" + newActivity);
4541                mActivity = newActivity;
4542                mPhone.notifyDataActivity();
4543            }
4544        }
4545    }
4546
4547    private void handlePcoData(AsyncResult ar) {
4548        if (ar.exception != null) {
4549            Rlog.e(LOG_TAG, "PCO_DATA exception: " + ar.exception);
4550            return;
4551        }
4552        PcoData pcoData = (PcoData)(ar.result);
4553        ArrayList<DataConnection> dcList = new ArrayList<>();
4554        DataConnection temp = mDcc.getActiveDcByCid(pcoData.cid);
4555        if (temp != null) {
4556            dcList.add(temp);
4557        }
4558        if (dcList.size() == 0) {
4559            Rlog.e(LOG_TAG, "PCO_DATA for unknown cid: " + pcoData.cid + ", inferring");
4560            for (DataConnection dc : mDataConnections.values()) {
4561                final int cid = dc.getCid();
4562                if (cid == pcoData.cid) {
4563                    if (VDBG) Rlog.d(LOG_TAG, "  found " + dc);
4564                    dcList.clear();
4565                    dcList.add(dc);
4566                    break;
4567                }
4568                // check if this dc is still connecting
4569                if (cid == -1) {
4570                    for (ApnContext apnContext : dc.mApnContexts.keySet()) {
4571                        if (apnContext.getState() == DctConstants.State.CONNECTING) {
4572                            if (VDBG) Rlog.d(LOG_TAG, "  found potential " + dc);
4573                            dcList.add(dc);
4574                            break;
4575                        }
4576                    }
4577                }
4578            }
4579        }
4580        if (dcList.size() == 0) {
4581            Rlog.e(LOG_TAG, "PCO_DATA - couldn't infer cid");
4582            return;
4583        }
4584        for (DataConnection dc : dcList) {
4585            if (dc.mApnContexts.size() == 0) {
4586                break;
4587            }
4588            // send one out for each apn type in play
4589            for (ApnContext apnContext : dc.mApnContexts.keySet()) {
4590                String apnType = apnContext.getApnType();
4591
4592                final Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE);
4593                intent.putExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY, apnType);
4594                intent.putExtra(TelephonyIntents.EXTRA_APN_PROTO_KEY, pcoData.bearerProto);
4595                intent.putExtra(TelephonyIntents.EXTRA_PCO_ID_KEY, pcoData.pcoId);
4596                intent.putExtra(TelephonyIntents.EXTRA_PCO_VALUE_KEY, pcoData.contents);
4597                mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
4598            }
4599        }
4600    }
4601
4602    /**
4603     * Data-Stall
4604     */
4605    // Recovery action taken in case of data stall
4606    private static class RecoveryAction {
4607        public static final int GET_DATA_CALL_LIST      = 0;
4608        public static final int CLEANUP                 = 1;
4609        public static final int REREGISTER              = 2;
4610        public static final int RADIO_RESTART           = 3;
4611        public static final int RADIO_RESTART_WITH_PROP = 4;
4612
4613        private static boolean isAggressiveRecovery(int value) {
4614            return ((value == RecoveryAction.CLEANUP) ||
4615                    (value == RecoveryAction.REREGISTER) ||
4616                    (value == RecoveryAction.RADIO_RESTART) ||
4617                    (value == RecoveryAction.RADIO_RESTART_WITH_PROP));
4618        }
4619    }
4620
4621    private int getRecoveryAction() {
4622        int action = Settings.System.getInt(mResolver,
4623                "radio.data.stall.recovery.action", RecoveryAction.GET_DATA_CALL_LIST);
4624        if (VDBG_STALL) log("getRecoveryAction: " + action);
4625        return action;
4626    }
4627
4628    private void putRecoveryAction(int action) {
4629        Settings.System.putInt(mResolver, "radio.data.stall.recovery.action", action);
4630        if (VDBG_STALL) log("putRecoveryAction: " + action);
4631    }
4632
4633    private void doRecovery() {
4634        if (getOverallState() == DctConstants.State.CONNECTED) {
4635            // Go through a series of recovery steps, each action transitions to the next action
4636            final int recoveryAction = getRecoveryAction();
4637            TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
4638            switch (recoveryAction) {
4639            case RecoveryAction.GET_DATA_CALL_LIST:
4640                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
4641                        mSentSinceLastRecv);
4642                if (DBG) log("doRecovery() get data call list");
4643                mPhone.mCi.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
4644                putRecoveryAction(RecoveryAction.CLEANUP);
4645                break;
4646            case RecoveryAction.CLEANUP:
4647                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv);
4648                if (DBG) log("doRecovery() cleanup all connections");
4649                cleanUpAllConnections(Phone.REASON_PDP_RESET);
4650                putRecoveryAction(RecoveryAction.REREGISTER);
4651                break;
4652            case RecoveryAction.REREGISTER:
4653                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
4654                        mSentSinceLastRecv);
4655                if (DBG) log("doRecovery() re-register");
4656                mPhone.getServiceStateTracker().reRegisterNetwork(null);
4657                putRecoveryAction(RecoveryAction.RADIO_RESTART);
4658                break;
4659            case RecoveryAction.RADIO_RESTART:
4660                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
4661                        mSentSinceLastRecv);
4662                if (DBG) log("restarting radio");
4663                putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
4664                restartRadio();
4665                break;
4666            case RecoveryAction.RADIO_RESTART_WITH_PROP:
4667                // This is in case radio restart has not recovered the data.
4668                // It will set an additional "gsm.radioreset" property to tell
4669                // RIL or system to take further action.
4670                // The implementation of hard reset recovery action is up to OEM product.
4671                // Once RADIO_RESET property is consumed, it is expected to set back
4672                // to false by RIL.
4673                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1);
4674                if (DBG) log("restarting radio with gsm.radioreset to true");
4675                SystemProperties.set(RADIO_RESET_PROPERTY, "true");
4676                // give 1 sec so property change can be notified.
4677                try {
4678                    Thread.sleep(1000);
4679                } catch (InterruptedException e) {}
4680                restartRadio();
4681                putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
4682                break;
4683            default:
4684                throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
4685                    recoveryAction);
4686            }
4687            mSentSinceLastRecv = 0;
4688        }
4689    }
4690
4691    private void updateDataStallInfo() {
4692        long sent, received;
4693
4694        TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
4695        mDataStallTxRxSum.updateTxRxSum();
4696
4697        if (VDBG_STALL) {
4698            log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
4699                    " preTxRxSum=" + preTxRxSum);
4700        }
4701
4702        sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
4703        received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
4704
4705        if (RADIO_TESTS) {
4706            if (SystemProperties.getBoolean("radio.test.data.stall", false)) {
4707                log("updateDataStallInfo: radio.test.data.stall true received = 0;");
4708                received = 0;
4709            }
4710        }
4711        if ( sent > 0 && received > 0 ) {
4712            if (VDBG_STALL) log("updateDataStallInfo: IN/OUT");
4713            mSentSinceLastRecv = 0;
4714            putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
4715        } else if (sent > 0 && received == 0) {
4716            if (mPhone.getState() == PhoneConstants.State.IDLE) {
4717                mSentSinceLastRecv += sent;
4718            } else {
4719                mSentSinceLastRecv = 0;
4720            }
4721            if (DBG) {
4722                log("updateDataStallInfo: OUT sent=" + sent +
4723                        " mSentSinceLastRecv=" + mSentSinceLastRecv);
4724            }
4725        } else if (sent == 0 && received > 0) {
4726            if (VDBG_STALL) log("updateDataStallInfo: IN");
4727            mSentSinceLastRecv = 0;
4728            putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
4729        } else {
4730            if (VDBG_STALL) log("updateDataStallInfo: NONE");
4731        }
4732    }
4733
4734    private void onDataStallAlarm(int tag) {
4735        if (mDataStallAlarmTag != tag) {
4736            if (DBG) {
4737                log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag);
4738            }
4739            return;
4740        }
4741        updateDataStallInfo();
4742
4743        int hangWatchdogTrigger = Settings.Global.getInt(mResolver,
4744                Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
4745                NUMBER_SENT_PACKETS_OF_HANG);
4746
4747        boolean suspectedStall = DATA_STALL_NOT_SUSPECTED;
4748        if (mSentSinceLastRecv >= hangWatchdogTrigger) {
4749            if (DBG) {
4750                log("onDataStallAlarm: tag=" + tag + " do recovery action=" + getRecoveryAction());
4751            }
4752            suspectedStall = DATA_STALL_SUSPECTED;
4753            sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
4754        } else {
4755            if (VDBG_STALL) {
4756                log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) +
4757                    " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger);
4758            }
4759        }
4760        startDataStallAlarm(suspectedStall);
4761    }
4762
4763    private void startDataStallAlarm(boolean suspectedStall) {
4764        int nextAction = getRecoveryAction();
4765        int delayInMs;
4766
4767        if (mDataStallDetectionEnabled && getOverallState() == DctConstants.State.CONNECTED) {
4768            // If screen is on or data stall is currently suspected, set the alarm
4769            // with an aggressive timeout.
4770            if (mIsScreenOn || suspectedStall || RecoveryAction.isAggressiveRecovery(nextAction)) {
4771                delayInMs = Settings.Global.getInt(mResolver,
4772                        Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
4773                        DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
4774            } else {
4775                delayInMs = Settings.Global.getInt(mResolver,
4776                        Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
4777                        DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
4778            }
4779
4780            mDataStallAlarmTag += 1;
4781            if (VDBG_STALL) {
4782                log("startDataStallAlarm: tag=" + mDataStallAlarmTag +
4783                        " delay=" + (delayInMs / 1000) + "s");
4784            }
4785            Intent intent = new Intent(INTENT_DATA_STALL_ALARM);
4786            intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag);
4787            mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
4788                    PendingIntent.FLAG_UPDATE_CURRENT);
4789            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
4790                    SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
4791        } else {
4792            if (VDBG_STALL) {
4793                log("startDataStallAlarm: NOT started, no connection tag=" + mDataStallAlarmTag);
4794            }
4795        }
4796    }
4797
4798    private void stopDataStallAlarm() {
4799        if (VDBG_STALL) {
4800            log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag +
4801                    " mDataStallAlarmIntent=" + mDataStallAlarmIntent);
4802        }
4803        mDataStallAlarmTag += 1;
4804        if (mDataStallAlarmIntent != null) {
4805            mAlarmManager.cancel(mDataStallAlarmIntent);
4806            mDataStallAlarmIntent = null;
4807        }
4808    }
4809
4810    private void restartDataStallAlarm() {
4811        if (isConnected() == false) return;
4812        // To be called on screen status change.
4813        // Do not cancel the alarm if it is set with aggressive timeout.
4814        int nextAction = getRecoveryAction();
4815
4816        if (RecoveryAction.isAggressiveRecovery(nextAction)) {
4817            if (DBG) log("restartDataStallAlarm: action is pending. not resetting the alarm.");
4818            return;
4819        }
4820        if (VDBG_STALL) log("restartDataStallAlarm: stop then start.");
4821        stopDataStallAlarm();
4822        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
4823    }
4824
4825    /**
4826     * Provisioning APN
4827     */
4828    private void onActionIntentProvisioningApnAlarm(Intent intent) {
4829        if (DBG) log("onActionIntentProvisioningApnAlarm: action=" + intent.getAction());
4830        Message msg = obtainMessage(DctConstants.EVENT_PROVISIONING_APN_ALARM,
4831                intent.getAction());
4832        msg.arg1 = intent.getIntExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, 0);
4833        sendMessage(msg);
4834    }
4835
4836    private void startProvisioningApnAlarm() {
4837        int delayInMs = Settings.Global.getInt(mResolver,
4838                                Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
4839                                PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT);
4840        if (Build.IS_DEBUGGABLE) {
4841            // Allow debug code to use a system property to provide another value
4842            String delayInMsStrg = Integer.toString(delayInMs);
4843            delayInMsStrg = System.getProperty(DEBUG_PROV_APN_ALARM, delayInMsStrg);
4844            try {
4845                delayInMs = Integer.parseInt(delayInMsStrg);
4846            } catch (NumberFormatException e) {
4847                loge("startProvisioningApnAlarm: e=" + e);
4848            }
4849        }
4850        mProvisioningApnAlarmTag += 1;
4851        if (DBG) {
4852            log("startProvisioningApnAlarm: tag=" + mProvisioningApnAlarmTag +
4853                    " delay=" + (delayInMs / 1000) + "s");
4854        }
4855        Intent intent = new Intent(INTENT_PROVISIONING_APN_ALARM);
4856        intent.putExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, mProvisioningApnAlarmTag);
4857        mProvisioningApnAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
4858                PendingIntent.FLAG_UPDATE_CURRENT);
4859        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
4860                SystemClock.elapsedRealtime() + delayInMs, mProvisioningApnAlarmIntent);
4861    }
4862
4863    private void stopProvisioningApnAlarm() {
4864        if (DBG) {
4865            log("stopProvisioningApnAlarm: current tag=" + mProvisioningApnAlarmTag +
4866                    " mProvsioningApnAlarmIntent=" + mProvisioningApnAlarmIntent);
4867        }
4868        mProvisioningApnAlarmTag += 1;
4869        if (mProvisioningApnAlarmIntent != null) {
4870            mAlarmManager.cancel(mProvisioningApnAlarmIntent);
4871            mProvisioningApnAlarmIntent = null;
4872        }
4873    }
4874
4875}
4876