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