DcTracker.java revision fb2e6abdeb0a84a9f4bd97f1411d45491c7eb59f
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.res.Resources;
30import android.database.ContentObserver;
31import android.database.Cursor;
32import android.net.ConnectivityManager;
33import android.net.LinkProperties;
34import android.net.NetworkCapabilities;
35import android.net.NetworkConfig;
36import android.net.NetworkUtils;
37import android.net.ProxyInfo;
38import android.net.Uri;
39import android.os.AsyncResult;
40import android.os.Build;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.Message;
44import android.os.RegistrantList;
45import android.os.ServiceManager;
46import android.os.SystemClock;
47import android.os.SystemProperties;
48import android.os.UserHandle;
49import android.provider.Settings;
50import android.provider.Telephony;
51import android.telephony.CellLocation;
52import android.telephony.ServiceState;
53import android.telephony.TelephonyManager;
54import android.telephony.SubscriptionManager;
55import android.telephony.cdma.CdmaCellLocation;
56import android.telephony.gsm.GsmCellLocation;
57import android.text.TextUtils;
58import android.util.EventLog;
59import android.util.LocalLog;
60import android.util.Pair;
61import android.view.WindowManager;
62import android.telephony.Rlog;
63
64import com.android.internal.telephony.cdma.CDMALTEPhone;
65import com.android.internal.telephony.Phone;
66import com.android.internal.telephony.PhoneBase;
67import com.android.internal.telephony.DctConstants;
68import com.android.internal.telephony.EventLogTags;
69import com.android.internal.telephony.ITelephony;
70import com.android.internal.telephony.TelephonyIntents;
71import com.android.internal.telephony.gsm.GSMPhone;
72import com.android.internal.telephony.PhoneConstants;
73import com.android.internal.telephony.RILConstants;
74import com.android.internal.telephony.uicc.IccRecords;
75import com.android.internal.telephony.uicc.UiccController;
76import com.android.internal.util.AsyncChannel;
77import com.android.internal.util.ArrayUtils;
78
79import java.io.FileDescriptor;
80import java.io.PrintWriter;
81import java.util.ArrayList;
82import java.util.Arrays;
83import java.util.concurrent.atomic.AtomicBoolean;
84import java.util.Objects;
85import java.lang.StringBuilder;
86
87import com.android.internal.telephony.ServiceStateTracker;
88/**
89 * {@hide}
90 */
91public final class DcTracker extends DcTrackerBase {
92    protected final String LOG_TAG = "DCT";
93
94    /**
95     * List of messages that are waiting to be posted, when data call disconnect
96     * is complete
97     */
98    private ArrayList<Message> mDisconnectAllCompleteMsgList = new ArrayList<Message>();
99
100    private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
101
102    protected int mDisconnectPendingCount = 0;
103
104    /**
105     * Handles changes to the APN db.
106     */
107    private class ApnChangeObserver extends ContentObserver {
108        public ApnChangeObserver () {
109            super(mDataConnectionTracker);
110        }
111
112        @Override
113        public void onChange(boolean selfChange) {
114            sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
115        }
116    }
117
118    //***** Instance Variables
119
120    private boolean mReregisterOnReconnectFailure = false;
121
122
123    //***** Constants
124
125    // Used by puppetmaster/*/radio_stress.py
126    private static final String PUPPET_MASTER_RADIO_STRESS_TEST = "gsm.defaultpdpcontext.active";
127
128    private static final int POLL_PDP_MILLIS = 5 * 1000;
129
130    private static final int PROVISIONING_SPINNER_TIMEOUT_MILLIS = 120 * 1000;
131
132    static final Uri PREFERAPN_NO_UPDATE_URI_USING_SUBID =
133                        Uri.parse("content://telephony/carriers/preferapn_no_update/subId/");
134    static final String APN_ID = "apn_id";
135
136    private boolean mCanSetPreferApn = false;
137
138    private AtomicBoolean mAttached = new AtomicBoolean(false);
139
140    /** Watches for changes to the APN db. */
141    private ApnChangeObserver mApnObserver;
142
143    private final String mProvisionActionName;
144    private BroadcastReceiver mProvisionBroadcastReceiver;
145    private ProgressDialog mProvisioningSpinner;
146
147    public boolean mImsRegistrationState = false;
148    private ApnContext mWaitCleanUpApnContext = null;
149    private boolean mDeregistrationAlarmState = false;
150    private PendingIntent mImsDeregistrationDelayIntent = null;
151
152    //***** Constructor
153    public DcTracker(PhoneBase p) {
154        super(p);
155        if (DBG) log("GsmDCT.constructor");
156
157        mDataConnectionTracker = this;
158        update();
159        mApnObserver = new ApnChangeObserver();
160        p.getContext().getContentResolver().registerContentObserver(
161                Telephony.Carriers.CONTENT_URI, true, mApnObserver);
162
163        initApnContexts();
164
165        for (ApnContext apnContext : mApnContexts.values()) {
166            // Register the reconnect and restart actions.
167            IntentFilter filter = new IntentFilter();
168            filter.addAction(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
169            filter.addAction(INTENT_RESTART_TRYSETUP_ALARM + '.' + apnContext.getApnType());
170            mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
171        }
172
173        // Add Emergency APN to APN setting list by default to support EPDN in sim absent cases
174        initEmergencyApnSetting();
175        addEmergencyApnSetting();
176
177        mProvisionActionName = "com.android.internal.telephony.PROVISION" + p.getPhoneId();
178    }
179
180    protected void registerForAllEvents() {
181        mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
182        mPhone.mCi.registerForOffOrNotAvailable(this,
183               DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
184        mPhone.mCi.registerForDataNetworkStateChanged(this,
185               DctConstants.EVENT_DATA_STATE_CHANGED, null);
186        // Note, this is fragile - the Phone is now presenting a merged picture
187        // of PS (volte) & CS and by diving into its internals you're just seeing
188        // the CS data.  This works well for the purposes this is currently used for
189        // but that may not always be the case.  Should probably be redesigned to
190        // accurately reflect what we're really interested in (registerForCSVoiceCallEnded).
191        mPhone.getCallTracker().registerForVoiceCallEnded (this,
192               DctConstants.EVENT_VOICE_CALL_ENDED, null);
193        mPhone.getCallTracker().registerForVoiceCallStarted (this,
194               DctConstants.EVENT_VOICE_CALL_STARTED, null);
195        mPhone.getServiceStateTracker().registerForDataConnectionAttached(this,
196               DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
197        mPhone.getServiceStateTracker().registerForDataConnectionDetached(this,
198               DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
199        mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
200               DctConstants.EVENT_ROAMING_ON, null);
201        mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
202               DctConstants.EVENT_ROAMING_OFF, null);
203        mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
204                DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
205        mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
206                DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
207     //   SubscriptionManager.registerForDdsSwitch(this,
208     //          DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
209        mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(this,
210                DctConstants.EVENT_DATA_RAT_CHANGED, null);
211    }
212    @Override
213    public void dispose() {
214        if (DBG) log("DcTracker.dispose");
215
216        if (mProvisionBroadcastReceiver != null) {
217            mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
218            mProvisionBroadcastReceiver = null;
219        }
220        if (mProvisioningSpinner != null) {
221            mProvisioningSpinner.dismiss();
222            mProvisioningSpinner = null;
223        }
224
225        cleanUpAllConnections(true, null);
226
227        super.dispose();
228
229        mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
230        mApnContexts.clear();
231        mPrioritySortedApnContexts.clear();
232
233        destroyDataConnections();
234    }
235    protected void unregisterForAllEvents() {
236         //Unregister for all events
237        mPhone.mCi.unregisterForAvailable(this);
238        mPhone.mCi.unregisterForOffOrNotAvailable(this);
239        IccRecords r = mIccRecords.get();
240        if (r != null) {
241            r.unregisterForRecordsLoaded(this);
242            mIccRecords.set(null);
243        }
244        mPhone.mCi.unregisterForDataNetworkStateChanged(this);
245        mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
246        mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
247        mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
248        mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
249        mPhone.getServiceStateTracker().unregisterForDataRoamingOn(this);
250        mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this);
251        mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
252        mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
253        //SubscriptionManager.unregisterForDdsSwitch(this);
254    }
255
256    @Override
257    public void incApnRefCount(String name, LocalLog log) {
258        ApnContext apnContext = mApnContexts.get(name);
259        log.log("DcTracker.incApnRefCount on " + name + " found " + apnContext);
260        if (apnContext != null) {
261            apnContext.incRefCount(log);
262        }
263    }
264
265    @Override
266    public void decApnRefCount(String name, LocalLog log) {
267        ApnContext apnContext = mApnContexts.get(name);
268        log.log("DcTracker.decApnRefCount on " + name + " found " + apnContext);
269        if (apnContext != null) {
270            apnContext.decRefCount(log);
271        }
272    }
273
274    @Override
275    public boolean isApnSupported(String name) {
276        if (name == null) {
277            loge("isApnSupported: name=null");
278            return false;
279        }
280        ApnContext apnContext = mApnContexts.get(name);
281        if (apnContext == null) {
282            loge("Request for unsupported mobile name: " + name);
283            return false;
284        }
285        return true;
286    }
287
288    @Override
289    public int getApnPriority(String name) {
290        ApnContext apnContext = mApnContexts.get(name);
291        if (apnContext == null) {
292            loge("Request for unsupported mobile name: " + name);
293        }
294        return apnContext.priority;
295    }
296
297    // Turn telephony radio on or off.
298    private void setRadio(boolean on) {
299        final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
300        try {
301            phone.setRadio(on);
302        } catch (Exception e) {
303            // Ignore.
304        }
305    }
306
307    // Class to handle Intent dispatched with user selects the "Sign-in to network"
308    // notification.
309    private class ProvisionNotificationBroadcastReceiver extends BroadcastReceiver {
310        private final String mNetworkOperator;
311        // Mobile provisioning URL.  Valid while provisioning notification is up.
312        // Set prior to notification being posted as URL contains ICCID which
313        // disappears when radio is off (which is the case when notification is up).
314        private final String mProvisionUrl;
315
316        public ProvisionNotificationBroadcastReceiver(String provisionUrl, String networkOperator) {
317            mNetworkOperator = networkOperator;
318            mProvisionUrl = provisionUrl;
319        }
320
321        private void setEnableFailFastMobileData(int enabled) {
322            sendMessage(obtainMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled, 0));
323        }
324
325        private void enableMobileProvisioning() {
326            final Message msg = obtainMessage(DctConstants.CMD_ENABLE_MOBILE_PROVISIONING);
327            msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl));
328            sendMessage(msg);
329        }
330
331        @Override
332        public void onReceive(Context context, Intent intent) {
333            // Turning back on the radio can take time on the order of a minute, so show user a
334            // spinner so they know something is going on.
335            mProvisioningSpinner = new ProgressDialog(context);
336            mProvisioningSpinner.setTitle(mNetworkOperator);
337            mProvisioningSpinner.setMessage(
338                    // TODO: Don't borrow "Connecting..." i18n string; give Telephony a version.
339                    context.getText(com.android.internal.R.string.media_route_status_connecting));
340            mProvisioningSpinner.setIndeterminate(true);
341            mProvisioningSpinner.setCancelable(true);
342            // Allow non-Activity Service Context to create a View.
343            mProvisioningSpinner.getWindow().setType(
344                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
345            mProvisioningSpinner.show();
346            // After timeout, hide spinner so user can at least use their device.
347            // TODO: Indicate to user that it is taking an unusually long time to connect?
348            sendMessageDelayed(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
349                    mProvisioningSpinner), PROVISIONING_SPINNER_TIMEOUT_MILLIS);
350            // This code is almost identical to the old
351            // ConnectivityService.handleMobileProvisioningAction code.
352            setRadio(true);
353            setEnableFailFastMobileData(DctConstants.ENABLED);
354            enableMobileProvisioning();
355        }
356    }
357
358    @Override
359    public boolean isApnTypeActive(String type) {
360        ApnContext apnContext = mApnContexts.get(type);
361        if (apnContext == null) return false;
362
363        return (apnContext.getDcAc() != null);
364    }
365
366    @Override
367    public boolean isDataPossible(String apnType) {
368        ApnContext apnContext = mApnContexts.get(apnType);
369        if (apnContext == null) {
370            return false;
371        }
372        boolean apnContextIsEnabled = apnContext.isEnabled();
373        DctConstants.State apnContextState = apnContext.getState();
374        boolean apnTypePossible = !(apnContextIsEnabled &&
375                (apnContextState == DctConstants.State.FAILED));
376        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
377        // Set the emergency APN availability status as TRUE irrespective of conditions checked in
378        // isDataAllowed() like IN_SERVICE, MOBILE DATA status etc.
379        boolean dataAllowed = isEmergencyApn || isDataAllowed();
380        boolean possible = dataAllowed && apnTypePossible;
381
382        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
383                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
384                && (mPhone.getServiceState().getRilDataRadioTechnology()
385                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
386            log("Default data call activation not possible in iwlan.");
387            possible = false;
388        }
389
390        if (VDBG) {
391            log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
392                    "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
393                    apnType, possible, dataAllowed, apnTypePossible,
394                    apnContextIsEnabled, apnContextState));
395        }
396        return possible;
397    }
398
399    @Override
400    protected void finalize() {
401        if(DBG) log("finalize");
402    }
403
404    private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
405        ApnContext apnContext = new ApnContext(mPhone.getContext(), type, LOG_TAG, networkConfig,
406                this);
407        mApnContexts.put(type, apnContext);
408        mPrioritySortedApnContexts.add(apnContext);
409        return apnContext;
410    }
411
412    protected void initApnContexts() {
413        log("initApnContexts: E");
414        // Load device network attributes from resources
415        String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
416                com.android.internal.R.array.networkAttributes);
417        for (String networkConfigString : networkConfigStrings) {
418            NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
419            ApnContext apnContext = null;
420
421            switch (networkConfig.type) {
422            case ConnectivityManager.TYPE_MOBILE:
423                apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT, networkConfig);
424                break;
425            case ConnectivityManager.TYPE_MOBILE_MMS:
426                apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS, networkConfig);
427                break;
428            case ConnectivityManager.TYPE_MOBILE_SUPL:
429                apnContext = addApnContext(PhoneConstants.APN_TYPE_SUPL, networkConfig);
430                break;
431            case ConnectivityManager.TYPE_MOBILE_DUN:
432                apnContext = addApnContext(PhoneConstants.APN_TYPE_DUN, networkConfig);
433                break;
434            case ConnectivityManager.TYPE_MOBILE_HIPRI:
435                apnContext = addApnContext(PhoneConstants.APN_TYPE_HIPRI, networkConfig);
436                break;
437            case ConnectivityManager.TYPE_MOBILE_FOTA:
438                apnContext = addApnContext(PhoneConstants.APN_TYPE_FOTA, networkConfig);
439                break;
440            case ConnectivityManager.TYPE_MOBILE_IMS:
441                apnContext = addApnContext(PhoneConstants.APN_TYPE_IMS, networkConfig);
442                break;
443            case ConnectivityManager.TYPE_MOBILE_CBS:
444                apnContext = addApnContext(PhoneConstants.APN_TYPE_CBS, networkConfig);
445                break;
446            case ConnectivityManager.TYPE_MOBILE_IA:
447                apnContext = addApnContext(PhoneConstants.APN_TYPE_IA, networkConfig);
448                break;
449            case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
450                apnContext = addApnContext(PhoneConstants.APN_TYPE_EMERGENCY, networkConfig);
451                break;
452            default:
453                log("initApnContexts: skipping unknown type=" + networkConfig.type);
454                continue;
455            }
456            log("initApnContexts: apnContext=" + apnContext);
457        }
458        log("initApnContexts: X mApnContexts=" + mApnContexts);
459    }
460
461    @Override
462    public LinkProperties getLinkProperties(String apnType) {
463        ApnContext apnContext = mApnContexts.get(apnType);
464        if (apnContext != null) {
465            DcAsyncChannel dcac = apnContext.getDcAc();
466            if (dcac != null) {
467                if (DBG) log("return link properites for " + apnType);
468                return dcac.getLinkPropertiesSync();
469            }
470        }
471        if (DBG) log("return new LinkProperties");
472        return new LinkProperties();
473    }
474
475    @Override
476    public NetworkCapabilities getNetworkCapabilities(String apnType) {
477        ApnContext apnContext = mApnContexts.get(apnType);
478        if (apnContext!=null) {
479            DcAsyncChannel dataConnectionAc = apnContext.getDcAc();
480            if (dataConnectionAc != null) {
481                if (DBG) {
482                    log("get active pdp is not null, return NetworkCapabilities for " + apnType);
483                }
484                return dataConnectionAc.getNetworkCapabilitiesSync();
485            }
486        }
487        if (DBG) log("return new NetworkCapabilities");
488        return new NetworkCapabilities();
489    }
490
491    @Override
492    // Return all active apn types
493    public String[] getActiveApnTypes() {
494        if (DBG) log("get all active apn types");
495        ArrayList<String> result = new ArrayList<String>();
496
497        for (ApnContext apnContext : mApnContexts.values()) {
498            if (mAttached.get() && apnContext.isReady()) {
499                result.add(apnContext.getApnType());
500            }
501        }
502
503        return result.toArray(new String[0]);
504    }
505
506    @Override
507    // Return active apn of specific apn type
508    public String getActiveApnString(String apnType) {
509        if (VDBG) log( "get active apn string for type:" + apnType);
510        ApnContext apnContext = mApnContexts.get(apnType);
511        if (apnContext != null) {
512            ApnSetting apnSetting = apnContext.getApnSetting();
513            if (apnSetting != null) {
514                return apnSetting.apn;
515            }
516        }
517        return null;
518    }
519
520    @Override
521    public boolean isApnTypeEnabled(String apnType) {
522        ApnContext apnContext = mApnContexts.get(apnType);
523        if (apnContext == null) {
524            return false;
525        }
526        return apnContext.isEnabled();
527    }
528
529    @Override
530    protected void setState(DctConstants.State s) {
531        if (DBG) log("setState should not be used in GSM" + s);
532    }
533
534    // Return state of specific apn type
535    @Override
536    public DctConstants.State getState(String apnType) {
537        ApnContext apnContext = mApnContexts.get(apnType);
538        if (apnContext != null) {
539            return apnContext.getState();
540        }
541        return DctConstants.State.FAILED;
542    }
543
544    // Return if apn type is a provisioning apn.
545    @Override
546    protected boolean isProvisioningApn(String apnType) {
547        ApnContext apnContext = mApnContexts.get(apnType);
548        if (apnContext != null) {
549            return apnContext.isProvisioningApn();
550        }
551        return false;
552    }
553
554    // Return state of overall
555    @Override
556    public DctConstants.State getOverallState() {
557        boolean isConnecting = false;
558        boolean isFailed = true; // All enabled Apns should be FAILED.
559        boolean isAnyEnabled = false;
560
561        for (ApnContext apnContext : mApnContexts.values()) {
562            if (apnContext.isEnabled()) {
563                isAnyEnabled = true;
564                switch (apnContext.getState()) {
565                case CONNECTED:
566                case DISCONNECTING:
567                    if (DBG) log("overall state is CONNECTED");
568                    return DctConstants.State.CONNECTED;
569                case RETRYING:
570                case CONNECTING:
571                    isConnecting = true;
572                    isFailed = false;
573                    break;
574                case IDLE:
575                case SCANNING:
576                    isFailed = false;
577                    break;
578                default:
579                    isAnyEnabled = true;
580                    break;
581                }
582            }
583        }
584
585        if (!isAnyEnabled) { // Nothing enabled. return IDLE.
586            if (DBG) log( "overall state is IDLE");
587            return DctConstants.State.IDLE;
588        }
589
590        if (isConnecting) {
591            if (DBG) log( "overall state is CONNECTING");
592            return DctConstants.State.CONNECTING;
593        } else if (!isFailed) {
594            if (DBG) log( "overall state is IDLE");
595            return DctConstants.State.IDLE;
596        } else {
597            if (DBG) log( "overall state is FAILED");
598            return DctConstants.State.FAILED;
599        }
600    }
601
602    @Override
603    protected boolean isApnTypeAvailable(String type) {
604        if (type.equals(PhoneConstants.APN_TYPE_DUN) && fetchDunApn() != null) {
605            return true;
606        }
607
608        if (mAllApnSettings != null) {
609            for (ApnSetting apn : mAllApnSettings) {
610                if (apn.canHandleType(type)) {
611                    return true;
612                }
613            }
614        }
615        return false;
616    }
617
618    /**
619     * Report on whether data connectivity is enabled for any APN.
620     * @return {@code false} if data connectivity has been explicitly disabled,
621     * {@code true} otherwise.
622     */
623    @Override
624    public boolean getAnyDataEnabled() {
625        synchronized (mDataEnabledLock) {
626            if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false;
627            for (ApnContext apnContext : mApnContexts.values()) {
628                // Make sure we don't have a context that is going down
629                // and is explicitly disabled.
630                if (isDataAllowed(apnContext)) {
631                    return true;
632                }
633            }
634            return false;
635        }
636    }
637
638    public boolean getAnyDataEnabled(boolean checkUserDataEnabled) {
639        synchronized (mDataEnabledLock) {
640            if (!(mInternalDataEnabled && (!checkUserDataEnabled || mUserDataEnabled)
641                        && (!checkUserDataEnabled || sPolicyDataEnabled)))
642                return false;
643
644            for (ApnContext apnContext : mApnContexts.values()) {
645                // Make sure we dont have a context that going down
646                // and is explicitly disabled.
647                if (isDataAllowed(apnContext)) {
648                    return true;
649                }
650            }
651            return false;
652        }
653    }
654
655    private boolean isDataAllowed(ApnContext apnContext) {
656        //If RAT is iwlan then dont allow default/IA PDP at all.
657        //Rest of APN types can be evaluated for remaining conditions.
658        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
659                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
660                && (mPhone.getServiceState().getRilDataRadioTechnology()
661                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
662            log("Default data call activation not allowed in iwlan.");
663            return false;
664        } else {
665            return apnContext.isReady() && isDataAllowed();
666        }
667    }
668
669    //****** Called from ServiceStateTracker
670    /**
671     * Invoked when ServiceStateTracker observes a transition from GPRS
672     * attach to detach.
673     */
674    protected void onDataConnectionDetached() {
675        /*
676         * We presently believe it is unnecessary to tear down the PDP context
677         * when GPRS detaches, but we should stop the network polling.
678         */
679        if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
680        stopNetStatPoll();
681        stopDataStallAlarm();
682        notifyDataConnection(Phone.REASON_DATA_DETACHED);
683        mAttached.set(false);
684    }
685
686    private void onDataConnectionAttached() {
687        if (DBG) log("onDataConnectionAttached");
688        mAttached.set(true);
689        if (getOverallState() == DctConstants.State.CONNECTED) {
690            if (DBG) log("onDataConnectionAttached: start polling notify attached");
691            startNetStatPoll();
692            startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
693            notifyDataConnection(Phone.REASON_DATA_ATTACHED);
694        } else {
695            // update APN availability so that APN can be enabled.
696            notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
697        }
698        if (mAutoAttachOnCreationConfig) {
699            mAutoAttachOnCreation.set(true);
700        }
701        setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
702    }
703
704    @Override
705    protected boolean isDataAllowed() {
706        final boolean internalDataEnabled;
707        synchronized (mDataEnabledLock) {
708            internalDataEnabled = mInternalDataEnabled;
709        }
710
711        boolean attachedState = mAttached.get();
712        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
713        int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
714        if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
715            desiredPowerState = true;
716        }
717
718        IccRecords r = mIccRecords.get();
719        boolean recordsLoaded = false;
720        if (r != null) {
721            recordsLoaded = r.getRecordsLoaded();
722            if (DBG && !recordsLoaded) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
723        }
724
725        //FIXME always attach
726        boolean psRestricted = mIsPsRestricted;
727        int phoneNum = TelephonyManager.getDefault().getPhoneCount();
728        if (phoneNum > 1) {
729            attachedState = true;
730            psRestricted = false;
731        }
732        int dataSub = SubscriptionManager.getDefaultDataSubId();
733        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(dataSub);
734        PhoneConstants.State state = PhoneConstants.State.IDLE;
735        // Note this is explicitly not using mPhone.getState.  See b/19090488.
736        // mPhone.getState reports the merge of CS and PS (volte) voice call state
737        // but we only care about CS calls here for data/voice concurrency issues.
738        // Calling getCallTracker currently gives you just the CS side where the
739        // ImsCallTracker is held internally where applicable.
740        // This should be redesigned to ask explicitly what we want:
741        // voiceCallStateAllowDataCall, or dataCallAllowed or something similar.
742        if (mPhone.getCallTracker() != null) {
743            state = mPhone.getCallTracker().getState();
744        }
745        boolean allowed =
746                    (attachedState || mAutoAttachOnCreation.get()) &&
747                    recordsLoaded &&
748                    (state == PhoneConstants.State.IDLE ||
749                     mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
750                    internalDataEnabled &&
751                    defaultDataSelected &&
752                    (!mPhone.getServiceState().getDataRoaming() || getDataOnRoamingEnabled()) &&
753                    //!mIsPsRestricted &&
754                    !psRestricted &&
755                    desiredPowerState;
756        if (!allowed && DBG) {
757            String reason = "";
758            if (!(attachedState || mAutoAttachOnCreation.get())) {
759                reason += " - Attached= " + attachedState;
760            }
761            if (!recordsLoaded) reason += " - SIM not loaded";
762            if (state != PhoneConstants.State.IDLE &&
763                    !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
764                reason += " - PhoneState= " + state;
765                reason += " - Concurrent voice and data not allowed";
766            }
767            if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
768            if (!defaultDataSelected) reason += " - defaultDataSelected= false";
769            if (mPhone.getServiceState().getDataRoaming() && !getDataOnRoamingEnabled()) {
770                reason += " - Roaming and data roaming not enabled";
771            }
772            if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
773            if (!desiredPowerState) reason += " - desiredPowerState= false";
774            if (DBG) log("isDataAllowed: not allowed due to" + reason);
775        }
776        return allowed;
777    }
778
779    // arg for setupDataOnConnectableApns
780    private enum RetryFailures {
781        // retry failed networks always (the old default)
782        ALWAYS,
783        // retry only when a substantial change has occured.  Either:
784        // 1) we were restricted by voice/data concurrency and aren't anymore
785        // 2) our apn list has change
786        ONLY_ON_CHANGE
787    };
788
789    private void setupDataOnConnectableApns(String reason) {
790        setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
791    }
792
793    private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
794        if (DBG) log("setupDataOnConnectableApns: " + reason);
795
796        for (ApnContext apnContext : mPrioritySortedApnContexts) {
797            ArrayList<ApnSetting> waitingApns = null;
798
799            if (DBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
800            if (apnContext.getState() == DctConstants.State.FAILED
801                    || apnContext.getState() == DctConstants.State.RETRYING) {
802                if (retryFailures == RetryFailures.ALWAYS) {
803                    apnContext.releaseDataConnection(reason);
804                } else if (apnContext.isConcurrentVoiceAndDataAllowed() == false &&
805                         mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
806                    // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
807                    apnContext.releaseDataConnection(reason);
808                } else {
809                    // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed
810                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
811                    ArrayList<ApnSetting> originalApns = apnContext.getOriginalWaitingApns();
812                    if (originalApns != null && originalApns.isEmpty() == false) {
813                        waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
814                        if (originalApns.size() != waitingApns.size() ||
815                                originalApns.containsAll(waitingApns) == false) {
816                            apnContext.releaseDataConnection(reason);
817                        }
818                    }
819                }
820            }
821            if (apnContext.isConnectable()) {
822                log("setupDataOnConnectableApns: isConnectable() call trySetupData");
823                apnContext.setReason(reason);
824                trySetupData(apnContext, waitingApns);
825            }
826        }
827    }
828
829    private boolean trySetupData(ApnContext apnContext) {
830        return trySetupData(apnContext, null);
831    }
832
833    private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns) {
834        if (DBG) {
835            log("trySetupData for type:" + apnContext.getApnType() +
836                    " due to " + apnContext.getReason() + " apnContext=" + apnContext);
837            log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
838        }
839        apnContext.requestLog("trySetupData due to " + apnContext.getReason());
840
841        if (mPhone.getSimulatedRadioControl() != null) {
842            // Assume data is connected on the simulator
843            // FIXME  this can be improved
844            apnContext.setState(DctConstants.State.CONNECTED);
845            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
846
847            log("trySetupData: X We're on the simulator; assuming connected retValue=true");
848            return true;
849        }
850
851        // Allow SETUP_DATA request for E-APN to be completed during emergency call
852        // and MOBILE DATA On/Off cases as well.
853        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
854        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
855        boolean desiredPowerState = sst.getDesiredPowerState();
856        boolean checkUserDataEnabled =
857                    !(apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS));
858
859        if (apnContext.isConnectable() && (isEmergencyApn ||
860                (isDataAllowed(apnContext) &&
861                getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
862            if (apnContext.getState() == DctConstants.State.FAILED) {
863                String str ="trySetupData: make a FAILED ApnContext IDLE so its reusable";
864                if (DBG) log(str);
865                apnContext.requestLog(str);
866                apnContext.setState(DctConstants.State.IDLE);
867            }
868            int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
869            apnContext.setConcurrentVoiceAndDataAllowed(sst.isConcurrentVoiceAndDataAllowed());
870            if (apnContext.getState() == DctConstants.State.IDLE) {
871                if (waitingApns == null) {
872                    waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
873                }
874                if (waitingApns.isEmpty()) {
875                    notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
876                    notifyOffApnsOfAvailability(apnContext.getReason());
877                    String str = "trySetupData: X No APN found retValue=false";
878                    if (DBG) log(str);
879                    apnContext.requestLog(str);
880                    return false;
881                } else {
882                    apnContext.setWaitingApns(waitingApns);
883                    if (DBG) {
884                        log ("trySetupData: Create from mAllApnSettings : "
885                                    + apnListToString(mAllApnSettings));
886                    }
887                }
888            }
889
890            if (DBG) {
891                log("trySetupData: call setupData, waitingApns : "
892                        + apnListToString(apnContext.getWaitingApns()));
893            }
894            boolean retValue = setupData(apnContext, radioTech);
895            notifyOffApnsOfAvailability(apnContext.getReason());
896
897            if (DBG) log("trySetupData: X retValue=" + retValue);
898            return retValue;
899        } else {
900            if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
901                    && apnContext.isConnectable()) {
902                mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
903            }
904            notifyOffApnsOfAvailability(apnContext.getReason());
905            String str = "trySetupData: X apnContext not 'ready' retValue=false";
906            apnContext.requestLog(str);
907            if (DBG) {
908                log(str);
909                if (!apnContext.isConnectable()) log("apnContext.isConnectable = false");
910                if (!isDataAllowed(apnContext)) log("isDataAllowed = false");
911                if (!getAnyDataEnabled(checkUserDataEnabled)) {
912                    log("getAnyDataEnabled(" + checkUserDataEnabled + ") = false");
913                }
914            }
915            return false;
916        }
917    }
918
919    @Override
920    // Disabled apn's still need avail/unavail notificiations - send them out
921    protected void notifyOffApnsOfAvailability(String reason) {
922        for (ApnContext apnContext : mApnContexts.values()) {
923            if (!mAttached.get() || !apnContext.isReady()) {
924                if (VDBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
925                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
926                                            apnContext.getApnType(),
927                                            PhoneConstants.DataState.DISCONNECTED);
928            } else {
929                if (VDBG) {
930                    log("notifyOffApnsOfAvailability skipped apn due to attached && isReady " +
931                            apnContext.toString());
932                }
933            }
934        }
935    }
936
937    /**
938     * If tearDown is true, this only tears down a CONNECTED session. Presently,
939     * there is no mechanism for abandoning an CONNECTING session,
940     * but would likely involve cancelling pending async requests or
941     * setting a flag or new state to ignore them when they came in
942     * @param tearDown true if the underlying DataConnection should be
943     * disconnected.
944     * @param reason reason for the clean up.
945     * @return boolean - true if we did cleanup any connections, false if they
946     *                   were already all disconnected.
947     */
948    protected boolean cleanUpAllConnections(boolean tearDown, String reason) {
949        if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
950        boolean didDisconnect = false;
951        boolean specificdisable = false;
952
953        if (!TextUtils.isEmpty(reason)) {
954            specificdisable = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED);
955        }
956
957        for (ApnContext apnContext : mApnContexts.values()) {
958            if (apnContext.isDisconnected() == false) didDisconnect = true;
959            if (specificdisable) {
960                if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
961                    if (DBG) log("ApnConextType: " + apnContext.getApnType());
962                    apnContext.setReason(reason);
963                    cleanUpConnection(tearDown, apnContext);
964                }
965            } else {
966                // TODO - only do cleanup if not disconnected
967                apnContext.setReason(reason);
968                cleanUpConnection(tearDown, apnContext);
969            }
970        }
971
972        stopNetStatPoll();
973        stopDataStallAlarm();
974
975        // TODO: Do we need mRequestedApnType?
976        mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
977
978        log("cleanUpConnection: mDisconnectPendingCount = " + mDisconnectPendingCount);
979        if (tearDown && mDisconnectPendingCount == 0) {
980            notifyDataDisconnectComplete();
981            notifyAllDataDisconnected();
982        }
983
984        return didDisconnect;
985    }
986
987    /**
988     * Cleanup all connections.
989     *
990     * TODO: Cleanup only a specified connection passed as a parameter.
991     *       Also, make sure when you clean up a conn, if it is last apply
992     *       logic as though it is cleanupAllConnections
993     *
994     * @param cause for the clean up.
995     */
996
997    @Override
998    protected void onCleanUpAllConnections(String cause) {
999        cleanUpAllConnections(true, cause);
1000    }
1001
1002    protected void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
1003
1004        if (apnContext == null) {
1005            if (DBG) log("cleanUpConnection: apn context is null");
1006            return;
1007        }
1008
1009        DcAsyncChannel dcac = apnContext.getDcAc();
1010        String str = "cleanUpConnection: tearDown=" + tearDown + " reason=" +
1011                apnContext.getReason();
1012        if (DBG) log(str + " apnContext=" + apnContext);
1013        apnContext.requestLog(str);
1014        if (tearDown) {
1015            if (apnContext.isDisconnected()) {
1016                // The request is tearDown and but ApnContext is not connected.
1017                // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
1018                apnContext.setState(DctConstants.State.IDLE);
1019                if (!apnContext.isReady()) {
1020                    if (dcac != null) {
1021                        str = "cleanUpConnection: teardown, disconnectd, !ready";
1022                        if (DBG) log(str + " apnContext=" + apnContext);
1023                        apnContext.requestLog(str);
1024                        dcac.tearDown(apnContext, "", null);
1025                    }
1026                    apnContext.setDataConnectionAc(null);
1027                }
1028            } else {
1029                // Connection is still there. Try to clean up.
1030                if (dcac != null) {
1031                    if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
1032                        boolean disconnectAll = false;
1033                        if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
1034                            // CAF_MSIM is this below condition required.
1035                            // if (PhoneConstants.APN_TYPE_DUN.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
1036                            if (teardownForDun()) {
1037                                if (DBG) {
1038                                    log("cleanUpConnection: disconnectAll DUN connection");
1039                                }
1040                                // we need to tear it down - we brought it up just for dun and
1041                                // other people are camped on it and now dun is done.  We need
1042                                // to stop using it and let the normal apn list get used to find
1043                                // connections for the remaining desired connections
1044                                disconnectAll = true;
1045                            }
1046                        }
1047                        str = "cleanUpConnection: tearing down" + (disconnectAll ? " all" : "");
1048                        if (DBG) log(str + "apnContext=" + apnContext);
1049                        apnContext.requestLog(str);
1050                        Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);
1051                        if (disconnectAll) {
1052                            apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
1053                        } else {
1054                            apnContext.getDcAc()
1055                                .tearDown(apnContext, apnContext.getReason(), msg);
1056                        }
1057                        apnContext.setState(DctConstants.State.DISCONNECTING);
1058                        mDisconnectPendingCount++;
1059                    }
1060                } else {
1061                    // apn is connected but no reference to dcac.
1062                    // Should not be happen, but reset the state in case.
1063                    apnContext.setState(DctConstants.State.IDLE);
1064                    apnContext.requestLog("cleanUpConnection: connected, bug no DCAC");
1065                    mPhone.notifyDataConnection(apnContext.getReason(),
1066                                                apnContext.getApnType());
1067                }
1068            }
1069        } else {
1070            // force clean up the data connection.
1071            if (dcac != null) dcac.reqReset();
1072            apnContext.setState(DctConstants.State.IDLE);
1073            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1074            apnContext.setDataConnectionAc(null);
1075        }
1076
1077        // Make sure reconnection alarm is cleaned up if there is no ApnContext
1078        // associated to the connection.
1079        if (dcac != null) {
1080            cancelReconnectAlarm(apnContext);
1081        }
1082        str = "cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason();
1083        if (DBG) log(str + " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
1084        apnContext.requestLog(str);
1085    }
1086
1087    /**
1088     * Determine if DUN connection is special and we need to teardown on start/stop
1089     */
1090    private boolean teardownForDun() {
1091        // CDMA always needs to do this the profile id is correct
1092        final int rilRat = mPhone.getServiceState().getRilDataRadioTechnology();
1093        if (ServiceState.isCdma(rilRat)) return true;
1094
1095        return (fetchDunApn() != null);
1096    }
1097
1098    /**
1099     * Cancels the alarm associated with apnContext.
1100     *
1101     * @param apnContext on which the alarm should be stopped.
1102     */
1103    private void cancelReconnectAlarm(ApnContext apnContext) {
1104        if (apnContext == null) return;
1105
1106        PendingIntent intent = apnContext.getReconnectIntent();
1107
1108        if (intent != null) {
1109                AlarmManager am =
1110                    (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1111                am.cancel(intent);
1112                apnContext.setReconnectIntent(null);
1113        }
1114    }
1115
1116    /**
1117     * @param types comma delimited list of APN types
1118     * @return array of APN types
1119     */
1120    private String[] parseTypes(String types) {
1121        String[] result;
1122        // If unset, set to DEFAULT.
1123        if (types == null || types.equals("")) {
1124            result = new String[1];
1125            result[0] = PhoneConstants.APN_TYPE_ALL;
1126        } else {
1127            result = types.split(",");
1128        }
1129        return result;
1130    }
1131
1132    @Override
1133    protected boolean isPermanentFail(DcFailCause dcFailCause) {
1134        return (dcFailCause.isPermanentFail() &&
1135                (mAttached.get() == false || dcFailCause != DcFailCause.SIGNAL_LOST));
1136    }
1137
1138    private ApnSetting makeApnSetting(Cursor cursor) {
1139        String[] types = parseTypes(
1140                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
1141        int auth_type = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE));
1142        if (auth_type == -1) {
1143            auth_type = 0;
1144        }
1145        ApnSetting apn = new ApnSetting(
1146                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
1147                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
1148                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
1149                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
1150                NetworkUtils.trimV4AddrZeros(
1151                        cursor.getString(
1152                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
1153                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
1154                NetworkUtils.trimV4AddrZeros(
1155                        cursor.getString(
1156                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
1157                NetworkUtils.trimV4AddrZeros(
1158                        cursor.getString(
1159                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
1160                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
1161                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
1162                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
1163                auth_type,
1164                types,
1165                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
1166                cursor.getString(cursor.getColumnIndexOrThrow(
1167                        Telephony.Carriers.ROAMING_PROTOCOL)),
1168                cursor.getInt(cursor.getColumnIndexOrThrow(
1169                        Telephony.Carriers.CARRIER_ENABLED)) == 1,
1170                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
1171                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER_BITMASK)),
1172                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
1173                cursor.getInt(cursor.getColumnIndexOrThrow(
1174                        Telephony.Carriers.MODEM_COGNITIVE)) == 1,
1175                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
1176                cursor.getInt(cursor.getColumnIndexOrThrow(
1177                        Telephony.Carriers.WAIT_TIME)),
1178                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
1179                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
1180                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
1181                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)));
1182        return apn;
1183    }
1184
1185    private ArrayList<ApnSetting> createApnList(Cursor cursor) {
1186        ArrayList<ApnSetting> mnoApns = new ArrayList<ApnSetting>();
1187        ArrayList<ApnSetting> mvnoApns = new ArrayList<ApnSetting>();
1188        IccRecords r = mIccRecords.get();
1189
1190        if (cursor.moveToFirst()) {
1191            do {
1192                ApnSetting apn = makeApnSetting(cursor);
1193                if (apn == null) {
1194                    continue;
1195                }
1196
1197                if (apn.hasMvnoParams()) {
1198                    if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
1199                        mvnoApns.add(apn);
1200                    }
1201                } else {
1202                    mnoApns.add(apn);
1203                }
1204            } while (cursor.moveToNext());
1205        }
1206
1207        ArrayList<ApnSetting> result = mvnoApns.isEmpty() ? mnoApns : mvnoApns;
1208        if (DBG) log("createApnList: X result=" + result);
1209        return result;
1210    }
1211
1212    private boolean dataConnectionNotInUse(DcAsyncChannel dcac) {
1213        if (DBG) log("dataConnectionNotInUse: check if dcac is inuse dcac=" + dcac);
1214        for (ApnContext apnContext : mApnContexts.values()) {
1215            if (apnContext.getDcAc() == dcac) {
1216                if (DBG) log("dataConnectionNotInUse: in use by apnContext=" + apnContext);
1217                return false;
1218            }
1219        }
1220        // TODO: Fix retry handling so free DataConnections have empty apnlists.
1221        // Probably move retry handling into DataConnections and reduce complexity
1222        // of DCT.
1223        if (DBG) log("dataConnectionNotInUse: tearDownAll");
1224        dcac.tearDownAll("No connection", null);
1225        if (DBG) log("dataConnectionNotInUse: not in use return true");
1226        return true;
1227    }
1228
1229    private DcAsyncChannel findFreeDataConnection() {
1230        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
1231            if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
1232                if (DBG) {
1233                    log("findFreeDataConnection: found free DataConnection=" +
1234                        " dcac=" + dcac);
1235                }
1236                return dcac;
1237            }
1238        }
1239        log("findFreeDataConnection: NO free DataConnection");
1240        return null;
1241    }
1242
1243    private boolean setupData(ApnContext apnContext, int radioTech) {
1244        if (DBG) log("setupData: apnContext=" + apnContext);
1245        apnContext.requestLog("setupData");
1246        ApnSetting apnSetting;
1247        DcAsyncChannel dcac = null;
1248
1249        apnSetting = apnContext.getNextWaitingApn();
1250        if (apnSetting == null) {
1251            if (DBG) log("setupData: return for no apn found!");
1252            return false;
1253        }
1254
1255        int profileId = apnSetting.profileId;
1256        if (profileId == 0) {
1257            profileId = getApnProfileID(apnContext.getApnType());
1258        }
1259
1260        // On CDMA, if we're explicitly asking for DUN, we need have
1261        // a dun-profiled connection so we can't share an existing one
1262        // On GSM/LTE we can share existing apn connections provided they support
1263        // this type.
1264        if (apnContext.getApnType() != PhoneConstants.APN_TYPE_DUN ||
1265                teardownForDun() == false) {
1266            dcac = checkForCompatibleConnectedApnContext(apnContext);
1267            if (dcac != null) {
1268                // Get the dcacApnSetting for the connection we want to share.
1269                ApnSetting dcacApnSetting = dcac.getApnSettingSync();
1270                if (dcacApnSetting != null) {
1271                    // Setting is good, so use it.
1272                    apnSetting = dcacApnSetting;
1273                }
1274            }
1275        }
1276        if (dcac == null) {
1277            if (isOnlySingleDcAllowed(radioTech)) {
1278                if (isHigherPriorityApnContextActive(apnContext)) {
1279                    if (DBG) {
1280                        log("setupData: Higher priority ApnContext active.  Ignoring call");
1281                    }
1282                    return false;
1283                }
1284
1285                // Only lower priority calls left.  Disconnect them all in this single PDP case
1286                // so that we can bring up the requested higher priority call (once we receive
1287                // repsonse for deactivate request for the calls we are about to disconnect
1288                if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
1289                    // If any call actually requested to be disconnected, means we can't
1290                    // bring up this connection yet as we need to wait for those data calls
1291                    // to be disconnected.
1292                    if (DBG) log("setupData: Some calls are disconnecting first.  Wait and retry");
1293                    return false;
1294                }
1295
1296                // No other calls are active, so proceed
1297                if (DBG) log("setupData: Single pdp. Continue setting up data call.");
1298            }
1299
1300            dcac = findFreeDataConnection();
1301
1302            if (dcac == null) {
1303                dcac = createDataConnection();
1304            }
1305
1306            if (dcac == null) {
1307                if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
1308                return false;
1309            }
1310        }
1311        if (DBG) log("setupData: dcac=" + dcac + " apnSetting=" + apnSetting);
1312
1313        apnContext.setDataConnectionAc(dcac);
1314        apnContext.setApnSetting(apnSetting);
1315        int connectionGeneration = apnContext.incAndGetConnectionGeneration();
1316        apnContext.setState(DctConstants.State.CONNECTING);
1317        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1318
1319        Message msg = obtainMessage();
1320        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
1321        msg.obj = apnContext;
1322        dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech,
1323                mAutoAttachOnCreation.get(), msg, connectionGeneration);
1324
1325        if (DBG) log("setupData: initing!");
1326        return true;
1327    }
1328
1329    /**
1330     * Handles changes to the APN database.
1331     */
1332    private void onApnChanged() {
1333        DctConstants.State overallState = getOverallState();
1334        boolean isDisconnected = (overallState == DctConstants.State.IDLE ||
1335                overallState == DctConstants.State.FAILED);
1336
1337        if (mPhone instanceof GSMPhone) {
1338            // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
1339            ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
1340        }
1341
1342        // TODO: It'd be nice to only do this if the changed entrie(s)
1343        // match the current operator.
1344        if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
1345        createAllApnList();
1346        setInitialAttachApn();
1347        cleanUpConnectionsOnUpdatedApns(!isDisconnected);
1348
1349        // FIXME: See bug 17426028 maybe no conditional is needed.
1350        if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubId()) {
1351            setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
1352        }
1353    }
1354
1355    /**
1356     * @param cid Connection id provided from RIL.
1357     * @return DataConnectionAc associated with specified cid.
1358     */
1359    private DcAsyncChannel findDataConnectionAcByCid(int cid) {
1360        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
1361            if (dcac.getCidSync() == cid) {
1362                return dcac;
1363            }
1364        }
1365        return null;
1366    }
1367
1368    // TODO: For multiple Active APNs not exactly sure how to do this.
1369    @Override
1370    protected void gotoIdleAndNotifyDataConnection(String reason) {
1371        if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
1372        notifyDataConnection(reason);
1373        mActiveApn = null;
1374    }
1375
1376    /**
1377     * "Active" here means ApnContext isEnabled() and not in FAILED state
1378     * @param apnContext to compare with
1379     * @return true if higher priority active apn found
1380     */
1381    private boolean isHigherPriorityApnContextActive(ApnContext apnContext) {
1382        for (ApnContext otherContext : mPrioritySortedApnContexts) {
1383            if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
1384            if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) {
1385                return true;
1386            }
1387        }
1388        return false;
1389    }
1390
1391    /**
1392     * Reports if we support multiple connections or not.
1393     * This is a combination of factors, based on carrier and RAT.
1394     * @param rilRadioTech the RIL Radio Tech currently in use
1395     * @return true if only single DataConnection is allowed
1396     */
1397    private boolean isOnlySingleDcAllowed(int rilRadioTech) {
1398        int[] singleDcRats = mPhone.getContext().getResources().getIntArray(
1399                com.android.internal.R.array.config_onlySingleDcAllowed);
1400        boolean onlySingleDcAllowed = false;
1401        if (Build.IS_DEBUGGABLE &&
1402                SystemProperties.getBoolean("persist.telephony.test.singleDc", false)) {
1403            onlySingleDcAllowed = true;
1404        }
1405        if (singleDcRats != null) {
1406            for (int i=0; i < singleDcRats.length && onlySingleDcAllowed == false; i++) {
1407                if (rilRadioTech == singleDcRats[i]) onlySingleDcAllowed = true;
1408            }
1409        }
1410
1411        if (DBG) log("isOnlySingleDcAllowed(" + rilRadioTech + "): " + onlySingleDcAllowed);
1412        return onlySingleDcAllowed;
1413    }
1414
1415    @Override
1416    protected void restartRadio() {
1417        if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
1418        cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
1419        mPhone.getServiceStateTracker().powerOffRadioSafely(this);
1420        /* Note: no need to call setRadioPower(true).  Assuming the desired
1421         * radio power state is still ON (as tracked by ServiceStateTracker),
1422         * ServiceStateTracker will call setRadioPower when it receives the
1423         * RADIO_STATE_CHANGED notification for the power off.  And if the
1424         * desired power state has changed in the interim, we don't want to
1425         * override it with an unconditional power on.
1426         */
1427
1428        int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
1429        SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
1430    }
1431
1432    /**
1433     * Return true if data connection need to be setup after disconnected due to
1434     * reason.
1435     *
1436     * @param reason the reason why data is disconnected
1437     * @return true if try setup data connection is need for this reason
1438     */
1439    private boolean retryAfterDisconnected(ApnContext apnContext) {
1440        boolean retry = true;
1441        String reason = apnContext.getReason();
1442
1443        if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ||
1444                (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())
1445                 && isHigherPriorityApnContextActive(apnContext))) {
1446            retry = false;
1447        }
1448        return retry;
1449    }
1450
1451    private void startAlarmForReconnect(int delay, ApnContext apnContext) {
1452        String apnType = apnContext.getApnType();
1453
1454        Intent intent = new Intent(INTENT_RECONNECT_ALARM + "." + apnType);
1455        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
1456        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnType);
1457
1458        // Get current sub id.
1459        int subId = SubscriptionManager.getDefaultDataSubId();
1460        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1461
1462        if (DBG) {
1463            log("startAlarmForReconnect: delay=" + delay + " action=" + intent.getAction()
1464                    + " apn=" + apnContext);
1465        }
1466
1467        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1468                                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
1469        apnContext.setReconnectIntent(alarmIntent);
1470        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1471                SystemClock.elapsedRealtime() + delay, alarmIntent);
1472    }
1473
1474    private void startAlarmForRestartTrySetup(int delay, ApnContext apnContext) {
1475        String apnType = apnContext.getApnType();
1476        Intent intent = new Intent(INTENT_RESTART_TRYSETUP_ALARM + "." + apnType);
1477        intent.putExtra(INTENT_RESTART_TRYSETUP_ALARM_EXTRA_TYPE, apnType);
1478
1479        if (DBG) {
1480            log("startAlarmForRestartTrySetup: delay=" + delay + " action=" + intent.getAction()
1481                    + " apn=" + apnContext);
1482        }
1483        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1484                                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
1485        apnContext.setReconnectIntent(alarmIntent);
1486        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1487                SystemClock.elapsedRealtime() + delay, alarmIntent);
1488    }
1489
1490    private void notifyNoData(DcFailCause lastFailCauseCode,
1491                              ApnContext apnContext) {
1492        if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
1493        if (isPermanentFail(lastFailCauseCode)
1494            && (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT))) {
1495            mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1496        }
1497    }
1498
1499    private void onRecordsLoaded() {
1500        if (DBG) log("onRecordsLoaded: createAllApnList");
1501        mAutoAttachOnCreationConfig = mPhone.getContext().getResources()
1502                .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
1503
1504        createAllApnList();
1505        setInitialAttachApn();
1506        if (mPhone.mCi.getRadioState().isOn()) {
1507            if (DBG) log("onRecordsLoaded: notifying data availability");
1508            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
1509        }
1510        setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
1511    }
1512
1513    private void onSimNotReady() {
1514        if (DBG) log("onSimNotReady");
1515
1516        cleanUpAllConnections(true, Phone.REASON_SIM_NOT_READY);
1517        mAllApnSettings = null;
1518        mAutoAttachOnCreationConfig = false;
1519    }
1520
1521    @Override
1522    protected void onSetDependencyMet(String apnType, boolean met) {
1523        // don't allow users to tweak hipri to work around default dependency not met
1524        if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
1525
1526        ApnContext apnContext = mApnContexts.get(apnType);
1527        if (apnContext == null) {
1528            loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
1529                    apnType + ", " + met + ")");
1530            return;
1531        }
1532        applyNewState(apnContext, apnContext.isEnabled(), met);
1533        if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
1534            // tie actions on default to similar actions on HIPRI regarding dependencyMet
1535            apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
1536            if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
1537        }
1538    }
1539
1540    private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
1541        boolean cleanup = false;
1542        boolean trySetup = false;
1543        String str ="applyNewState(" + apnContext.getApnType() + ", " + enabled +
1544                "(" + apnContext.isEnabled() + "), " + met + "(" +
1545                apnContext.getDependencyMet() +"))";
1546        if (DBG) log(str);
1547        apnContext.requestLog(str);
1548
1549        if (apnContext.isReady()) {
1550            cleanup = true;
1551            if (enabled && met) {
1552                DctConstants.State state = apnContext.getState();
1553                switch(state) {
1554                    case CONNECTING:
1555                    case SCANNING:
1556                    case CONNECTED:
1557                    case DISCONNECTING:
1558                        // We're "READY" and active so just return
1559                        if (DBG) log("applyNewState: 'ready' so return");
1560                        apnContext.requestLog("applyNewState state=" + state + ", so return");
1561                        return;
1562                    case IDLE:
1563                        // fall through: this is unexpected but if it happens cleanup and try setup
1564                    case FAILED:
1565                    case RETRYING: {
1566                        // We're "READY" but not active so disconnect (cleanup = true) and
1567                        // connect (trySetup = true) to be sure we retry the connection.
1568                        trySetup = true;
1569                        apnContext.setReason(Phone.REASON_DATA_ENABLED);
1570                        break;
1571                    }
1572                }
1573            } else if (met) {
1574                apnContext.setReason(Phone.REASON_DATA_DISABLED);
1575                // If ConnectivityService has disabled this network, stop trying to bring
1576                // it up, but do not tear it down - ConnectivityService will do that
1577                // directly by talking with the DataConnection.
1578                //
1579                // This doesn't apply to DUN, however.  Those connections have special
1580                // requirements from carriers and we need stop using them when the dun
1581                // request goes away.  This applies to both CDMA and GSM because they both
1582                // can declare the DUN APN sharable by default traffic, thus still satisfying
1583                // those requests and not torn down organically.
1584                if (apnContext.getApnType() == PhoneConstants.APN_TYPE_DUN && teardownForDun()) {
1585                    cleanup = true;
1586                } else {
1587                    cleanup = false;
1588                }
1589            } else {
1590                apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
1591            }
1592        } else {
1593            if (enabled && met) {
1594                if (apnContext.isEnabled()) {
1595                    apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
1596                } else {
1597                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
1598                }
1599                if (apnContext.getState() == DctConstants.State.FAILED) {
1600                    apnContext.setState(DctConstants.State.IDLE);
1601                }
1602                trySetup = true;
1603            }
1604        }
1605        apnContext.setEnabled(enabled);
1606        apnContext.setDependencyMet(met);
1607        if (cleanup) cleanUpConnection(true, apnContext);
1608        if (trySetup) {
1609            apnContext.resetErrorCodeRetries();
1610            trySetupData(apnContext);
1611        }
1612    }
1613
1614    private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
1615        String apnType = apnContext.getApnType();
1616        ApnSetting dunSetting = null;
1617
1618        if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
1619            dunSetting = fetchDunApn();
1620        }
1621        if (DBG) {
1622            log("checkForCompatibleConnectedApnContext: apnContext=" + apnContext );
1623        }
1624
1625        DcAsyncChannel potentialDcac = null;
1626        ApnContext potentialApnCtx = null;
1627        for (ApnContext curApnCtx : mApnContexts.values()) {
1628            DcAsyncChannel curDcac = curApnCtx.getDcAc();
1629            if (curDcac != null) {
1630                ApnSetting apnSetting = curApnCtx.getApnSetting();
1631                log("apnSetting: " + apnSetting);
1632                if (dunSetting != null) {
1633                    if (dunSetting.equals(apnSetting)) {
1634                        switch (curApnCtx.getState()) {
1635                            case CONNECTED:
1636                                if (DBG) {
1637                                    log("checkForCompatibleConnectedApnContext:"
1638                                            + " found dun conn=" + curDcac
1639                                            + " curApnCtx=" + curApnCtx);
1640                                }
1641                                return curDcac;
1642                            case RETRYING:
1643                            case CONNECTING:
1644                                potentialDcac = curDcac;
1645                                potentialApnCtx = curApnCtx;
1646                            default:
1647                                // Not connected, potential unchanged
1648                                break;
1649                        }
1650                    }
1651                } else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
1652                    switch (curApnCtx.getState()) {
1653                        case CONNECTED:
1654                            if (DBG) {
1655                                log("checkForCompatibleConnectedApnContext:"
1656                                        + " found canHandle conn=" + curDcac
1657                                        + " curApnCtx=" + curApnCtx);
1658                            }
1659                            return curDcac;
1660                        case RETRYING:
1661                        case CONNECTING:
1662                            potentialDcac = curDcac;
1663                            potentialApnCtx = curApnCtx;
1664                        default:
1665                            // Not connected, potential unchanged
1666                            break;
1667                    }
1668                }
1669            } else {
1670                if (VDBG) {
1671                    log("checkForCompatibleConnectedApnContext: not conn curApnCtx=" + curApnCtx);
1672                }
1673            }
1674        }
1675        if (potentialDcac != null) {
1676            if (DBG) {
1677                log("checkForCompatibleConnectedApnContext: found potential conn=" + potentialDcac
1678                        + " curApnCtx=" + potentialApnCtx);
1679            }
1680            return potentialDcac;
1681        }
1682
1683        if (DBG) log("checkForCompatibleConnectedApnContext: NO conn apnContext=" + apnContext);
1684        return null;
1685    }
1686
1687    @Override
1688    protected void onEnableApn(int apnId, int enabled) {
1689        ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1690        if (apnContext == null) {
1691            loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
1692            return;
1693        }
1694        // TODO change our retry manager to use the appropriate numbers for the new APN
1695        if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
1696        applyNewState(apnContext, enabled == DctConstants.ENABLED, apnContext.getDependencyMet());
1697    }
1698
1699    @Override
1700    // TODO: We shouldnt need this.
1701    protected boolean onTrySetupData(String reason) {
1702        if (DBG) log("onTrySetupData: reason=" + reason);
1703        setupDataOnConnectableApns(reason);
1704        return true;
1705    }
1706
1707    protected boolean onTrySetupData(ApnContext apnContext) {
1708        if (DBG) log("onTrySetupData: apnContext=" + apnContext);
1709        return trySetupData(apnContext);
1710    }
1711
1712    @Override
1713    protected void onRoamingOff() {
1714        if (DBG) log("onRoamingOff");
1715
1716        if (!mUserDataEnabled) return;
1717
1718        if (getDataOnRoamingEnabled() == false) {
1719            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
1720            setupDataOnConnectableApns(Phone.REASON_ROAMING_OFF);
1721        } else {
1722            notifyDataConnection(Phone.REASON_ROAMING_OFF);
1723        }
1724    }
1725
1726    @Override
1727    protected void onRoamingOn() {
1728        if (DBG) log("onRoamingOn");
1729
1730        if (!mUserDataEnabled) return;
1731
1732        if (getDataOnRoamingEnabled()) {
1733            if (DBG) log("onRoamingOn: setup data on roaming");
1734            setupDataOnConnectableApns(Phone.REASON_ROAMING_ON);
1735            notifyDataConnection(Phone.REASON_ROAMING_ON);
1736        } else {
1737            if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
1738            cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
1739            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
1740        }
1741    }
1742
1743    @Override
1744    protected void onRadioAvailable() {
1745        if (DBG) log("onRadioAvailable");
1746        if (mPhone.getSimulatedRadioControl() != null) {
1747            // Assume data is connected on the simulator
1748            // FIXME  this can be improved
1749            // setState(DctConstants.State.CONNECTED);
1750            notifyDataConnection(null);
1751
1752            log("onRadioAvailable: We're on the simulator; assuming data is connected");
1753        }
1754
1755        IccRecords r = mIccRecords.get();
1756        if (r != null && r.getRecordsLoaded()) {
1757            notifyOffApnsOfAvailability(null);
1758        }
1759
1760        if (getOverallState() != DctConstants.State.IDLE) {
1761            cleanUpConnection(true, null);
1762        }
1763    }
1764
1765    @Override
1766    protected void onRadioOffOrNotAvailable() {
1767        // Make sure our reconnect delay starts at the initial value
1768        // next time the radio comes on
1769
1770        mReregisterOnReconnectFailure = false;
1771
1772        if (mPhone.getSimulatedRadioControl() != null) {
1773            // Assume data is connected on the simulator
1774            // FIXME  this can be improved
1775            log("We're on the simulator; assuming radio off is meaningless");
1776        } else {
1777            if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
1778            cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
1779        }
1780        notifyOffApnsOfAvailability(null);
1781    }
1782
1783    @Override
1784    protected void completeConnection(ApnContext apnContext) {
1785        boolean isProvApn = apnContext.isProvisioningApn();
1786
1787        if (DBG) log("completeConnection: successful, notify the world apnContext=" + apnContext);
1788
1789        if (mIsProvisioning && !TextUtils.isEmpty(mProvisioningUrl)) {
1790            if (DBG) {
1791                log("completeConnection: MOBILE_PROVISIONING_ACTION url="
1792                        + mProvisioningUrl);
1793            }
1794            Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
1795                    Intent.CATEGORY_APP_BROWSER);
1796            newIntent.setData(Uri.parse(mProvisioningUrl));
1797            newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
1798                    Intent.FLAG_ACTIVITY_NEW_TASK);
1799            try {
1800                mPhone.getContext().startActivity(newIntent);
1801            } catch (ActivityNotFoundException e) {
1802                loge("completeConnection: startActivityAsUser failed" + e);
1803            }
1804        }
1805        mIsProvisioning = false;
1806        mProvisioningUrl = null;
1807        if (mProvisioningSpinner != null) {
1808            sendMessage(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
1809                    mProvisioningSpinner));
1810        }
1811
1812        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1813        startNetStatPoll();
1814        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1815    }
1816
1817    /**
1818     * A SETUP (aka bringUp) has completed, possibly with an error. If
1819     * there is an error this method will call {@link #onDataSetupCompleteError}.
1820     */
1821    @Override
1822    protected void onDataSetupComplete(AsyncResult ar) {
1823
1824        DcFailCause cause = DcFailCause.UNKNOWN;
1825        boolean handleError = false;
1826        ApnContext apnContext = getValidApnContext(ar, "onDataSetupComplete");
1827
1828        if (apnContext == null) return;
1829
1830        if (ar.exception == null) {
1831            DcAsyncChannel dcac = apnContext.getDcAc();
1832
1833            if (RADIO_TESTS) {
1834                // Note: To change radio.test.onDSC.null.dcac from command line you need to
1835                // adb root and adb remount and from the command line you can only change the
1836                // value to 1 once. To change it a second time you can reboot or execute
1837                // adb shell stop and then adb shell start. The command line to set the value is:
1838                // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');"
1839                ContentResolver cr = mPhone.getContext().getContentResolver();
1840                String radioTestProperty = "radio.test.onDSC.null.dcac";
1841                if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) {
1842                    log("onDataSetupComplete: " + radioTestProperty +
1843                            " is true, set dcac to null and reset property to false");
1844                    dcac = null;
1845                    Settings.System.putInt(cr, radioTestProperty, 0);
1846                    log("onDataSetupComplete: " + radioTestProperty + "=" +
1847                            Settings.System.getInt(mPhone.getContext().getContentResolver(),
1848                                    radioTestProperty, -1));
1849                }
1850            }
1851            if (dcac == null) {
1852                log("onDataSetupComplete: no connection to DC, handle as error");
1853                cause = DcFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
1854                handleError = true;
1855            } else {
1856                ApnSetting apn = apnContext.getApnSetting();
1857                if (DBG) {
1858                    log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
1859                }
1860                if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
1861                    try {
1862                        String port = apn.port;
1863                        if (TextUtils.isEmpty(port)) port = "8080";
1864                        ProxyInfo proxy = new ProxyInfo(apn.proxy,
1865                                Integer.parseInt(port), null);
1866                        dcac.setLinkPropertiesHttpProxySync(proxy);
1867                    } catch (NumberFormatException e) {
1868                        loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
1869                                apn.port + "): " + e);
1870                    }
1871                }
1872
1873                // everything is setup
1874                if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
1875                    SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");
1876                    if (mCanSetPreferApn && mPreferredApn == null) {
1877                        if (DBG) log("onDataSetupComplete: PREFERED APN is null");
1878                        mPreferredApn = apn;
1879                        if (mPreferredApn != null) {
1880                            setPreferredApn(mPreferredApn.id);
1881                        }
1882                    }
1883                } else {
1884                    SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
1885                }
1886
1887                // A connection is setup
1888                apnContext.setState(DctConstants.State.CONNECTED);
1889                boolean isProvApn = apnContext.isProvisioningApn();
1890                final ConnectivityManager cm = ConnectivityManager.from(mPhone.getContext());
1891                if (mProvisionBroadcastReceiver != null) {
1892                    mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
1893                    mProvisionBroadcastReceiver = null;
1894                }
1895                if ((!isProvApn) || mIsProvisioning) {
1896                    // Hide any provisioning notification.
1897                    cm.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_MOBILE,
1898                            mProvisionActionName);
1899                    // Complete the connection normally notifying the world we're connected.
1900                    // We do this if this isn't a special provisioning apn or if we've been
1901                    // told its time to provision.
1902                    completeConnection(apnContext);
1903                } else {
1904                    // This is a provisioning APN that we're reporting as connected. Later
1905                    // when the user desires to upgrade this to a "default" connection,
1906                    // mIsProvisioning == true, we'll go through the code path above.
1907                    // mIsProvisioning becomes true when CMD_ENABLE_MOBILE_PROVISIONING
1908                    // is sent to the DCT.
1909                    if (DBG) {
1910                        log("onDataSetupComplete: successful, BUT send connected to prov apn as"
1911                                + " mIsProvisioning:" + mIsProvisioning + " == false"
1912                                + " && (isProvisioningApn:" + isProvApn + " == true");
1913                    }
1914
1915                    // While radio is up, grab provisioning URL.  The URL contains ICCID which
1916                    // disappears when radio is off.
1917                    mProvisionBroadcastReceiver = new ProvisionNotificationBroadcastReceiver(
1918                            cm.getMobileProvisioningUrl(),
1919                            TelephonyManager.getDefault().getNetworkOperatorName());
1920                    mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver,
1921                            new IntentFilter(mProvisionActionName));
1922                    // Put up user notification that sign-in is required.
1923                    cm.setProvisioningNotificationVisible(true, ConnectivityManager.TYPE_MOBILE,
1924                            mProvisionActionName);
1925                    // Turn off radio to save battery and avoid wasting carrier resources.
1926                    // The network isn't usable and network validation will just fail anyhow.
1927                    setRadio(false);
1928
1929                    Intent intent = new Intent(
1930                            TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
1931                    intent.putExtra(PhoneConstants.DATA_APN_KEY, apnContext.getApnSetting().apn);
1932                    intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnContext.getApnType());
1933
1934                    String apnType = apnContext.getApnType();
1935                    LinkProperties linkProperties = getLinkProperties(apnType);
1936                    if (linkProperties != null) {
1937                        intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
1938                        String iface = linkProperties.getInterfaceName();
1939                        if (iface != null) {
1940                            intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface);
1941                        }
1942                    }
1943                    NetworkCapabilities networkCapabilities = getNetworkCapabilities(apnType);
1944                    if (networkCapabilities != null) {
1945                        intent.putExtra(PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY,
1946                                networkCapabilities);
1947                    }
1948
1949                    mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
1950                }
1951                if (DBG) {
1952                    log("onDataSetupComplete: SETUP complete type=" + apnContext.getApnType()
1953                        + ", reason:" + apnContext.getReason());
1954                }
1955            }
1956        } else {
1957            cause = (DcFailCause) (ar.result);
1958            if (DBG) {
1959                ApnSetting apn = apnContext.getApnSetting();
1960                log(String.format("onDataSetupComplete: error apn=%s cause=%s",
1961                        (apn == null ? "unknown" : apn.apn), cause));
1962            }
1963            if (cause.isEventLoggable()) {
1964                // Log this failure to the Event Logs.
1965                int cid = getCellLocationId();
1966                EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1967                        cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType());
1968            }
1969            ApnSetting apn = apnContext.getApnSetting();
1970            mPhone.notifyPreciseDataConnectionFailed(apnContext.getReason(),
1971                    apnContext.getApnType(), apn != null ? apn.apn : "unknown", cause.toString());
1972
1973            // Count permanent failures and remove the APN we just tried
1974            if (isPermanentFail(cause)) apnContext.decWaitingApnsPermFailCount();
1975
1976            apnContext.removeWaitingApn(apnContext.getApnSetting());
1977            if (DBG) {
1978                log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
1979                        " WaitingApnsPermFailureCountDown=%d",
1980                        apnContext.getWaitingApns().size(),
1981                        apnContext.getWaitingApnsPermFailCount()));
1982            }
1983            handleError = true;
1984        }
1985
1986        if (handleError) {
1987            onDataSetupCompleteError(ar);
1988        }
1989
1990        /* If flag is set to false after SETUP_DATA_CALL is invoked, we need
1991         * to clean data connections.
1992         */
1993        if (!mInternalDataEnabled) {
1994            cleanUpAllConnections(null);
1995        }
1996
1997    }
1998
1999    /**
2000     * @return number of milli-seconds to delay between trying apns'
2001     */
2002    private int getApnDelay() {
2003        if (mFailFast) {
2004            return SystemProperties.getInt("persist.radio.apn_ff_delay",
2005                    APN_FAIL_FAST_DELAY_DEFAULT_MILLIS);
2006        } else {
2007            return SystemProperties.getInt("persist.radio.apn_delay", APN_DELAY_DEFAULT_MILLIS);
2008        }
2009    }
2010
2011    /**
2012     * check for obsolete messages.  Return ApnContext if valid, null if not
2013     */
2014    private ApnContext getValidApnContext(AsyncResult ar, String logString) {
2015        if (ar != null && ar.userObj instanceof Pair) {
2016            Pair<ApnContext, Integer>pair = (Pair<ApnContext, Integer>)ar.userObj;
2017            ApnContext apnContext = pair.first;
2018            if (apnContext != null) {
2019                if (apnContext.getConnectionGeneration() == pair.second) {
2020                    return apnContext;
2021                } else {
2022                    log("ignoring obsolete " + logString);
2023                    return null;
2024                }
2025            }
2026        }
2027        throw new RuntimeException(logString + ": No apnContext");
2028    }
2029
2030
2031
2032    /**
2033     * Error has occurred during the SETUP {aka bringUP} request and the DCT
2034     * should either try the next waiting APN or start over from the
2035     * beginning if the list is empty. Between each SETUP request there will
2036     * be a delay defined by {@link #getApnDelay()}.
2037     */
2038    @Override
2039    protected void onDataSetupCompleteError(AsyncResult ar) {
2040        String reason = "";
2041        ApnContext apnContext = getValidApnContext(ar, "onDataSetupCompleteError");
2042
2043        if (apnContext == null) return;
2044
2045        // See if there are more APN's to try
2046        if (apnContext.getWaitingApns().isEmpty()) {
2047            apnContext.setState(DctConstants.State.FAILED);
2048            mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
2049
2050            apnContext.setDataConnectionAc(null);
2051
2052            if (apnContext.getWaitingApnsPermFailCount() == 0) {
2053                if (DBG) {
2054                    log("onDataSetupCompleteError: All APN's had permanent failures, stop retrying");
2055                }
2056            } else {
2057                int delay = getApnDelay();
2058                if (DBG) {
2059                    log("onDataSetupCompleteError: Not all APN's had permanent failures delay="
2060                            + delay);
2061                }
2062                startAlarmForRestartTrySetup(delay, apnContext);
2063            }
2064        } else {
2065            if (DBG) log("onDataSetupCompleteError: Try next APN");
2066            apnContext.setState(DctConstants.State.SCANNING);
2067            // Wait a bit before trying the next APN, so that
2068            // we're not tying up the RIL command channel
2069            startAlarmForReconnect(getApnDelay(), apnContext);
2070        }
2071    }
2072
2073    /**
2074     * Called when EVENT_DISCONNECT_DONE is received.
2075     */
2076    @Override
2077    protected void onDisconnectDone(AsyncResult ar) {
2078        ApnContext apnContext = getValidApnContext(ar, "onDisconnectDone");
2079        if (apnContext == null) return;
2080
2081        if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
2082        apnContext.setState(DctConstants.State.IDLE);
2083
2084        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
2085
2086        // if all data connection are gone, check whether Airplane mode request was
2087        // pending.
2088        if (isDisconnected()) {
2089            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
2090                if(DBG) log("onDisconnectDone: radio will be turned off, no retries");
2091                // Radio will be turned off. No need to retry data setup
2092                apnContext.setApnSetting(null);
2093                apnContext.setDataConnectionAc(null);
2094
2095                // Need to notify disconnect as well, in the case of switching Airplane mode.
2096                // Otherwise, it would cause 30s delayed to turn on Airplane mode.
2097                if (mDisconnectPendingCount > 0)
2098                    mDisconnectPendingCount--;
2099
2100                if (mDisconnectPendingCount == 0) {
2101                    notifyDataDisconnectComplete();
2102                    notifyAllDataDisconnected();
2103                }
2104                return;
2105            }
2106        }
2107
2108        // If APN is still enabled, try to bring it back up automatically
2109        if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
2110            SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
2111            // Wait a bit before trying the next APN, so that
2112            // we're not tying up the RIL command channel.
2113            // This also helps in any external dependency to turn off the context.
2114            if(DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
2115            startAlarmForReconnect(getApnDelay(), apnContext);
2116        } else {
2117            boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean(
2118                    com.android.internal.R.bool.config_restartRadioAfterProvisioning);
2119
2120            if (apnContext.isProvisioningApn() && restartRadioAfterProvisioning) {
2121                log("onDisconnectDone: restartRadio after provisioning");
2122                restartRadio();
2123            }
2124            apnContext.setApnSetting(null);
2125            apnContext.setDataConnectionAc(null);
2126            if (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())) {
2127                if(DBG) log("onDisconnectDone: isOnlySigneDcAllowed true so setup single apn");
2128                setupDataOnConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION);
2129            } else {
2130                if(DBG) log("onDisconnectDone: not retrying");
2131            }
2132        }
2133
2134        if (mDisconnectPendingCount > 0)
2135            mDisconnectPendingCount--;
2136
2137        if (mDisconnectPendingCount == 0) {
2138            apnContext.setConcurrentVoiceAndDataAllowed(
2139                    mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed());
2140            notifyDataDisconnectComplete();
2141            notifyAllDataDisconnected();
2142        }
2143
2144    }
2145
2146    /**
2147     * Called when EVENT_DISCONNECT_DC_RETRYING is received.
2148     */
2149    @Override
2150    protected void onDisconnectDcRetrying(AsyncResult ar) {
2151        // We could just do this in DC!!!
2152        ApnContext apnContext = getValidApnContext(ar, "onDisconnectDcRetrying");
2153        if (apnContext == null) return;
2154
2155        apnContext.setState(DctConstants.State.RETRYING);
2156        if(DBG) log("onDisconnectDcRetrying: apnContext=" + apnContext);
2157
2158        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
2159    }
2160
2161
2162    @Override
2163    protected void onVoiceCallStarted() {
2164        if (DBG) log("onVoiceCallStarted");
2165        mInVoiceCall = true;
2166        if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
2167            if (DBG) log("onVoiceCallStarted stop polling");
2168            stopNetStatPoll();
2169            stopDataStallAlarm();
2170            notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
2171        }
2172    }
2173
2174    @Override
2175    protected void onVoiceCallEnded() {
2176        if (DBG) log("onVoiceCallEnded");
2177        mInVoiceCall = false;
2178        if (isConnected()) {
2179            if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
2180                startNetStatPoll();
2181                startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
2182                notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
2183            } else {
2184                // clean slate after call end.
2185                resetPollStats();
2186            }
2187        }
2188        // reset reconnect timer
2189        setupDataOnConnectableApns(Phone.REASON_VOICE_CALL_ENDED);
2190    }
2191
2192    @Override
2193    protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
2194        if (DBG) log("onCleanUpConnection");
2195        ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
2196        if (apnContext != null) {
2197            apnContext.setReason(reason);
2198            cleanUpConnection(tearDown, apnContext);
2199        }
2200    }
2201
2202    @Override
2203    protected boolean isConnected() {
2204        for (ApnContext apnContext : mApnContexts.values()) {
2205            if (apnContext.getState() == DctConstants.State.CONNECTED) {
2206                // At least one context is connected, return true
2207                return true;
2208            }
2209        }
2210        // There are not any contexts connected, return false
2211        return false;
2212    }
2213
2214    @Override
2215    public boolean isDisconnected() {
2216        for (ApnContext apnContext : mApnContexts.values()) {
2217            if (!apnContext.isDisconnected()) {
2218                // At least one context was not disconnected return false
2219                return false;
2220            }
2221        }
2222        // All contexts were disconnected so return true
2223        return true;
2224    }
2225
2226    @Override
2227    protected void notifyDataConnection(String reason) {
2228        if (DBG) log("notifyDataConnection: reason=" + reason);
2229        for (ApnContext apnContext : mApnContexts.values()) {
2230            if (mAttached.get() && apnContext.isReady()) {
2231                if (DBG) log("notifyDataConnection: type:" + apnContext.getApnType());
2232                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
2233                        apnContext.getApnType());
2234            }
2235        }
2236        notifyOffApnsOfAvailability(reason);
2237    }
2238
2239    /**
2240     * Based on the sim operator numeric, create a list for all possible
2241     * Data Connections and setup the preferredApn.
2242     */
2243    private void createAllApnList() {
2244        mAllApnSettings = new ArrayList<ApnSetting>();
2245        IccRecords r = mIccRecords.get();
2246        String operator = (r != null) ? r.getOperatorNumeric() : "";
2247        if (operator != null) {
2248            String selection = "numeric = '" + operator + "'";
2249            String orderBy = "_id";
2250            // query only enabled apn.
2251            // carrier_enabled : 1 means enabled apn, 0 disabled apn.
2252            // selection += " and carrier_enabled = 1";
2253            if (DBG) log("createAllApnList: selection=" + selection);
2254
2255            Cursor cursor = mPhone.getContext().getContentResolver().query(
2256                    Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);
2257
2258            if (cursor != null) {
2259                if (cursor.getCount() > 0) {
2260                    mAllApnSettings = createApnList(cursor);
2261                }
2262                cursor.close();
2263            }
2264        }
2265
2266        addEmergencyApnSetting();
2267
2268        dedupeApnSettings();
2269
2270        if (mAllApnSettings.isEmpty()) {
2271            if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
2272            mPreferredApn = null;
2273            // TODO: What is the right behavior?
2274            //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
2275        } else {
2276            mPreferredApn = getPreferredApn();
2277            if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
2278                mPreferredApn = null;
2279                setPreferredApn(-1);
2280            }
2281            if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
2282        }
2283        if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
2284
2285        setDataProfilesAsNeeded();
2286    }
2287
2288    private void dedupeApnSettings() {
2289        ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();
2290
2291        // coalesce APNs if they are similar enough to prevent
2292        // us from bringing up two data calls with the same interface
2293        int i = 0;
2294        while (i < mAllApnSettings.size() - 1) {
2295            ApnSetting first = mAllApnSettings.get(i);
2296            ApnSetting second = null;
2297            int j = i + 1;
2298            while (j < mAllApnSettings.size()) {
2299                second = mAllApnSettings.get(j);
2300                if (apnsSimilar(first, second)) {
2301                    ApnSetting newApn = mergeApns(first, second);
2302                    mAllApnSettings.set(i, newApn);
2303                    first = newApn;
2304                    mAllApnSettings.remove(j);
2305                } else {
2306                    j++;
2307                }
2308            }
2309            i++;
2310        }
2311    }
2312
2313    //check whether the types of two APN same (even only one type of each APN is same)
2314    private boolean apnTypeSameAny(ApnSetting first, ApnSetting second) {
2315        if(VDBG) {
2316            StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
2317            for(int index1 = 0; index1 < first.types.length; index1++) {
2318                apnType1.append(first.types[index1]);
2319                apnType1.append(",");
2320            }
2321
2322            StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
2323            for(int index1 = 0; index1 < second.types.length; index1++) {
2324                apnType2.append(second.types[index1]);
2325                apnType2.append(",");
2326            }
2327            log("APN1: is " + apnType1);
2328            log("APN2: is " + apnType2);
2329        }
2330
2331        for(int index1 = 0; index1 < first.types.length; index1++) {
2332            for(int index2 = 0; index2 < second.types.length; index2++) {
2333                if(first.types[index1].equals(PhoneConstants.APN_TYPE_ALL) ||
2334                        second.types[index2].equals(PhoneConstants.APN_TYPE_ALL) ||
2335                        first.types[index1].equals(second.types[index2])) {
2336                    if(VDBG)log("apnTypeSameAny: return true");
2337                    return true;
2338                }
2339            }
2340        }
2341
2342        if(VDBG)log("apnTypeSameAny: return false");
2343        return false;
2344    }
2345
2346    // Check if neither mention DUN and are substantially similar
2347    private boolean apnsSimilar(ApnSetting first, ApnSetting second) {
2348        return (first.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
2349                second.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
2350                Objects.equals(first.apn, second.apn) &&
2351                !apnTypeSameAny(first, second) &&
2352                xorEquals(first.proxy, second.proxy) &&
2353                xorEquals(first.port, second.port) &&
2354                first.carrierEnabled == second.carrierEnabled &&
2355                first.bearerBitmask == second.bearerBitmask &&
2356                first.profileId == second.profileId &&
2357                Objects.equals(first.mvnoType, second.mvnoType) &&
2358                Objects.equals(first.mvnoMatchData, second.mvnoMatchData) &&
2359                xorEquals(first.mmsc, second.mmsc) &&
2360                xorEquals(first.mmsProxy, second.mmsProxy) &&
2361                xorEquals(first.mmsPort, second.mmsPort));
2362    }
2363
2364    // equal or one is not specified
2365    private boolean xorEquals(String first, String second) {
2366        return (Objects.equals(first, second) ||
2367                TextUtils.isEmpty(first) ||
2368                TextUtils.isEmpty(second));
2369    }
2370
2371    private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
2372        int id = dest.id;
2373        ArrayList<String> resultTypes = new ArrayList<String>();
2374        resultTypes.addAll(Arrays.asList(dest.types));
2375        for (String srcType : src.types) {
2376            if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
2377            if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
2378        }
2379        String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
2380        String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
2381        String mmsPort = (TextUtils.isEmpty(dest.mmsPort) ? src.mmsPort : dest.mmsPort);
2382        String proxy = (TextUtils.isEmpty(dest.proxy) ? src.proxy : dest.proxy);
2383        String port = (TextUtils.isEmpty(dest.port) ? src.port : dest.port);
2384        String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
2385        String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
2386                dest.roamingProtocol;
2387        int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0) ?
2388                0 : (dest.bearerBitmask | src.bearerBitmask);
2389
2390        return new ApnSetting(id, dest.numeric, dest.carrier, dest.apn,
2391                proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
2392                dest.authType, resultTypes.toArray(new String[0]), protocol,
2393                roamingProtocol, dest.carrierEnabled, 0, bearerBitmask, dest.profileId,
2394                (dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
2395                dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
2396    }
2397
2398    /** Return the DC AsyncChannel for the new data connection */
2399    private DcAsyncChannel createDataConnection() {
2400        if (DBG) log("createDataConnection E");
2401
2402        int id = mUniqueIdGenerator.getAndIncrement();
2403        DataConnection conn = DataConnection.makeDataConnection(mPhone, id,
2404                                                this, mDcTesterFailBringUpAll, mDcc);
2405        mDataConnections.put(id, conn);
2406        DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
2407        int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
2408        if (status == AsyncChannel.STATUS_SUCCESSFUL) {
2409            mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac);
2410        } else {
2411            loge("createDataConnection: Could not connect to dcac=" + dcac + " status=" + status);
2412        }
2413
2414        if (DBG) log("createDataConnection() X id=" + id + " dc=" + conn);
2415        return dcac;
2416    }
2417
2418    private void destroyDataConnections() {
2419        if(mDataConnections != null) {
2420            if (DBG) log("destroyDataConnections: clear mDataConnectionList");
2421            mDataConnections.clear();
2422        } else {
2423            if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
2424        }
2425    }
2426
2427    /**
2428     * Build a list of APNs to be used to create PDP's.
2429     *
2430     * @param requestedApnType
2431     * @return waitingApns list to be used to create PDP
2432     *          error when waitingApns.isEmpty()
2433     */
2434    private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, int radioTech) {
2435        if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
2436        ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
2437
2438        if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
2439            ApnSetting dun = fetchDunApn();
2440            if (dun != null) {
2441                apnList.add(dun);
2442                if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
2443                return apnList;
2444            }
2445        }
2446
2447        IccRecords r = mIccRecords.get();
2448        String operator = (r != null) ? r.getOperatorNumeric() : "";
2449
2450        // This is a workaround for a bug (7305641) where we don't failover to other
2451        // suitable APNs if our preferred APN fails.  On prepaid ATT sims we need to
2452        // failover to a provisioning APN, but once we've used their default data
2453        // connection we are locked to it for life.  This change allows ATT devices
2454        // to say they don't want to use preferred at all.
2455        boolean usePreferred = true;
2456        try {
2457            usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
2458                    internal.R.bool.config_dontPreferApn);
2459        } catch (Resources.NotFoundException e) {
2460            if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
2461            usePreferred = true;
2462        }
2463        if (usePreferred) {
2464            mPreferredApn = getPreferredApn();
2465        }
2466        if (DBG) {
2467            log("buildWaitingApns: usePreferred=" + usePreferred
2468                    + " canSetPreferApn=" + mCanSetPreferApn
2469                    + " mPreferredApn=" + mPreferredApn
2470                    + " operator=" + operator + " radioTech=" + radioTech
2471                    + " IccRecords r=" + r);
2472        }
2473
2474        if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
2475                mPreferredApn.canHandleType(requestedApnType)) {
2476            if (DBG) {
2477                log("buildWaitingApns: Preferred APN:" + operator + ":"
2478                        + mPreferredApn.numeric + ":" + mPreferredApn);
2479            }
2480            if (mPreferredApn.numeric.equals(operator)) {
2481                if (ServiceState.bitmaskHasTech(mPreferredApn.bearerBitmask, radioTech)) {
2482                    apnList.add(mPreferredApn);
2483                    if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
2484                    return apnList;
2485                } else {
2486                    if (DBG) log("buildWaitingApns: no preferred APN");
2487                    setPreferredApn(-1);
2488                    mPreferredApn = null;
2489                }
2490            } else {
2491                if (DBG) log("buildWaitingApns: no preferred APN");
2492                setPreferredApn(-1);
2493                mPreferredApn = null;
2494            }
2495        }
2496        if (mAllApnSettings != null) {
2497            if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
2498            for (ApnSetting apn : mAllApnSettings) {
2499                if (apn.canHandleType(requestedApnType)) {
2500                    if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {
2501                        if (DBG) log("buildWaitingApns: adding apn=" + apn);
2502                        apnList.add(apn);
2503                    } else {
2504                        if (DBG) {
2505                            log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask + " does " +
2506                                    "not include radioTech:" + radioTech);
2507                        }
2508                    }
2509                } else if (DBG) {
2510                    log("buildWaitingApns: couldn't handle requesedApnType="
2511                            + requestedApnType);
2512                }
2513            }
2514        } else {
2515            loge("mAllApnSettings is null!");
2516        }
2517        if (DBG) log("buildWaitingApns: X apnList=" + apnList);
2518        return apnList;
2519    }
2520
2521    private String apnListToString (ArrayList<ApnSetting> apns) {
2522        StringBuilder result = new StringBuilder();
2523        for (int i = 0, size = apns.size(); i < size; i++) {
2524            result.append('[')
2525                  .append(apns.get(i).toString())
2526                  .append(']');
2527        }
2528        return result.toString();
2529    }
2530
2531    private void setPreferredApn(int pos) {
2532        if (!mCanSetPreferApn) {
2533            log("setPreferredApn: X !canSEtPreferApn");
2534            return;
2535        }
2536
2537        String subId = Long.toString(mPhone.getSubId());
2538        Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
2539        log("setPreferredApn: delete");
2540        ContentResolver resolver = mPhone.getContext().getContentResolver();
2541        resolver.delete(uri, null, null);
2542
2543        if (pos >= 0) {
2544            log("setPreferredApn: insert");
2545            ContentValues values = new ContentValues();
2546            values.put(APN_ID, pos);
2547            resolver.insert(uri, values);
2548        }
2549    }
2550
2551    private ApnSetting getPreferredApn() {
2552        if (mAllApnSettings == null || mAllApnSettings.isEmpty()) {
2553            log("getPreferredApn: mAllApnSettings is " + ((mAllApnSettings == null)?"null":"empty"));
2554            return null;
2555        }
2556
2557        String subId = Long.toString(mPhone.getSubId());
2558        Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
2559        Cursor cursor = mPhone.getContext().getContentResolver().query(
2560                uri, new String[] { "_id", "name", "apn" },
2561                null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
2562
2563        if (cursor != null) {
2564            mCanSetPreferApn = true;
2565        } else {
2566            mCanSetPreferApn = false;
2567        }
2568        log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
2569                + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));
2570
2571        if (mCanSetPreferApn && cursor.getCount() > 0) {
2572            int pos;
2573            cursor.moveToFirst();
2574            pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
2575            for(ApnSetting p : mAllApnSettings) {
2576                log("getPreferredApn: apnSetting=" + p);
2577                if (p.id == pos && p.canHandleType(mRequestedApnType)) {
2578                    log("getPreferredApn: X found apnSetting" + p);
2579                    cursor.close();
2580                    return p;
2581                }
2582            }
2583        }
2584
2585        if (cursor != null) {
2586            cursor.close();
2587        }
2588
2589        log("getPreferredApn: X not found");
2590        return null;
2591    }
2592
2593    @Override
2594    public void handleMessage (Message msg) {
2595        if (DBG) log("handleMessage msg=" + msg);
2596
2597        if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
2598            loge("handleMessage: Ignore GSM msgs since GSM phone is inactive");
2599            return;
2600        }
2601
2602        switch (msg.what) {
2603            case DctConstants.EVENT_RECORDS_LOADED:
2604                onRecordsLoaded();
2605                break;
2606
2607            case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
2608                onDataConnectionDetached();
2609                break;
2610
2611            case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
2612                onDataConnectionAttached();
2613                break;
2614
2615            case DctConstants.EVENT_DO_RECOVERY:
2616                doRecovery();
2617                break;
2618
2619            case DctConstants.EVENT_APN_CHANGED:
2620                onApnChanged();
2621                break;
2622
2623            case DctConstants.EVENT_PS_RESTRICT_ENABLED:
2624                /**
2625                 * We don't need to explicitly to tear down the PDP context
2626                 * when PS restricted is enabled. The base band will deactive
2627                 * PDP context and notify us with PDP_CONTEXT_CHANGED.
2628                 * But we should stop the network polling and prevent reset PDP.
2629                 */
2630                if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
2631                stopNetStatPoll();
2632                stopDataStallAlarm();
2633                mIsPsRestricted = true;
2634                break;
2635
2636            case DctConstants.EVENT_PS_RESTRICT_DISABLED:
2637                /**
2638                 * When PS restrict is removed, we need setup PDP connection if
2639                 * PDP connection is down.
2640                 */
2641                if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
2642                mIsPsRestricted  = false;
2643                if (isConnected()) {
2644                    startNetStatPoll();
2645                    startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
2646                } else {
2647                    // TODO: Should all PDN states be checked to fail?
2648                    if (mState == DctConstants.State.FAILED) {
2649                        cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
2650                        mReregisterOnReconnectFailure = false;
2651                    }
2652                    ApnContext apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_DEFAULT);
2653                    if (apnContext != null) {
2654                        apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
2655                        trySetupData(apnContext);
2656                    } else {
2657                        loge("**** Default ApnContext not found ****");
2658                        if (Build.IS_DEBUGGABLE) {
2659                            throw new RuntimeException("Default ApnContext not found");
2660                        }
2661                    }
2662                }
2663                break;
2664
2665            case DctConstants.EVENT_TRY_SETUP_DATA:
2666                if (msg.obj instanceof ApnContext) {
2667                    onTrySetupData((ApnContext)msg.obj);
2668                } else if (msg.obj instanceof String) {
2669                    onTrySetupData((String)msg.obj);
2670                } else {
2671                    loge("EVENT_TRY_SETUP request w/o apnContext or String");
2672                }
2673                break;
2674
2675            case DctConstants.EVENT_CLEAN_UP_CONNECTION:
2676                boolean tearDown = (msg.arg1 == 0) ? false : true;
2677                if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
2678                if (msg.obj instanceof ApnContext) {
2679                    cleanUpConnection(tearDown, (ApnContext)msg.obj);
2680                } else {
2681                    loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context, call super");
2682                    super.handleMessage(msg);
2683                }
2684                break;
2685            case DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE:
2686                boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
2687                onSetInternalDataEnabled(enabled, (Message) msg.obj);
2688                break;
2689
2690            case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS:
2691                Message mCause = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
2692                if ((msg.obj != null) && (msg.obj instanceof String)) {
2693                    mCause.obj = msg.obj;
2694                }
2695                super.handleMessage(mCause);
2696                break;
2697
2698            case DctConstants.EVENT_DATA_RAT_CHANGED:
2699                //May new Network allow setupData, so try it here
2700                setupDataOnConnectableApns(Phone.REASON_NW_TYPE_CHANGED,
2701                        RetryFailures.ONLY_ON_CHANGE);
2702                break;
2703
2704            case DctConstants.CMD_CLEAR_PROVISIONING_SPINNER:
2705                // Check message sender intended to clear the current spinner.
2706                if (mProvisioningSpinner == msg.obj) {
2707                    mProvisioningSpinner.dismiss();
2708                    mProvisioningSpinner = null;
2709                }
2710                break;
2711
2712            default:
2713                // handle the message in the super class DataConnectionTracker
2714                super.handleMessage(msg);
2715                break;
2716        }
2717    }
2718
2719    protected int getApnProfileID(String apnType) {
2720        if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
2721            return RILConstants.DATA_PROFILE_IMS;
2722        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_FOTA)) {
2723            return RILConstants.DATA_PROFILE_FOTA;
2724        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_CBS)) {
2725            return RILConstants.DATA_PROFILE_CBS;
2726        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IA)) {
2727            return RILConstants.DATA_PROFILE_DEFAULT; // DEFAULT for now
2728        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_DUN)) {
2729            return RILConstants.DATA_PROFILE_TETHERED;
2730        } else {
2731            return RILConstants.DATA_PROFILE_DEFAULT;
2732        }
2733    }
2734
2735    private int getCellLocationId() {
2736        int cid = -1;
2737        CellLocation loc = mPhone.getCellLocation();
2738
2739        if (loc != null) {
2740            if (loc instanceof GsmCellLocation) {
2741                cid = ((GsmCellLocation)loc).getCid();
2742            } else if (loc instanceof CdmaCellLocation) {
2743                cid = ((CdmaCellLocation)loc).getBaseStationId();
2744            }
2745        }
2746        return cid;
2747    }
2748
2749    private IccRecords getUiccRecords(int appFamily) {
2750        return mUiccController.getIccRecords(mPhone.getPhoneId(), appFamily);
2751    }
2752
2753
2754    @Override
2755    protected void onUpdateIcc() {
2756        if (mUiccController == null ) {
2757            return;
2758        }
2759
2760        IccRecords newIccRecords = getUiccRecords(UiccController.APP_FAM_3GPP);
2761
2762        IccRecords r = mIccRecords.get();
2763        if (r != newIccRecords) {
2764            if (r != null) {
2765                log("Removing stale icc objects.");
2766                r.unregisterForRecordsLoaded(this);
2767                mIccRecords.set(null);
2768            }
2769            if (newIccRecords != null) {
2770                if (mPhone.getSubId() >= 0) {
2771                    log("New records found.");
2772                    mIccRecords.set(newIccRecords);
2773                    newIccRecords.registerForRecordsLoaded(
2774                            this, DctConstants.EVENT_RECORDS_LOADED, null);
2775                }
2776            } else {
2777                onSimNotReady();
2778            }
2779        }
2780    }
2781
2782    public void update() {
2783        log("update sub = " + mPhone.getSubId());
2784        log("update(): Active DDS, register for all events now!");
2785        registerForAllEvents();
2786        onUpdateIcc();
2787
2788        mUserDataEnabled = getDataEnabled();
2789
2790        if (mPhone instanceof CDMALTEPhone) {
2791            ((CDMALTEPhone)mPhone).updateCurrentCarrierInProvider();
2792        } else if (mPhone instanceof GSMPhone) {
2793            ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
2794        } else {
2795            log("Phone object is not MultiSim. This should not hit!!!!");
2796        }
2797    }
2798
2799    @Override
2800    public void cleanUpAllConnections(String cause) {
2801        cleanUpAllConnections(cause, null);
2802    }
2803
2804    public void updateRecords() {
2805        onUpdateIcc();
2806    }
2807
2808    public void cleanUpAllConnections(String cause, Message disconnectAllCompleteMsg) {
2809        log("cleanUpAllConnections");
2810        if (disconnectAllCompleteMsg != null) {
2811            mDisconnectAllCompleteMsgList.add(disconnectAllCompleteMsg);
2812        }
2813
2814        Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS);
2815        msg.obj = cause;
2816        sendMessage(msg);
2817    }
2818
2819    protected void notifyDataDisconnectComplete() {
2820        log("notifyDataDisconnectComplete");
2821        for (Message m: mDisconnectAllCompleteMsgList) {
2822            m.sendToTarget();
2823        }
2824        mDisconnectAllCompleteMsgList.clear();
2825    }
2826
2827
2828    protected void notifyAllDataDisconnected() {
2829        sEnableFailFastRefCounter = 0;
2830        mFailFast = false;
2831        mAllDataDisconnectedRegistrants.notifyRegistrants();
2832    }
2833
2834    public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
2835        mAllDataDisconnectedRegistrants.addUnique(h, what, obj);
2836
2837        if (isDisconnected()) {
2838            log("notify All Data Disconnected");
2839            notifyAllDataDisconnected();
2840        }
2841    }
2842
2843    public void unregisterForAllDataDisconnected(Handler h) {
2844        mAllDataDisconnectedRegistrants.remove(h);
2845    }
2846
2847
2848    @Override
2849    protected void onSetInternalDataEnabled(boolean enable) {
2850        if (DBG) log("onSetInternalDataEnabled: enabled=" + enable);
2851        onSetInternalDataEnabled(enable, null);
2852    }
2853
2854    protected void onSetInternalDataEnabled(boolean enabled, Message onCompleteMsg) {
2855        if (DBG) log("onSetInternalDataEnabled: enabled=" + enabled);
2856        boolean sendOnComplete = true;
2857
2858        synchronized (mDataEnabledLock) {
2859            mInternalDataEnabled = enabled;
2860            if (enabled) {
2861                log("onSetInternalDataEnabled: changed to enabled, try to setup data call");
2862                onTrySetupData(Phone.REASON_DATA_ENABLED);
2863            } else {
2864                sendOnComplete = false;
2865                log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections");
2866                cleanUpAllConnections(null, onCompleteMsg);
2867            }
2868        }
2869
2870        if (sendOnComplete) {
2871            if (onCompleteMsg != null) {
2872                onCompleteMsg.sendToTarget();
2873            }
2874        }
2875    }
2876
2877    public boolean setInternalDataEnabledFlag(boolean enable) {
2878        if (DBG) log("setInternalDataEnabledFlag(" + enable + ")");
2879
2880        if (mInternalDataEnabled != enable) {
2881            mInternalDataEnabled = enable;
2882        }
2883        return true;
2884    }
2885
2886    @Override
2887    public boolean setInternalDataEnabled(boolean enable) {
2888        return setInternalDataEnabled(enable, null);
2889    }
2890
2891    public boolean setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
2892        if (DBG) log("setInternalDataEnabled(" + enable + ")");
2893
2894        Message msg = obtainMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE, onCompleteMsg);
2895        msg.arg1 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
2896        sendMessage(msg);
2897        return true;
2898    }
2899
2900    public void setDataAllowed(boolean enable, Message response) {
2901         if (DBG) log("setDataAllowed: enable=" + enable);
2902         mIsCleanupRequired = !enable;
2903         mPhone.mCi.setDataAllowed(enable, response);
2904         mInternalDataEnabled = enable;
2905    }
2906
2907    @Override
2908    protected void log(String s) {
2909        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
2910    }
2911
2912    @Override
2913    protected void loge(String s) {
2914        Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
2915    }
2916
2917    @Override
2918    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2919        pw.println("DcTracker extends:");
2920        super.dump(fd, pw, args);
2921        pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
2922        pw.println(" canSetPreferApn=" + mCanSetPreferApn);
2923        pw.println(" mApnObserver=" + mApnObserver);
2924        pw.println(" getOverallState=" + getOverallState());
2925        pw.println(" mDataConnectionAsyncChannels=%s\n" + mDataConnectionAcHashMap);
2926        pw.println(" mAttached=" + mAttached.get());
2927    }
2928
2929    @Override
2930    public String[] getPcscfAddress(String apnType) {
2931        log("getPcscfAddress()");
2932        ApnContext apnContext = null;
2933
2934        if(apnType == null){
2935            log("apnType is null, return null");
2936            return null;
2937        }
2938
2939        if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_EMERGENCY)) {
2940            apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_EMERGENCY);
2941        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
2942            apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_IMS);
2943        } else {
2944            log("apnType is invalid, return null");
2945            return null;
2946        }
2947
2948        if (apnContext == null) {
2949            log("apnContext is null, return null");
2950            return null;
2951        }
2952
2953        DcAsyncChannel dcac = apnContext.getDcAc();
2954        String[] result = null;
2955
2956        if (dcac != null) {
2957            result = dcac.getPcscfAddr();
2958
2959            for (int i = 0; i < result.length; i++) {
2960                log("Pcscf[" + i + "]: " + result[i]);
2961            }
2962            return result;
2963        }
2964        return null;
2965    }
2966
2967    @Override
2968    public void setImsRegistrationState(boolean registered) {
2969        log("setImsRegistrationState - mImsRegistrationState(before): "+ mImsRegistrationState
2970                + ", registered(current) : " + registered);
2971
2972        if (mPhone == null) return;
2973
2974        ServiceStateTracker sst = mPhone.getServiceStateTracker();
2975        if (sst == null) return;
2976
2977        sst.setImsRegistrationState(registered);
2978    }
2979
2980    /**
2981     * Read APN configuration from Telephony.db for Emergency APN
2982     * All opertors recognize the connection request for EPDN based on APN type
2983     * PLMN name,APN name are not mandatory parameters
2984     */
2985    private void initEmergencyApnSetting() {
2986        // Operator Numeric is not available when sim records are not loaded.
2987        // Query Telephony.db with APN type as EPDN request does not
2988        // require APN name, plmn and all operators support same APN config.
2989        // DB will contain only one entry for Emergency APN
2990        String selection = "type=\"emergency\"";
2991        Cursor cursor = mPhone.getContext().getContentResolver().query(
2992                Telephony.Carriers.CONTENT_URI, null, selection, null, null);
2993
2994        if (cursor != null) {
2995            if (cursor.getCount() > 0) {
2996                if (cursor.moveToFirst()) {
2997                    mEmergencyApn = makeApnSetting(cursor);
2998                }
2999            }
3000            cursor.close();
3001        }
3002    }
3003
3004    /**
3005     * Add the Emergency APN settings to APN settings list
3006     */
3007    private void addEmergencyApnSetting() {
3008        if(mEmergencyApn != null) {
3009            if(mAllApnSettings == null) {
3010                mAllApnSettings = new ArrayList<ApnSetting>();
3011            } else {
3012                boolean hasEmergencyApn = false;
3013                for (ApnSetting apn : mAllApnSettings) {
3014                    if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_EMERGENCY)) {
3015                        hasEmergencyApn = true;
3016                        break;
3017                    }
3018                }
3019
3020                if(hasEmergencyApn == false) {
3021                    mAllApnSettings.add(mEmergencyApn);
3022                } else {
3023                    log("addEmergencyApnSetting - E-APN setting is already present");
3024                }
3025            }
3026        }
3027    }
3028
3029    private void cleanUpConnectionsOnUpdatedApns(boolean tearDown) {
3030        if (DBG) log("cleanUpConnectionsOnUpdatedApns: tearDown=" + tearDown);
3031        if (mAllApnSettings.isEmpty()) {
3032            cleanUpAllConnections(tearDown, Phone.REASON_APN_CHANGED);
3033        } else {
3034            for (ApnContext apnContext : mApnContexts.values()) {
3035                if (VDBG) log("cleanUpConnectionsOnUpdatedApns for "+ apnContext);
3036
3037                boolean cleanUpApn = true;
3038                ArrayList<ApnSetting> currentWaitingApns = apnContext.getWaitingApns();
3039
3040                if ((currentWaitingApns != null) && (!apnContext.isDisconnected())) {
3041                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
3042                    ArrayList<ApnSetting> waitingApns = buildWaitingApns(
3043                            apnContext.getApnType(), radioTech);
3044                    if (VDBG) log("new waitingApns:" + waitingApns);
3045                    if (waitingApns.size() == currentWaitingApns.size()) {
3046                        cleanUpApn = false;
3047                        for (int i = 0; i < waitingApns.size(); i++) {
3048                            if (!currentWaitingApns.get(i).equals(waitingApns.get(i))) {
3049                                if (VDBG) log("new waiting apn is different at " + i);
3050                                cleanUpApn = true;
3051                                apnContext.setWaitingApns(waitingApns);
3052                                break;
3053                            }
3054                        }
3055                    }
3056                }
3057
3058                if (cleanUpApn) {
3059                    apnContext.setReason(Phone.REASON_APN_CHANGED);
3060                    cleanUpConnection(true, apnContext);
3061                }
3062            }
3063        }
3064
3065        if (!isConnected()) {
3066            stopNetStatPoll();
3067            stopDataStallAlarm();
3068        }
3069
3070        mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
3071
3072        if (DBG) log("mDisconnectPendingCount = " + mDisconnectPendingCount);
3073        if (tearDown && mDisconnectPendingCount == 0) {
3074            notifyDataDisconnectComplete();
3075            notifyAllDataDisconnected();
3076        }
3077    }
3078}
3079