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