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