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