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