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