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