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