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