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