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