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