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