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