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