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