DcTracker.java revision 3fad7daba62dcb7aafc4adc7f8cc123726ed5a7c
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.content.ActivityNotFoundException;
22import android.content.ContentResolver;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.res.Resources;
28import android.database.ContentObserver;
29import android.database.Cursor;
30import android.net.ConnectivityManager;
31import android.net.LinkCapabilities;
32import android.net.LinkProperties;
33import android.net.NetworkConfig;
34import android.net.NetworkUtils;
35import android.net.ProxyProperties;
36import android.net.Uri;
37import android.os.AsyncResult;
38import android.os.Build;
39import android.os.Message;
40import android.os.Messenger;
41import android.os.SystemClock;
42import android.os.SystemProperties;
43import android.os.UserHandle;
44import android.provider.Settings;
45import android.provider.Telephony;
46import android.telephony.CellLocation;
47import android.telephony.ServiceState;
48import android.telephony.TelephonyManager;
49import android.telephony.cdma.CdmaCellLocation;
50import android.telephony.gsm.GsmCellLocation;
51import android.text.TextUtils;
52import android.util.EventLog;
53import android.telephony.Rlog;
54
55import com.android.internal.telephony.Phone;
56import com.android.internal.telephony.PhoneBase;
57import com.android.internal.telephony.DctConstants;
58import com.android.internal.telephony.EventLogTags;
59import com.android.internal.telephony.TelephonyIntents;
60import com.android.internal.telephony.gsm.GSMPhone;
61import com.android.internal.telephony.PhoneConstants;
62import com.android.internal.telephony.RILConstants;
63import com.android.internal.telephony.uicc.IccRecords;
64import com.android.internal.telephony.uicc.UiccController;
65import com.android.internal.util.AsyncChannel;
66
67import java.io.FileDescriptor;
68import java.io.PrintWriter;
69import java.util.ArrayList;
70import java.util.concurrent.atomic.AtomicBoolean;
71import java.util.HashMap;
72
73/**
74 * {@hide}
75 */
76public final class DcTracker extends DcTrackerBase {
77    protected final String LOG_TAG = "DCT";
78
79    /**
80     * Handles changes to the APN db.
81     */
82    private class ApnChangeObserver extends ContentObserver {
83        public ApnChangeObserver () {
84            super(mDataConnectionTracker);
85        }
86
87        @Override
88        public void onChange(boolean selfChange) {
89            sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
90        }
91    }
92
93    //***** Instance Variables
94
95    private boolean mReregisterOnReconnectFailure = false;
96
97
98    //***** Constants
99
100    // Used by puppetmaster/*/radio_stress.py
101    private static final String PUPPET_MASTER_RADIO_STRESS_TEST = "gsm.defaultpdpcontext.active";
102
103    private static final int POLL_PDP_MILLIS = 5 * 1000;
104
105    static final Uri PREFERAPN_NO_UPDATE_URI =
106                        Uri.parse("content://telephony/carriers/preferapn_no_update");
107    static final String APN_ID = "apn_id";
108
109    private boolean mCanSetPreferApn = false;
110
111    private AtomicBoolean mAttached = new AtomicBoolean(false);
112
113    /** Watches for changes to the APN db. */
114    private ApnChangeObserver mApnObserver;
115
116    //***** Constructor
117
118    public DcTracker(PhoneBase p) {
119        super(p);
120        if (DBG) log("GsmDCT.constructor");
121        p.mCi.registerForAvailable (this, DctConstants.EVENT_RADIO_AVAILABLE, null);
122        p.mCi.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE,
123                null);
124        p.mCi.registerForDataNetworkStateChanged (this, DctConstants.EVENT_DATA_STATE_CHANGED,
125                null);
126        p.getCallTracker().registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED,
127                null);
128        p.getCallTracker().registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED,
129                null);
130        p.getServiceStateTracker().registerForDataConnectionAttached(this,
131                DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
132        p.getServiceStateTracker().registerForDataConnectionDetached(this,
133                DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
134        p.getServiceStateTracker().registerForRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null);
135        p.getServiceStateTracker().registerForRoamingOff(this, DctConstants.EVENT_ROAMING_OFF,
136                null);
137        p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
138                DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
139        p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
140                DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
141
142        mDataConnectionTracker = this;
143
144        mApnObserver = new ApnChangeObserver();
145        p.getContext().getContentResolver().registerContentObserver(
146                Telephony.Carriers.CONTENT_URI, true, mApnObserver);
147
148        initApnContexts();
149
150        for (ApnContext apnContext : mApnContexts.values()) {
151            // Register the reconnect and restart actions.
152            IntentFilter filter = new IntentFilter();
153            filter.addAction(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
154            filter.addAction(INTENT_RESTART_TRYSETUP_ALARM + '.' + apnContext.getApnType());
155            mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
156        }
157
158        ConnectivityManager cm = (ConnectivityManager)p.getContext().getSystemService(
159                Context.CONNECTIVITY_SERVICE);
160        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE, new Messenger(this));
161        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_MMS, new Messenger(this));
162        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_SUPL, new Messenger(this));
163        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_DUN, new Messenger(this));
164        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_HIPRI, new Messenger(this));
165        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_FOTA, new Messenger(this));
166        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_IMS, new Messenger(this));
167        cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_CBS, new Messenger(this));
168    }
169
170    @Override
171    public void dispose() {
172        if (DBG) log("GsmDCT.dispose");
173        cleanUpAllConnections(true, null);
174
175        super.dispose();
176
177        //Unregister for all events
178        mPhone.mCi.unregisterForAvailable(this);
179        mPhone.mCi.unregisterForOffOrNotAvailable(this);
180        IccRecords r = mIccRecords.get();
181        if (r != null) { r.unregisterForRecordsLoaded(this);}
182        mPhone.mCi.unregisterForDataNetworkStateChanged(this);
183        mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
184        mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
185        mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
186        mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
187        mPhone.getServiceStateTracker().unregisterForRoamingOn(this);
188        mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
189        mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
190        mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
191
192        mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
193        mApnContexts.clear();
194        mPrioritySortedApnContexts.clear();
195
196        destroyDataConnections();
197    }
198
199    @Override
200    public boolean isApnTypeActive(String type) {
201        ApnContext apnContext = mApnContexts.get(type);
202        if (apnContext == null) return false;
203
204        return (apnContext.getDcAc() != null);
205    }
206
207    @Override
208    public boolean isDataPossible(String apnType) {
209        ApnContext apnContext = mApnContexts.get(apnType);
210        if (apnContext == null) {
211            return false;
212        }
213        boolean apnContextIsEnabled = apnContext.isEnabled();
214        DctConstants.State apnContextState = apnContext.getState();
215        boolean apnTypePossible = !(apnContextIsEnabled &&
216                (apnContextState == DctConstants.State.FAILED));
217        boolean dataAllowed = isDataAllowed();
218        boolean possible = dataAllowed && apnTypePossible;
219
220        if (VDBG) {
221            log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
222                    "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
223                    apnType, possible, dataAllowed, apnTypePossible,
224                    apnContextIsEnabled, apnContextState));
225        }
226        return possible;
227    }
228
229    @Override
230    protected void finalize() {
231        if(DBG) log("finalize");
232    }
233
234    private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
235        ApnContext apnContext = new ApnContext(mPhone.getContext(), type, LOG_TAG, networkConfig);
236        mApnContexts.put(type, apnContext);
237        mPrioritySortedApnContexts.add(apnContext);
238        return apnContext;
239    }
240
241    protected void initApnContexts() {
242        log("initApnContexts: E");
243        boolean defaultEnabled = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP, true);
244        // Load device network attributes from resources
245        String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
246                com.android.internal.R.array.networkAttributes);
247        for (String networkConfigString : networkConfigStrings) {
248            NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
249            ApnContext apnContext = null;
250
251            switch (networkConfig.type) {
252            case ConnectivityManager.TYPE_MOBILE:
253                apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT, networkConfig);
254                apnContext.setEnabled(defaultEnabled);
255                break;
256            case ConnectivityManager.TYPE_MOBILE_MMS:
257                apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS, networkConfig);
258                break;
259            case ConnectivityManager.TYPE_MOBILE_SUPL:
260                apnContext = addApnContext(PhoneConstants.APN_TYPE_SUPL, networkConfig);
261                break;
262            case ConnectivityManager.TYPE_MOBILE_DUN:
263                apnContext = addApnContext(PhoneConstants.APN_TYPE_DUN, networkConfig);
264                break;
265            case ConnectivityManager.TYPE_MOBILE_HIPRI:
266                apnContext = addApnContext(PhoneConstants.APN_TYPE_HIPRI, networkConfig);
267                break;
268            case ConnectivityManager.TYPE_MOBILE_FOTA:
269                apnContext = addApnContext(PhoneConstants.APN_TYPE_FOTA, networkConfig);
270                break;
271            case ConnectivityManager.TYPE_MOBILE_IMS:
272                apnContext = addApnContext(PhoneConstants.APN_TYPE_IMS, networkConfig);
273                break;
274            case ConnectivityManager.TYPE_MOBILE_CBS:
275                apnContext = addApnContext(PhoneConstants.APN_TYPE_CBS, networkConfig);
276                break;
277            case ConnectivityManager.TYPE_MOBILE_IA:
278                apnContext = addApnContext(PhoneConstants.APN_TYPE_IA, networkConfig);
279                break;
280            default:
281                log("initApnContexts: skipping unknown type=" + networkConfig.type);
282                continue;
283            }
284            log("initApnContexts: apnContext=" + apnContext);
285        }
286        log("initApnContexts: X mApnContexts=" + mApnContexts);
287    }
288
289    @Override
290    public LinkProperties getLinkProperties(String apnType) {
291        ApnContext apnContext = mApnContexts.get(apnType);
292        if (apnContext != null) {
293            DcAsyncChannel dcac = apnContext.getDcAc();
294            if (dcac != null) {
295                if (DBG) log("return link properites for " + apnType);
296                return dcac.getLinkPropertiesSync();
297            }
298        }
299        if (DBG) log("return new LinkProperties");
300        return new LinkProperties();
301    }
302
303    @Override
304    public LinkCapabilities getLinkCapabilities(String apnType) {
305        ApnContext apnContext = mApnContexts.get(apnType);
306        if (apnContext!=null) {
307            DcAsyncChannel dataConnectionAc = apnContext.getDcAc();
308            if (dataConnectionAc != null) {
309                if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
310                return dataConnectionAc.getLinkCapabilitiesSync();
311            }
312        }
313        if (DBG) log("return new LinkCapabilities");
314        return new LinkCapabilities();
315    }
316
317    @Override
318    // Return all active apn types
319    public String[] getActiveApnTypes() {
320        if (DBG) log("get all active apn types");
321        ArrayList<String> result = new ArrayList<String>();
322
323        for (ApnContext apnContext : mApnContexts.values()) {
324            if (mAttached.get() && apnContext.isReady()) {
325                result.add(apnContext.getApnType());
326            }
327        }
328
329        return result.toArray(new String[0]);
330    }
331
332    @Override
333    // Return active apn of specific apn type
334    public String getActiveApnString(String apnType) {
335        if (VDBG) log( "get active apn string for type:" + apnType);
336        ApnContext apnContext = mApnContexts.get(apnType);
337        if (apnContext != null) {
338            ApnSetting apnSetting = apnContext.getApnSetting();
339            if (apnSetting != null) {
340                return apnSetting.apn;
341            }
342        }
343        return null;
344    }
345
346    @Override
347    public boolean isApnTypeEnabled(String apnType) {
348        ApnContext apnContext = mApnContexts.get(apnType);
349        if (apnContext == null) {
350            return false;
351        }
352        return apnContext.isEnabled();
353    }
354
355    @Override
356    protected void setState(DctConstants.State s) {
357        if (DBG) log("setState should not be used in GSM" + s);
358    }
359
360    // Return state of specific apn type
361    @Override
362    public DctConstants.State getState(String apnType) {
363        ApnContext apnContext = mApnContexts.get(apnType);
364        if (apnContext != null) {
365            return apnContext.getState();
366        }
367        return DctConstants.State.FAILED;
368    }
369
370    // Return if apn type is a provisioning apn.
371    @Override
372    protected boolean isProvisioningApn(String apnType) {
373        ApnContext apnContext = mApnContexts.get(apnType);
374        if (apnContext != null) {
375            return apnContext.isProvisioningApn();
376        }
377        return false;
378    }
379
380    // Return state of overall
381    @Override
382    public DctConstants.State getOverallState() {
383        boolean isConnecting = false;
384        boolean isFailed = true; // All enabled Apns should be FAILED.
385        boolean isAnyEnabled = false;
386
387        for (ApnContext apnContext : mApnContexts.values()) {
388            if (apnContext.isEnabled()) {
389                isAnyEnabled = true;
390                switch (apnContext.getState()) {
391                case CONNECTED:
392                case DISCONNECTING:
393                    if (DBG) log("overall state is CONNECTED");
394                    return DctConstants.State.CONNECTED;
395                case RETRYING:
396                case CONNECTING:
397                    isConnecting = true;
398                    isFailed = false;
399                    break;
400                case IDLE:
401                case SCANNING:
402                    isFailed = false;
403                    break;
404                default:
405                    isAnyEnabled = true;
406                    break;
407                }
408            }
409        }
410
411        if (!isAnyEnabled) { // Nothing enabled. return IDLE.
412            if (DBG) log( "overall state is IDLE");
413            return DctConstants.State.IDLE;
414        }
415
416        if (isConnecting) {
417            if (DBG) log( "overall state is CONNECTING");
418            return DctConstants.State.CONNECTING;
419        } else if (!isFailed) {
420            if (DBG) log( "overall state is IDLE");
421            return DctConstants.State.IDLE;
422        } else {
423            if (DBG) log( "overall state is FAILED");
424            return DctConstants.State.FAILED;
425        }
426    }
427
428    /**
429     * Ensure that we are connected to an APN of the specified type.
430     *
431     * @param apnType the APN type
432     * @return Success is indicated by {@code PhoneConstants.APN_ALREADY_ACTIVE} or
433     *         {@code PhoneConstants.APN_REQUEST_STARTED}. In the latter case, a
434     *         broadcast will be sent by the ConnectivityManager when a
435     *         connection to the APN has been established.
436     */
437    @Override
438    public synchronized int enableApnType(String apnType) {
439        ApnContext apnContext = mApnContexts.get(apnType);
440        if (apnContext == null || !isApnTypeAvailable(apnType)) {
441            if (DBG) log("enableApnType: " + apnType + " is type not available");
442            return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
443        }
444
445        // If already active, return
446        if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")");
447
448        if (apnContext.getState() == DctConstants.State.CONNECTED) {
449            if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE");
450            return PhoneConstants.APN_ALREADY_ACTIVE;
451        }
452        setEnabled(apnTypeToId(apnType), true);
453        if (DBG) {
454            log("enableApnType: new apn request for type " + apnType +
455                    " return APN_REQUEST_STARTED");
456        }
457        return PhoneConstants.APN_REQUEST_STARTED;
458    }
459
460    @Override
461    public synchronized int disableApnType(String type) {
462        if (DBG) log("disableApnType:" + type);
463        ApnContext apnContext = mApnContexts.get(type);
464
465        if (apnContext != null) {
466            setEnabled(apnTypeToId(type), false);
467            if (apnContext.getState() != DctConstants.State.IDLE && apnContext.getState()
468                    != DctConstants.State.FAILED) {
469                if (DBG) log("diableApnType: return APN_REQUEST_STARTED");
470                return PhoneConstants.APN_REQUEST_STARTED;
471            } else {
472                if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE");
473                return PhoneConstants.APN_ALREADY_INACTIVE;
474            }
475
476        } else {
477            if (DBG) {
478                log("disableApnType: no apn context was found, return APN_REQUEST_FAILED");
479            }
480            return PhoneConstants.APN_REQUEST_FAILED;
481        }
482    }
483
484    @Override
485    protected boolean isApnTypeAvailable(String type) {
486        if (type.equals(PhoneConstants.APN_TYPE_DUN) && fetchDunApn() != null) {
487            return true;
488        }
489
490        if (mAllApnSettings != null) {
491            for (ApnSetting apn : mAllApnSettings) {
492                if (apn.canHandleType(type)) {
493                    return true;
494                }
495            }
496        }
497        return false;
498    }
499
500    /**
501     * Report on whether data connectivity is enabled for any APN.
502     * @return {@code false} if data connectivity has been explicitly disabled,
503     * {@code true} otherwise.
504     */
505    @Override
506    public boolean getAnyDataEnabled() {
507        synchronized (mDataEnabledLock) {
508            if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false;
509            for (ApnContext apnContext : mApnContexts.values()) {
510                // Make sure we don't have a context that is going down
511                // and is explicitly disabled.
512                if (isDataAllowed(apnContext)) {
513                    return true;
514                }
515            }
516            return false;
517        }
518    }
519
520    private boolean isDataAllowed(ApnContext apnContext) {
521        return apnContext.isReady() && isDataAllowed();
522    }
523
524    //****** Called from ServiceStateTracker
525    /**
526     * Invoked when ServiceStateTracker observes a transition from GPRS
527     * attach to detach.
528     */
529    protected void onDataConnectionDetached() {
530        /*
531         * We presently believe it is unnecessary to tear down the PDP context
532         * when GPRS detaches, but we should stop the network polling.
533         */
534        if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
535        stopNetStatPoll();
536        stopDataStallAlarm();
537        notifyDataConnection(Phone.REASON_DATA_DETACHED);
538        mAttached.set(false);
539    }
540
541    private void onDataConnectionAttached() {
542        if (DBG) log("onDataConnectionAttached");
543        mAttached.set(true);
544        if (getOverallState() == DctConstants.State.CONNECTED) {
545            if (DBG) log("onDataConnectionAttached: start polling notify attached");
546            startNetStatPoll();
547            startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
548            notifyDataConnection(Phone.REASON_DATA_ATTACHED);
549        } else {
550            // update APN availability so that APN can be enabled.
551            notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
552        }
553        mAutoAttachOnCreation = true;
554        setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
555    }
556
557    @Override
558    protected boolean isDataAllowed() {
559        final boolean internalDataEnabled;
560        synchronized (mDataEnabledLock) {
561            internalDataEnabled = mInternalDataEnabled;
562        }
563
564        boolean attachedState = mAttached.get();
565        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
566        IccRecords r = mIccRecords.get();
567        boolean recordsLoaded = (r != null) ? r.getRecordsLoaded() : false;
568
569        boolean allowed =
570                    (attachedState || mAutoAttachOnCreation) &&
571                    recordsLoaded &&
572                    (mPhone.getState() == PhoneConstants.State.IDLE ||
573                     mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
574                    internalDataEnabled &&
575                    (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
576                    !mIsPsRestricted &&
577                    desiredPowerState;
578        if (!allowed && DBG) {
579            String reason = "";
580            if (!(attachedState || mAutoAttachOnCreation)) {
581                reason += " - Attached= " + attachedState;
582            }
583            if (!recordsLoaded) reason += " - SIM not loaded";
584            if (mPhone.getState() != PhoneConstants.State.IDLE &&
585                    !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
586                reason += " - PhoneState= " + mPhone.getState();
587                reason += " - Concurrent voice and data not allowed";
588            }
589            if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
590            if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
591                reason += " - Roaming and data roaming not enabled";
592            }
593            if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
594            if (!desiredPowerState) reason += " - desiredPowerState= false";
595            if (DBG) log("isDataAllowed: not allowed due to" + reason);
596        }
597        return allowed;
598    }
599
600    private void setupDataOnConnectableApns(String reason) {
601        if (DBG) log("setupDataOnConnectableApns: " + reason);
602
603        for (ApnContext apnContext : mPrioritySortedApnContexts) {
604            if (DBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
605            if (apnContext.getState() == DctConstants.State.FAILED) {
606                apnContext.setState(DctConstants.State.IDLE);
607            }
608            if (apnContext.isConnectable()) {
609                log("setupDataOnConnectableApns: isConnectable() call trySetupData");
610                apnContext.setReason(reason);
611                trySetupData(apnContext);
612            }
613        }
614    }
615
616    private boolean trySetupData(ApnContext apnContext) {
617        if (DBG) {
618            log("trySetupData for type:" + apnContext.getApnType() +
619                    " due to " + apnContext.getReason() + " apnContext=" + apnContext);
620            log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
621        }
622
623        if (mPhone.getSimulatedRadioControl() != null) {
624            // Assume data is connected on the simulator
625            // FIXME  this can be improved
626            apnContext.setState(DctConstants.State.CONNECTED);
627            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
628
629            log("trySetupData: X We're on the simulator; assuming connected retValue=true");
630            return true;
631        }
632
633        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
634
635        if (apnContext.isConnectable() &&
636                isDataAllowed(apnContext) && getAnyDataEnabled() && !isEmergency()) {
637            if (apnContext.getState() == DctConstants.State.FAILED) {
638                if (DBG) log("trySetupData: make a FAILED ApnContext IDLE so its reusable");
639                apnContext.setState(DctConstants.State.IDLE);
640            }
641            int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
642            if (apnContext.getState() == DctConstants.State.IDLE) {
643
644                ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType(),
645                        radioTech);
646                if (waitingApns.isEmpty()) {
647                    notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
648                    notifyOffApnsOfAvailability(apnContext.getReason());
649                    if (DBG) log("trySetupData: X No APN found retValue=false");
650                    return false;
651                } else {
652                    apnContext.setWaitingApns(waitingApns);
653                    if (DBG) {
654                        log ("trySetupData: Create from mAllApnSettings : "
655                                    + apnListToString(mAllApnSettings));
656                    }
657                }
658            }
659
660            if (DBG) {
661                log("trySetupData: call setupData, waitingApns : "
662                        + apnListToString(apnContext.getWaitingApns()));
663            }
664            boolean retValue = setupData(apnContext, radioTech);
665            notifyOffApnsOfAvailability(apnContext.getReason());
666
667            if (DBG) log("trySetupData: X retValue=" + retValue);
668            return retValue;
669        } else {
670            if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
671                    && apnContext.isConnectable()) {
672                mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
673            }
674            notifyOffApnsOfAvailability(apnContext.getReason());
675            if (DBG) log ("trySetupData: X apnContext not 'ready' retValue=false");
676            return false;
677        }
678    }
679
680    @Override
681    // Disabled apn's still need avail/unavail notificiations - send them out
682    protected void notifyOffApnsOfAvailability(String reason) {
683        for (ApnContext apnContext : mApnContexts.values()) {
684            if (!mAttached.get() || !apnContext.isReady()) {
685                if (VDBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
686                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
687                                            apnContext.getApnType(),
688                                            PhoneConstants.DataState.DISCONNECTED);
689            } else {
690                if (VDBG) {
691                    log("notifyOffApnsOfAvailability skipped apn due to attached && isReady " +
692                            apnContext.toString());
693                }
694            }
695        }
696    }
697
698    /**
699     * If tearDown is true, this only tears down a CONNECTED session. Presently,
700     * there is no mechanism for abandoning an CONNECTING session,
701     * but would likely involve cancelling pending async requests or
702     * setting a flag or new state to ignore them when they came in
703     * @param tearDown true if the underlying DataConnection should be
704     * disconnected.
705     * @param reason reason for the clean up.
706     * @return boolean - true if we did cleanup any connections, false if they
707     *                   were already all disconnected.
708     */
709    protected boolean cleanUpAllConnections(boolean tearDown, String reason) {
710        if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
711        boolean didDisconnect = false;
712
713        for (ApnContext apnContext : mApnContexts.values()) {
714            if (apnContext.isDisconnected() == false) didDisconnect = true;
715            // TODO - only do cleanup if not disconnected
716            apnContext.setReason(reason);
717            cleanUpConnection(tearDown, apnContext);
718        }
719
720        stopNetStatPoll();
721        stopDataStallAlarm();
722
723        // TODO: Do we need mRequestedApnType?
724        mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
725        return didDisconnect;
726    }
727
728    /**
729     * Cleanup all connections.
730     *
731     * TODO: Cleanup only a specified connection passed as a parameter.
732     *       Also, make sure when you clean up a conn, if it is last apply
733     *       logic as though it is cleanupAllConnections
734     *
735     * @param cause for the clean up.
736     */
737
738    @Override
739    protected void onCleanUpAllConnections(String cause) {
740        cleanUpAllConnections(true, cause);
741    }
742
743    private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
744
745        if (apnContext == null) {
746            if (DBG) log("cleanUpConnection: apn context is null");
747            return;
748        }
749
750        DcAsyncChannel dcac = apnContext.getDcAc();
751        if (DBG) {
752            log("cleanUpConnection: E tearDown=" + tearDown + " reason=" + apnContext.getReason() +
753                    " apnContext=" + apnContext);
754        }
755        if (tearDown) {
756            if (apnContext.isDisconnected()) {
757                // The request is tearDown and but ApnContext is not connected.
758                // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
759                apnContext.setState(DctConstants.State.IDLE);
760                if (!apnContext.isReady()) {
761                    if (dcac != null) {
762                        dcac.tearDown(apnContext, "", null);
763                    }
764                    apnContext.setDataConnectionAc(null);
765                }
766            } else {
767                // Connection is still there. Try to clean up.
768                if (dcac != null) {
769                    if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
770                        boolean disconnectAll = false;
771                        if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
772                            ApnSetting dunSetting = fetchDunApn();
773                            if (dunSetting != null &&
774                                    dunSetting.equals(apnContext.getApnSetting())) {
775                                if (DBG) log("tearing down dedicated DUN connection");
776                                // we need to tear it down - we brought it up just for dun and
777                                // other people are camped on it and now dun is done.  We need
778                                // to stop using it and let the normal apn list get used to find
779                                // connections for the remaining desired connections
780                                disconnectAll = true;
781                            }
782                        }
783                        if (DBG) {
784                            log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :""));
785                        }
786                        Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);
787                        if (disconnectAll) {
788                            apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
789                        } else {
790                            apnContext.getDcAc()
791                                .tearDown(apnContext, apnContext.getReason(), msg);
792                        }
793                        apnContext.setState(DctConstants.State.DISCONNECTING);
794                    }
795                } else {
796                    // apn is connected but no reference to dcac.
797                    // Should not be happen, but reset the state in case.
798                    apnContext.setState(DctConstants.State.IDLE);
799                    mPhone.notifyDataConnection(apnContext.getReason(),
800                                                apnContext.getApnType());
801                }
802            }
803        } else {
804            // force clean up the data connection.
805            if (dcac != null) dcac.reqReset();
806            apnContext.setState(DctConstants.State.IDLE);
807            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
808            apnContext.setDataConnectionAc(null);
809        }
810
811        // Make sure reconnection alarm is cleaned up if there is no ApnContext
812        // associated to the connection.
813        if (dcac != null) {
814            cancelReconnectAlarm(apnContext);
815        }
816        if (DBG) {
817            log("cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason() +
818                    " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
819        }
820    }
821
822    /**
823     * Cancels the alarm associated with apnContext.
824     *
825     * @param apnContext on which the alarm should be stopped.
826     */
827    private void cancelReconnectAlarm(ApnContext apnContext) {
828        if (apnContext == null) return;
829
830        PendingIntent intent = apnContext.getReconnectIntent();
831
832        if (intent != null) {
833                AlarmManager am =
834                    (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
835                am.cancel(intent);
836                apnContext.setReconnectIntent(null);
837        }
838    }
839
840    /**
841     * @param types comma delimited list of APN types
842     * @return array of APN types
843     */
844    private String[] parseTypes(String types) {
845        String[] result;
846        // If unset, set to DEFAULT.
847        if (types == null || types.equals("")) {
848            result = new String[1];
849            result[0] = PhoneConstants.APN_TYPE_ALL;
850        } else {
851            result = types.split(",");
852        }
853        return result;
854    }
855
856   private boolean imsiMatches(String imsiDB, String imsiSIM) {
857        // Note: imsiDB value has digit number or 'x' character for seperating USIM information
858        // for MVNO operator. And then digit number is matched at same order and 'x' character
859        // could replace by any digit number.
860        // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
861        //     that means first 6 digits, 8th and 9th digit
862        //     should be set in USIM for GG Operator.
863        int len = imsiDB.length();
864        int idxCompare = 0;
865
866        if (len <= 0) return false;
867        if (len > imsiSIM.length()) return false;
868
869        for (int idx=0; idx<len; idx++) {
870            char c = imsiDB.charAt(idx);
871            if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
872                continue;
873            } else {
874                return false;
875            }
876        }
877        return true;
878    }
879
880    private boolean mvnoMatches(IccRecords r, String mvno_type, String mvno_match_data) {
881        if (mvno_type.equalsIgnoreCase("spn")) {
882            if ((r.getServiceProviderName() != null) &&
883                    r.getServiceProviderName().equalsIgnoreCase(mvno_match_data)) {
884                return true;
885            }
886        } else if (mvno_type.equalsIgnoreCase("imsi")) {
887            String imsiSIM = r.getIMSI();
888            if ((imsiSIM != null) && imsiMatches(mvno_match_data, imsiSIM)) {
889                return true;
890            }
891        } else if (mvno_type.equalsIgnoreCase("gid")) {
892            String gid1 = r.getGid1();
893            int mvno_match_data_length = mvno_match_data.length();
894            if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
895                    gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvno_match_data)) {
896                return true;
897            }
898        }
899        return false;
900    }
901
902    private ApnSetting makeApnSetting(Cursor cursor) {
903        String[] types = parseTypes(
904                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
905        ApnSetting apn = new ApnSetting(
906                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
907                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
908                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
909                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
910                NetworkUtils.trimV4AddrZeros(
911                        cursor.getString(
912                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
913                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
914                NetworkUtils.trimV4AddrZeros(
915                        cursor.getString(
916                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
917                NetworkUtils.trimV4AddrZeros(
918                        cursor.getString(
919                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
920                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
921                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
922                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
923                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
924                types,
925                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
926                cursor.getString(cursor.getColumnIndexOrThrow(
927                        Telephony.Carriers.ROAMING_PROTOCOL)),
928                cursor.getInt(cursor.getColumnIndexOrThrow(
929                        Telephony.Carriers.CARRIER_ENABLED)) == 1,
930                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)));
931        return apn;
932    }
933
934    private ArrayList<ApnSetting> createApnList(Cursor cursor) {
935        ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
936        IccRecords r = mIccRecords.get();
937
938        if (cursor.moveToFirst()) {
939            String mvnoType = null;
940            String mvnoMatchData = null;
941            do {
942                String cursorMvnoType = cursor.getString(
943                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE));
944                String cursorMvnoMatchData = cursor.getString(
945                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA));
946                if (mvnoType != null) {
947                    if (mvnoType.equals(cursorMvnoType) &&
948                            mvnoMatchData.equals(cursorMvnoMatchData)) {
949                        result.add(makeApnSetting(cursor));
950                    }
951                } else {
952                    // no mvno match yet
953                    if (mvnoMatches(r, cursorMvnoType, cursorMvnoMatchData)) {
954                        // first match - toss out non-mvno data
955                        result.clear();
956                        mvnoType = cursorMvnoType;
957                        mvnoMatchData = cursorMvnoMatchData;
958                        result.add(makeApnSetting(cursor));
959                    } else {
960                        // add only non-mvno data
961                        if (cursorMvnoType.equals("")) {
962                            result.add(makeApnSetting(cursor));
963                        }
964                    }
965                }
966            } while (cursor.moveToNext());
967        }
968        if (DBG) log("createApnList: X result=" + result);
969        return result;
970    }
971
972    private boolean dataConnectionNotInUse(DcAsyncChannel dcac) {
973        if (DBG) log("dataConnectionNotInUse: check if dcac is inuse dcac=" + dcac);
974        for (ApnContext apnContext : mApnContexts.values()) {
975            if (apnContext.getDcAc() == dcac) {
976                if (DBG) log("dataConnectionNotInUse: in use by apnContext=" + apnContext);
977                return false;
978            }
979        }
980        // TODO: Fix retry handling so free DataConnections have empty apnlists.
981        // Probably move retry handling into DataConnections and reduce complexity
982        // of DCT.
983        if (DBG) log("dataConnectionNotInUse: tearDownAll");
984        dcac.tearDownAll("No connection", null);
985        if (DBG) log("dataConnectionNotInUse: not in use return true");
986        return true;
987    }
988
989    private DcAsyncChannel findFreeDataConnection() {
990        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
991            if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
992                if (DBG) {
993                    log("findFreeDataConnection: found free DataConnection=" +
994                        " dcac=" + dcac);
995                }
996                return dcac;
997            }
998        }
999        log("findFreeDataConnection: NO free DataConnection");
1000        return null;
1001    }
1002
1003    private boolean setupData(ApnContext apnContext, int radioTech) {
1004        if (DBG) log("setupData: apnContext=" + apnContext);
1005        ApnSetting apnSetting;
1006        DcAsyncChannel dcac;
1007
1008        int profileId = getApnProfileID(apnContext.getApnType());
1009        apnSetting = apnContext.getNextWaitingApn();
1010        if (apnSetting == null) {
1011            if (DBG) log("setupData: return for no apn found!");
1012            return false;
1013        }
1014
1015        dcac = checkForCompatibleConnectedApnContext(apnContext);
1016        if (dcac != null) {
1017            // Get the dcacApnSetting for the connection we want to share.
1018            ApnSetting dcacApnSetting = dcac.getApnSettingSync();
1019            if (dcacApnSetting != null) {
1020                // Setting is good, so use it.
1021                apnSetting = dcacApnSetting;
1022            }
1023        }
1024        if (dcac == null) {
1025            if (isOnlySingleDcAllowed(radioTech)) {
1026                if (isHigherPriorityApnContextActive(apnContext)) {
1027                    if (DBG) {
1028                        log("setupData: Higher priority ApnContext active.  Ignoring call");
1029                    }
1030                    return false;
1031                }
1032
1033                // Only lower priority calls left.  Disconnect them all in this single PDP case
1034                // so that we can bring up the requested higher priority call (once we receive
1035                // repsonse for deactivate request for the calls we are about to disconnect
1036                if (cleanUpAllConnections(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
1037                    // If any call actually requested to be disconnected, means we can't
1038                    // bring up this connection yet as we need to wait for those data calls
1039                    // to be disconnected.
1040                    if (DBG) log("setupData: Some calls are disconnecting first.  Wait and retry");
1041                    return false;
1042                }
1043
1044                // No other calls are active, so proceed
1045                if (DBG) log("setupData: Single pdp. Continue setting up data call.");
1046            }
1047
1048            dcac = findFreeDataConnection();
1049
1050            if (dcac == null) {
1051                dcac = createDataConnection();
1052            }
1053
1054            if (dcac == null) {
1055                if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
1056                return false;
1057            }
1058        }
1059        if (DBG) log("setupData: dcac=" + dcac + " apnSetting=" + apnSetting);
1060
1061        apnContext.setDataConnectionAc(dcac);
1062        apnContext.setApnSetting(apnSetting);
1063        apnContext.setState(DctConstants.State.CONNECTING);
1064        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1065
1066        Message msg = obtainMessage();
1067        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
1068        msg.obj = apnContext;
1069        dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, msg);
1070
1071        if (DBG) log("setupData: initing!");
1072        return true;
1073    }
1074
1075    /**
1076     * Handles changes to the APN database.
1077     */
1078    private void onApnChanged() {
1079        DctConstants.State overallState = getOverallState();
1080        boolean isDisconnected = (overallState == DctConstants.State.IDLE ||
1081                overallState == DctConstants.State.FAILED);
1082
1083        if (mPhone instanceof GSMPhone) {
1084            // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
1085            ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
1086        }
1087
1088        // TODO: It'd be nice to only do this if the changed entrie(s)
1089        // match the current operator.
1090        if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
1091        createAllApnList();
1092        setInitialAttachApn();
1093        cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);
1094        if (isDisconnected) {
1095            setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
1096        }
1097    }
1098
1099    /**
1100     * @param cid Connection id provided from RIL.
1101     * @return DataConnectionAc associated with specified cid.
1102     */
1103    private DcAsyncChannel findDataConnectionAcByCid(int cid) {
1104        for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
1105            if (dcac.getCidSync() == cid) {
1106                return dcac;
1107            }
1108        }
1109        return null;
1110    }
1111
1112    /**
1113     * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
1114     * or RIL_UNSOL_DATA_CALL_LIST_CHANGED
1115     */
1116    private void onDataStateChanged (AsyncResult ar) {
1117        ArrayList<DataCallResponse> dataCallStates;
1118
1119        if (DBG) log("onDataStateChanged(ar): E");
1120        dataCallStates = (ArrayList<DataCallResponse>)(ar.result);
1121
1122        if (ar.exception != null) {
1123            // This is probably "radio not available" or something
1124            // of that sort. If so, the whole connection is going
1125            // to come down soon anyway
1126            if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
1127            return;
1128        }
1129        if (DBG) log("onDataStateChanged(ar): DataCallResponse size=" + dataCallStates.size());
1130
1131        // Create a hash map to store the dataCallState of each DataConnectionAc
1132        HashMap<DataCallResponse, DcAsyncChannel> dataCallStateToDcac;
1133        dataCallStateToDcac = new HashMap<DataCallResponse, DcAsyncChannel>();
1134        for (DataCallResponse dataCallState : dataCallStates) {
1135            DcAsyncChannel dcac = findDataConnectionAcByCid(dataCallState.cid);
1136
1137            if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac);
1138        }
1139
1140        // Check if we should start or stop polling, by looking
1141        // for dormant and active connections.
1142        boolean isAnyDataCallDormant = false;
1143        boolean isAnyDataCallActive = false;
1144        for (DataCallResponse newState : dataCallStates) {
1145            if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) isAnyDataCallActive = true;
1146            if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DOWN) isAnyDataCallDormant = true;
1147        }
1148
1149        if (isAnyDataCallDormant && !isAnyDataCallActive) {
1150            // There is no way to indicate link activity per APN right now. So
1151            // Link Activity will be considered dormant only when all data calls
1152            // are dormant.
1153            // If a single data call is in dormant state and none of the data
1154            // calls are active broadcast overall link state as dormant.
1155            mActivity = DctConstants.Activity.DORMANT;
1156            if (DBG) {
1157                log("onDataStateChanged: Data Activity updated to DORMANT. stopNetStatePoll");
1158            }
1159            stopNetStatPoll();
1160        } else {
1161            mActivity = DctConstants.Activity.NONE;
1162            if (DBG) {
1163                log("onDataStateChanged: Data Activity updated to NONE. " +
1164                         "isAnyDataCallActive = " + isAnyDataCallActive +
1165                         " isAnyDataCallDormant = " + isAnyDataCallDormant);
1166            }
1167            if (isAnyDataCallActive) startNetStatPoll();
1168        }
1169
1170        if (DBG) log("onDataStateChanged(ar): X");
1171    }
1172
1173    // TODO: For multiple Active APNs not exactly sure how to do this.
1174    @Override
1175    protected void gotoIdleAndNotifyDataConnection(String reason) {
1176        if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
1177        notifyDataConnection(reason);
1178        mActiveApn = null;
1179    }
1180
1181    /**
1182     * "Active" here means ApnContext isEnabled() and not in FAILED state
1183     * @param apnContext to compare with
1184     * @return true if higher priority active apn found
1185     */
1186    private boolean isHigherPriorityApnContextActive(ApnContext apnContext) {
1187        for (ApnContext otherContext : mPrioritySortedApnContexts) {
1188            if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
1189            if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) {
1190                return true;
1191            }
1192        }
1193        return false;
1194    }
1195
1196    /**
1197     * Reports if we support multiple connections or not.
1198     * This is a combination of factors, based on carrier and RAT.
1199     * @param rilRadioTech the RIL Radio Tech currently in use
1200     * @return true if only single DataConnection is allowed
1201     */
1202    private boolean isOnlySingleDcAllowed(int rilRadioTech) {
1203        int[] singleDcRats = mPhone.getContext().getResources().getIntArray(
1204                com.android.internal.R.array.config_onlySingleDcAllowed);
1205        boolean onlySingleDcAllowed = false;
1206        if (Build.IS_DEBUGGABLE &&
1207                SystemProperties.getBoolean("persist.telephony.test.singleDc", false)) {
1208            onlySingleDcAllowed = true;
1209        }
1210        if (singleDcRats != null) {
1211            for (int i=0; i < singleDcRats.length && onlySingleDcAllowed == false; i++) {
1212                if (rilRadioTech == singleDcRats[i]) onlySingleDcAllowed = true;
1213            }
1214        }
1215
1216        if (DBG) log("isOnlySingleDcAllowed(" + rilRadioTech + "): " + onlySingleDcAllowed);
1217        return onlySingleDcAllowed;
1218    }
1219
1220    @Override
1221    protected void restartRadio() {
1222        if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
1223        cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
1224        mPhone.getServiceStateTracker().powerOffRadioSafely(this);
1225        /* Note: no need to call setRadioPower(true).  Assuming the desired
1226         * radio power state is still ON (as tracked by ServiceStateTracker),
1227         * ServiceStateTracker will call setRadioPower when it receives the
1228         * RADIO_STATE_CHANGED notification for the power off.  And if the
1229         * desired power state has changed in the interim, we don't want to
1230         * override it with an unconditional power on.
1231         */
1232
1233        int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
1234        SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
1235    }
1236
1237    /**
1238     * Return true if data connection need to be setup after disconnected due to
1239     * reason.
1240     *
1241     * @param reason the reason why data is disconnected
1242     * @return true if try setup data connection is need for this reason
1243     */
1244    private boolean retryAfterDisconnected(ApnContext apnContext) {
1245        boolean retry = true;
1246        String reason = apnContext.getReason();
1247
1248        if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ||
1249                (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())
1250                 && isHigherPriorityApnContextActive(apnContext))) {
1251            retry = false;
1252        }
1253        return retry;
1254    }
1255
1256    private void startAlarmForReconnect(int delay, ApnContext apnContext) {
1257        String apnType = apnContext.getApnType();
1258
1259        Intent intent = new Intent(INTENT_RECONNECT_ALARM + "." + apnType);
1260        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
1261        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnType);
1262
1263        if (DBG) {
1264            log("startAlarmForReconnect: delay=" + delay + " action=" + intent.getAction()
1265                    + " apn=" + apnContext);
1266        }
1267
1268        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1269                                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
1270        apnContext.setReconnectIntent(alarmIntent);
1271        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1272                SystemClock.elapsedRealtime() + delay, alarmIntent);
1273    }
1274
1275    private void startAlarmForRestartTrySetup(int delay, ApnContext apnContext) {
1276        String apnType = apnContext.getApnType();
1277        Intent intent = new Intent(INTENT_RESTART_TRYSETUP_ALARM + "." + apnType);
1278        intent.putExtra(INTENT_RESTART_TRYSETUP_ALARM_EXTRA_TYPE, apnType);
1279
1280        if (DBG) {
1281            log("startAlarmForRestartTrySetup: delay=" + delay + " action=" + intent.getAction()
1282                    + " apn=" + apnContext);
1283        }
1284        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1285                                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
1286        apnContext.setReconnectIntent(alarmIntent);
1287        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1288                SystemClock.elapsedRealtime() + delay, alarmIntent);
1289    }
1290
1291    private void notifyNoData(DcFailCause lastFailCauseCode,
1292                              ApnContext apnContext) {
1293        if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
1294        if (lastFailCauseCode.isPermanentFail()
1295            && (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT))) {
1296            mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1297        }
1298    }
1299
1300    private void onRecordsLoaded() {
1301        if (DBG) log("onRecordsLoaded: createAllApnList");
1302        createAllApnList();
1303        setInitialAttachApn();
1304        if (mPhone.mCi.getRadioState().isOn()) {
1305            if (DBG) log("onRecordsLoaded: notifying data availability");
1306            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
1307        }
1308        setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
1309    }
1310
1311    @Override
1312    protected void onSetDependencyMet(String apnType, boolean met) {
1313        // don't allow users to tweak hipri to work around default dependency not met
1314        if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
1315
1316        ApnContext apnContext = mApnContexts.get(apnType);
1317        if (apnContext == null) {
1318            loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
1319                    apnType + ", " + met + ")");
1320            return;
1321        }
1322        applyNewState(apnContext, apnContext.isEnabled(), met);
1323        if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
1324            // tie actions on default to similar actions on HIPRI regarding dependencyMet
1325            apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
1326            if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
1327        }
1328    }
1329
1330    private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
1331        boolean cleanup = false;
1332        boolean trySetup = false;
1333        if (DBG) {
1334            log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
1335                    "(" + apnContext.isEnabled() + "), " + met + "(" +
1336                    apnContext.getDependencyMet() +"))");
1337        }
1338        if (apnContext.isReady()) {
1339            if (enabled && met) {
1340                DctConstants.State state = apnContext.getState();
1341                switch(state) {
1342                    case CONNECTING:
1343                    case SCANNING:
1344                    case CONNECTED:
1345                    case DISCONNECTING:
1346                        // We're "READY" and active so just return
1347                        if (DBG) log("applyNewState: 'ready' so return");
1348                        return;
1349                    case IDLE:
1350                        // fall through: this is unexpected but if it happens cleanup and try setup
1351                    case FAILED:
1352                    case RETRYING: {
1353                        // We're "READY" but not active so disconnect (cleanup = true) and
1354                        // connect (trySetup = true) to be sure we retry the connection.
1355                        trySetup = true;
1356                        apnContext.setReason(Phone.REASON_DATA_ENABLED);
1357                        break;
1358                    }
1359                }
1360            } else if (!enabled) {
1361                apnContext.setReason(Phone.REASON_DATA_DISABLED);
1362            } else {
1363                apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
1364            }
1365            cleanup = true;
1366        } else {
1367            if (enabled && met) {
1368                if (apnContext.isEnabled()) {
1369                    apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
1370                } else {
1371                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
1372                }
1373                if (apnContext.getState() == DctConstants.State.FAILED) {
1374                    apnContext.setState(DctConstants.State.IDLE);
1375                }
1376                trySetup = true;
1377            }
1378        }
1379        apnContext.setEnabled(enabled);
1380        apnContext.setDependencyMet(met);
1381        if (cleanup) cleanUpConnection(true, apnContext);
1382        if (trySetup) trySetupData(apnContext);
1383    }
1384
1385    private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
1386        String apnType = apnContext.getApnType();
1387        ApnSetting dunSetting = null;
1388
1389        if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
1390            dunSetting = fetchDunApn();
1391        }
1392        if (DBG) {
1393            log("checkForCompatibleConnectedApnContext: apnContext=" + apnContext );
1394        }
1395
1396        DcAsyncChannel potentialDcac = null;
1397        ApnContext potentialApnCtx = null;
1398        for (ApnContext curApnCtx : mApnContexts.values()) {
1399            DcAsyncChannel curDcac = curApnCtx.getDcAc();
1400            if (curDcac != null) {
1401                ApnSetting apnSetting = curApnCtx.getApnSetting();
1402                if (dunSetting != null) {
1403                    if (dunSetting.equals(apnSetting)) {
1404                        switch (curApnCtx.getState()) {
1405                            case CONNECTED:
1406                                if (DBG) {
1407                                    log("checkForCompatibleConnectedApnContext:"
1408                                            + " found dun conn=" + curDcac
1409                                            + " curApnCtx=" + curApnCtx);
1410                                }
1411                                return curDcac;
1412                            case RETRYING:
1413                            case CONNECTING:
1414                                potentialDcac = curDcac;
1415                                potentialApnCtx = curApnCtx;
1416                            default:
1417                                // Not connected, potential unchanged
1418                                break;
1419                        }
1420                    }
1421                } else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
1422                    switch (curApnCtx.getState()) {
1423                        case CONNECTED:
1424                            if (DBG) {
1425                                log("checkForCompatibleConnectedApnContext:"
1426                                        + " found canHandle conn=" + curDcac
1427                                        + " curApnCtx=" + curApnCtx);
1428                            }
1429                            return curDcac;
1430                        case RETRYING:
1431                        case CONNECTING:
1432                            potentialDcac = curDcac;
1433                            potentialApnCtx = curApnCtx;
1434                        default:
1435                            // Not connected, potential unchanged
1436                            break;
1437                    }
1438                }
1439            } else {
1440                if (VDBG) {
1441                    log("checkForCompatibleConnectedApnContext: not conn curApnCtx=" + curApnCtx);
1442                }
1443            }
1444        }
1445        if (potentialDcac != null) {
1446            if (DBG) {
1447                log("checkForCompatibleConnectedApnContext: found potential conn=" + potentialDcac
1448                        + " curApnCtx=" + potentialApnCtx);
1449            }
1450            return potentialDcac;
1451        }
1452
1453        if (DBG) log("checkForCompatibleConnectedApnContext: NO conn apnContext=" + apnContext);
1454        return null;
1455    }
1456
1457    @Override
1458    protected void onEnableApn(int apnId, int enabled) {
1459        ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1460        if (apnContext == null) {
1461            loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
1462            return;
1463        }
1464        // TODO change our retry manager to use the appropriate numbers for the new APN
1465        if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
1466        applyNewState(apnContext, enabled == DctConstants.ENABLED, apnContext.getDependencyMet());
1467    }
1468
1469    @Override
1470    // TODO: We shouldnt need this.
1471    protected boolean onTrySetupData(String reason) {
1472        if (DBG) log("onTrySetupData: reason=" + reason);
1473        setupDataOnConnectableApns(reason);
1474        return true;
1475    }
1476
1477    protected boolean onTrySetupData(ApnContext apnContext) {
1478        if (DBG) log("onTrySetupData: apnContext=" + apnContext);
1479        return trySetupData(apnContext);
1480    }
1481
1482    @Override
1483    protected void onRoamingOff() {
1484        if (DBG) log("onRoamingOff");
1485
1486        if (mUserDataEnabled == false) return;
1487
1488        if (getDataOnRoamingEnabled() == false) {
1489            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
1490            setupDataOnConnectableApns(Phone.REASON_ROAMING_OFF);
1491        } else {
1492            notifyDataConnection(Phone.REASON_ROAMING_OFF);
1493        }
1494    }
1495
1496    @Override
1497    protected void onRoamingOn() {
1498        if (mUserDataEnabled == false) return;
1499
1500        if (getDataOnRoamingEnabled()) {
1501            if (DBG) log("onRoamingOn: setup data on roaming");
1502            setupDataOnConnectableApns(Phone.REASON_ROAMING_ON);
1503            notifyDataConnection(Phone.REASON_ROAMING_ON);
1504        } else {
1505            if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
1506            cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
1507            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
1508        }
1509    }
1510
1511    @Override
1512    protected void onRadioAvailable() {
1513        if (DBG) log("onRadioAvailable");
1514        if (mPhone.getSimulatedRadioControl() != null) {
1515            // Assume data is connected on the simulator
1516            // FIXME  this can be improved
1517            // setState(DctConstants.State.CONNECTED);
1518            notifyDataConnection(null);
1519
1520            log("onRadioAvailable: We're on the simulator; assuming data is connected");
1521        }
1522
1523        IccRecords r = mIccRecords.get();
1524        if (r != null && r.getRecordsLoaded()) {
1525            notifyOffApnsOfAvailability(null);
1526        }
1527
1528        if (getOverallState() != DctConstants.State.IDLE) {
1529            cleanUpConnection(true, null);
1530        }
1531    }
1532
1533    @Override
1534    protected void onRadioOffOrNotAvailable() {
1535        // Make sure our reconnect delay starts at the initial value
1536        // next time the radio comes on
1537
1538        mReregisterOnReconnectFailure = false;
1539
1540        if (mPhone.getSimulatedRadioControl() != null) {
1541            // Assume data is connected on the simulator
1542            // FIXME  this can be improved
1543            log("We're on the simulator; assuming radio off is meaningless");
1544        } else {
1545            if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
1546            cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
1547        }
1548        notifyOffApnsOfAvailability(null);
1549    }
1550
1551    @Override
1552    protected void completeConnection(ApnContext apnContext) {
1553        boolean isProvApn = apnContext.isProvisioningApn();
1554
1555        if (DBG) log("completeConnection: successful, notify the world apnContext=" + apnContext);
1556
1557        if (mIsProvisioning && !TextUtils.isEmpty(mProvisioningUrl)) {
1558            if (DBG) {
1559                log("completeConnection: MOBILE_PROVISIONING_ACTION url="
1560                        + mProvisioningUrl);
1561            }
1562            Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
1563                    Intent.CATEGORY_APP_BROWSER);
1564            newIntent.setData(Uri.parse(mProvisioningUrl));
1565            newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
1566                    Intent.FLAG_ACTIVITY_NEW_TASK);
1567            try {
1568                mPhone.getContext().startActivity(newIntent);
1569            } catch (ActivityNotFoundException e) {
1570                loge("completeConnection: startActivityAsUser failed" + e);
1571            }
1572        }
1573        mIsProvisioning = false;
1574        mProvisioningUrl = null;
1575
1576        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1577        startNetStatPoll();
1578        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1579    }
1580
1581    /**
1582     * A SETUP (aka bringUp) has completed, possibly with an error. If
1583     * there is an error this method will call {@link #onDataSetupCompleteError}.
1584     */
1585    @Override
1586    protected void onDataSetupComplete(AsyncResult ar) {
1587
1588        DcFailCause cause = DcFailCause.UNKNOWN;
1589        boolean handleError = false;
1590        ApnContext apnContext = null;
1591
1592        if(ar.userObj instanceof ApnContext){
1593            apnContext = (ApnContext)ar.userObj;
1594        } else {
1595            throw new RuntimeException("onDataSetupComplete: No apnContext");
1596        }
1597
1598        if (ar.exception == null) {
1599            DcAsyncChannel dcac = apnContext.getDcAc();
1600
1601            if (RADIO_TESTS) {
1602                // Note: To change radio.test.onDSC.null.dcac from command line you need to
1603                // adb root and adb remount and from the command line you can only change the
1604                // value to 1 once. To change it a second time you can reboot or execute
1605                // adb shell stop and then adb shell start. The command line to set the value is:
1606                // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');"
1607                ContentResolver cr = mPhone.getContext().getContentResolver();
1608                String radioTestProperty = "radio.test.onDSC.null.dcac";
1609                if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) {
1610                    log("onDataSetupComplete: " + radioTestProperty +
1611                            " is true, set dcac to null and reset property to false");
1612                    dcac = null;
1613                    Settings.System.putInt(cr, radioTestProperty, 0);
1614                    log("onDataSetupComplete: " + radioTestProperty + "=" +
1615                            Settings.System.getInt(mPhone.getContext().getContentResolver(),
1616                                    radioTestProperty, -1));
1617                }
1618            }
1619            if (dcac == null) {
1620                log("onDataSetupComplete: no connection to DC, handle as error");
1621                cause = DcFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
1622                handleError = true;
1623            } else {
1624                ApnSetting apn = apnContext.getApnSetting();
1625                if (DBG) {
1626                    log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
1627                }
1628                if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
1629                    try {
1630                        String port = apn.port;
1631                        if (TextUtils.isEmpty(port)) port = "8080";
1632                        ProxyProperties proxy = new ProxyProperties(apn.proxy,
1633                                Integer.parseInt(port), null);
1634                        dcac.setLinkPropertiesHttpProxySync(proxy);
1635                    } catch (NumberFormatException e) {
1636                        loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
1637                                apn.port + "): " + e);
1638                    }
1639                }
1640
1641                // everything is setup
1642                if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
1643                    SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");
1644                    if (mCanSetPreferApn && mPreferredApn == null) {
1645                        if (DBG) log("onDataSetupComplete: PREFERED APN is null");
1646                        mPreferredApn = apn;
1647                        if (mPreferredApn != null) {
1648                            setPreferredApn(mPreferredApn.id);
1649                        }
1650                    }
1651                } else {
1652                    SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
1653                }
1654
1655                // A connection is setup
1656                apnContext.setState(DctConstants.State.CONNECTED);
1657                boolean isProvApn = apnContext.isProvisioningApn();
1658                if ((!isProvApn) || mIsProvisioning) {
1659                    // Complete the connection normally notifying the world we're connected.
1660                    // We do this if this isn't a special provisioning apn or if we've been
1661                    // told its time to provision.
1662                    completeConnection(apnContext);
1663                } else {
1664                    // This is a provisioning APN that we're reporting as connected. Later
1665                    // when the user desires to upgrade this to a "default" connection,
1666                    // mIsProvisioning == true, we'll go through the code path above.
1667                    // mIsProvisioning becomes true when CMD_ENABLE_MOBILE_PROVISIONING
1668                    // is sent to the DCT.
1669                    if (DBG) {
1670                        log("onDataSetupComplete: successful, BUT send connected to prov apn as"
1671                                + " mIsProvisioning:" + mIsProvisioning + " == false"
1672                                + " && (isProvisioningApn:" + isProvApn + " == true");
1673                    }
1674
1675                    Intent intent = new Intent(
1676                            TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
1677                    intent.putExtra(PhoneConstants.DATA_APN_KEY, apnContext.getApnSetting().apn);
1678                    intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnContext.getApnType());
1679
1680                    String apnType = apnContext.getApnType();
1681                    LinkProperties linkProperties = getLinkProperties(apnType);
1682                    if (linkProperties != null) {
1683                        intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
1684                        String iface = linkProperties.getInterfaceName();
1685                        if (iface != null) {
1686                            intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface);
1687                        }
1688                    }
1689                    LinkCapabilities linkCapabilities = getLinkCapabilities(apnType);
1690                    if (linkCapabilities != null) {
1691                        intent.putExtra(PhoneConstants.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
1692                    }
1693
1694                    mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
1695                }
1696                if (DBG) {
1697                    log("onDataSetupComplete: SETUP complete type=" + apnContext.getApnType()
1698                        + ", reason:" + apnContext.getReason());
1699                }
1700            }
1701        } else {
1702            cause = (DcFailCause) (ar.result);
1703            if (DBG) {
1704                ApnSetting apn = apnContext.getApnSetting();
1705                log(String.format("onDataSetupComplete: error apn=%s cause=%s",
1706                        (apn == null ? "unknown" : apn.apn), cause));
1707            }
1708            if (cause.isEventLoggable()) {
1709                // Log this failure to the Event Logs.
1710                int cid = getCellLocationId();
1711                EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1712                        cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType());
1713            }
1714
1715            // Count permanent failures and remove the APN we just tried
1716            if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
1717
1718            apnContext.removeWaitingApn(apnContext.getApnSetting());
1719            if (DBG) {
1720                log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
1721                        " WaitingApnsPermFailureCountDown=%d",
1722                        apnContext.getWaitingApns().size(),
1723                        apnContext.getWaitingApnsPermFailCount()));
1724            }
1725            handleError = true;
1726        }
1727
1728        if (handleError) {
1729            onDataSetupCompleteError(ar);
1730        }
1731    }
1732
1733    /**
1734     * @return number of milli-seconds to delay between trying apns'
1735     */
1736    private int getApnDelay() {
1737        if (mFailFast) {
1738            return SystemProperties.getInt("persist.radio.apn_ff_delay",
1739                    APN_FAIL_FAST_DELAY_DEFAULT_MILLIS);
1740        } else {
1741            return SystemProperties.getInt("persist.radio.apn_delay", APN_DELAY_DEFAULT_MILLIS);
1742        }
1743    }
1744
1745    /**
1746     * Error has occurred during the SETUP {aka bringUP} request and the DCT
1747     * should either try the next waiting APN or start over from the
1748     * beginning if the list is empty. Between each SETUP request there will
1749     * be a delay defined by {@link #getApnDelay()}.
1750     */
1751    @Override
1752    protected void onDataSetupCompleteError(AsyncResult ar) {
1753        String reason = "";
1754        ApnContext apnContext = null;
1755
1756        if(ar.userObj instanceof ApnContext){
1757            apnContext = (ApnContext)ar.userObj;
1758        } else {
1759            throw new RuntimeException("onDataSetupCompleteError: No apnContext");
1760        }
1761
1762        // See if there are more APN's to try
1763        if (apnContext.getWaitingApns().isEmpty()) {
1764            apnContext.setState(DctConstants.State.FAILED);
1765            mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
1766
1767            apnContext.setDataConnectionAc(null);
1768
1769            if (apnContext.getWaitingApnsPermFailCount() == 0) {
1770                if (DBG) {
1771                    log("onDataSetupCompleteError: All APN's had permanent failures, stop retrying");
1772                }
1773            } else {
1774                int delay = getApnDelay();
1775                if (DBG) {
1776                    log("onDataSetupCompleteError: Not all APN's had permanent failures delay="
1777                            + delay);
1778                }
1779                startAlarmForRestartTrySetup(delay, apnContext);
1780            }
1781        } else {
1782            if (DBG) log("onDataSetupCompleteError: Try next APN");
1783            apnContext.setState(DctConstants.State.SCANNING);
1784            // Wait a bit before trying the next APN, so that
1785            // we're not tying up the RIL command channel
1786            startAlarmForReconnect(getApnDelay(), apnContext);
1787        }
1788    }
1789
1790    /**
1791     * Called when EVENT_DISCONNECT_DONE is received.
1792     */
1793    @Override
1794    protected void onDisconnectDone(int connId, AsyncResult ar) {
1795        ApnContext apnContext = null;
1796
1797        if (ar.userObj instanceof ApnContext) {
1798            apnContext = (ApnContext) ar.userObj;
1799        } else {
1800            loge("onDisconnectDone: Invalid ar in onDisconnectDone, ignore");
1801            return;
1802        }
1803
1804        if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
1805        apnContext.setState(DctConstants.State.IDLE);
1806
1807        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1808
1809        // if all data connection are gone, check whether Airplane mode request was
1810        // pending.
1811        if (isDisconnected()) {
1812            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
1813                // Radio will be turned off. No need to retry data setup
1814                apnContext.setApnSetting(null);
1815                apnContext.setDataConnectionAc(null);
1816                return;
1817            }
1818        }
1819
1820        // If APN is still enabled, try to bring it back up automatically
1821        if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
1822            SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
1823            // Wait a bit before trying the next APN, so that
1824            // we're not tying up the RIL command channel.
1825            // This also helps in any external dependency to turn off the context.
1826            startAlarmForReconnect(getApnDelay(), apnContext);
1827        } else {
1828            apnContext.setApnSetting(null);
1829            apnContext.setDataConnectionAc(null);
1830            if (isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology())) {
1831                setupDataOnConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION);
1832            }
1833        }
1834    }
1835
1836    /**
1837     * Called when EVENT_DISCONNECT_DC_RETRYING is received.
1838     */
1839    @Override
1840    protected void onDisconnectDcRetrying(int connId, AsyncResult ar) {
1841        // We could just do this in DC!!!
1842        ApnContext apnContext = null;
1843
1844        if (ar.userObj instanceof ApnContext) {
1845            apnContext = (ApnContext) ar.userObj;
1846        } else {
1847            loge("onDisconnectDcRetrying: Invalid ar in onDisconnectDone, ignore");
1848            return;
1849        }
1850
1851        apnContext.setState(DctConstants.State.RETRYING);
1852        if(DBG) log("onDisconnectDcRetrying: apnContext=" + apnContext);
1853
1854        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1855    }
1856
1857    protected void onPollPdp() {
1858        if (getOverallState() == DctConstants.State.CONNECTED) {
1859            // only poll when connected
1860            mPhone.mCi.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
1861            sendMessageDelayed(obtainMessage(DctConstants.EVENT_POLL_PDP), POLL_PDP_MILLIS);
1862        }
1863    }
1864
1865    @Override
1866    protected void onVoiceCallStarted() {
1867        if (DBG) log("onVoiceCallStarted");
1868        mInVoiceCall = true;
1869        if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1870            if (DBG) log("onVoiceCallStarted stop polling");
1871            stopNetStatPoll();
1872            stopDataStallAlarm();
1873            notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1874        }
1875    }
1876
1877    @Override
1878    protected void onVoiceCallEnded() {
1879        if (DBG) log("onVoiceCallEnded");
1880        mInVoiceCall = false;
1881        if (isConnected()) {
1882            if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1883                startNetStatPoll();
1884                startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1885                notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1886            } else {
1887                // clean slate after call end.
1888                resetPollStats();
1889            }
1890        }
1891        // reset reconnect timer
1892        setupDataOnConnectableApns(Phone.REASON_VOICE_CALL_ENDED);
1893    }
1894
1895    @Override
1896    protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
1897        if (DBG) log("onCleanUpConnection");
1898        ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1899        if (apnContext != null) {
1900            apnContext.setReason(reason);
1901            cleanUpConnection(tearDown, apnContext);
1902        }
1903    }
1904
1905    @Override
1906    protected boolean isConnected() {
1907        for (ApnContext apnContext : mApnContexts.values()) {
1908            if (apnContext.getState() == DctConstants.State.CONNECTED) {
1909                // At least one context is connected, return true
1910                return true;
1911            }
1912        }
1913        // There are not any contexts connected, return false
1914        return false;
1915    }
1916
1917    @Override
1918    public boolean isDisconnected() {
1919        for (ApnContext apnContext : mApnContexts.values()) {
1920            if (!apnContext.isDisconnected()) {
1921                // At least one context was not disconnected return false
1922                return false;
1923            }
1924        }
1925        // All contexts were disconnected so return true
1926        return true;
1927    }
1928
1929    @Override
1930    protected void notifyDataConnection(String reason) {
1931        if (DBG) log("notifyDataConnection: reason=" + reason);
1932        for (ApnContext apnContext : mApnContexts.values()) {
1933            if (mAttached.get() && apnContext.isReady()) {
1934                if (DBG) log("notifyDataConnection: type:" + apnContext.getApnType());
1935                mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
1936                        apnContext.getApnType());
1937            }
1938        }
1939        notifyOffApnsOfAvailability(reason);
1940    }
1941
1942    /**
1943     * Based on the sim operator numeric, create a list for all possible
1944     * Data Connections and setup the preferredApn.
1945     */
1946    private void createAllApnList() {
1947        mAllApnSettings = new ArrayList<ApnSetting>();
1948        IccRecords r = mIccRecords.get();
1949        String operator = (r != null) ? r.getOperatorNumeric() : "";
1950        if (operator != null) {
1951            String selection = "numeric = '" + operator + "'";
1952            // query only enabled apn.
1953            // carrier_enabled : 1 means enabled apn, 0 disabled apn.
1954            selection += " and carrier_enabled = 1";
1955            if (DBG) log("createAllApnList: selection=" + selection);
1956
1957            Cursor cursor = mPhone.getContext().getContentResolver().query(
1958                    Telephony.Carriers.CONTENT_URI, null, selection, null, null);
1959
1960            if (cursor != null) {
1961                if (cursor.getCount() > 0) {
1962                    mAllApnSettings = createApnList(cursor);
1963                }
1964                cursor.close();
1965            }
1966        }
1967
1968        if (mAllApnSettings.isEmpty()) {
1969            if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
1970            mPreferredApn = null;
1971            // TODO: What is the right behavior?
1972            //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
1973        } else {
1974            mPreferredApn = getPreferredApn();
1975            if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
1976                mPreferredApn = null;
1977                setPreferredApn(-1);
1978            }
1979            if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
1980        }
1981        if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
1982    }
1983
1984    /** Return the DC AsyncChannel for the new data connection */
1985    private DcAsyncChannel createDataConnection() {
1986        if (DBG) log("createDataConnection E");
1987
1988        int id = mUniqueIdGenerator.getAndIncrement();
1989        DataConnection conn = DataConnection.makeDataConnection(mPhone, id,
1990                                                this, mDcTesterFailBringUpAll, mDcc);
1991        mDataConnections.put(id, conn);
1992        DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
1993        int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
1994        if (status == AsyncChannel.STATUS_SUCCESSFUL) {
1995            mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac);
1996        } else {
1997            loge("createDataConnection: Could not connect to dcac=" + dcac + " status=" + status);
1998        }
1999
2000        if (DBG) log("createDataConnection() X id=" + id + " dc=" + conn);
2001        return dcac;
2002    }
2003
2004    private void destroyDataConnections() {
2005        if(mDataConnections != null) {
2006            if (DBG) log("destroyDataConnections: clear mDataConnectionList");
2007            mDataConnections.clear();
2008        } else {
2009            if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
2010        }
2011    }
2012
2013    /**
2014     * Build a list of APNs to be used to create PDP's.
2015     *
2016     * @param requestedApnType
2017     * @return waitingApns list to be used to create PDP
2018     *          error when waitingApns.isEmpty()
2019     */
2020    private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, int radioTech) {
2021        if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
2022        ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
2023
2024        if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
2025            ApnSetting dun = fetchDunApn();
2026            if (dun != null) {
2027                apnList.add(dun);
2028                if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
2029                return apnList;
2030            }
2031        }
2032
2033        IccRecords r = mIccRecords.get();
2034        String operator = (r != null) ? r.getOperatorNumeric() : "";
2035
2036        // This is a workaround for a bug (7305641) where we don't failover to other
2037        // suitable APNs if our preferred APN fails.  On prepaid ATT sims we need to
2038        // failover to a provisioning APN, but once we've used their default data
2039        // connection we are locked to it for life.  This change allows ATT devices
2040        // to say they don't want to use preferred at all.
2041        boolean usePreferred = true;
2042        try {
2043            usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
2044                    internal.R.bool.config_dontPreferApn);
2045        } catch (Resources.NotFoundException e) {
2046            if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
2047            usePreferred = true;
2048        }
2049        if (DBG) {
2050            log("buildWaitingApns: usePreferred=" + usePreferred
2051                    + " canSetPreferApn=" + mCanSetPreferApn
2052                    + " mPreferredApn=" + mPreferredApn
2053                    + " operator=" + operator + " radioTech=" + radioTech
2054                    + " IccRecords r=" + r);
2055        }
2056
2057        if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
2058                mPreferredApn.canHandleType(requestedApnType)) {
2059            if (DBG) {
2060                log("buildWaitingApns: Preferred APN:" + operator + ":"
2061                        + mPreferredApn.numeric + ":" + mPreferredApn);
2062            }
2063            if (mPreferredApn.numeric.equals(operator)) {
2064                if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) {
2065                    apnList.add(mPreferredApn);
2066                    if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
2067                    return apnList;
2068                } else {
2069                    if (DBG) log("buildWaitingApns: no preferred APN");
2070                    setPreferredApn(-1);
2071                    mPreferredApn = null;
2072                }
2073            } else {
2074                if (DBG) log("buildWaitingApns: no preferred APN");
2075                setPreferredApn(-1);
2076                mPreferredApn = null;
2077            }
2078        }
2079        if (mAllApnSettings != null) {
2080            if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
2081            for (ApnSetting apn : mAllApnSettings) {
2082                if (DBG) log("buildWaitingApns: apn=" + apn);
2083                if (apn.canHandleType(requestedApnType)) {
2084                    if (apn.bearer == 0 || apn.bearer == radioTech) {
2085                        if (DBG) log("buildWaitingApns: adding apn=" + apn.toString());
2086                        apnList.add(apn);
2087                    } else {
2088                        if (DBG) {
2089                            log("buildWaitingApns: bearer:" + apn.bearer + " != "
2090                                    + "radioTech:" + radioTech);
2091                        }
2092                    }
2093                } else {
2094                if (DBG) {
2095                    log("buildWaitingApns: couldn't handle requesedApnType="
2096                            + requestedApnType);
2097                }
2098            }
2099            }
2100        } else {
2101            loge("mAllApnSettings is empty!");
2102        }
2103        if (DBG) log("buildWaitingApns: X apnList=" + apnList);
2104        return apnList;
2105    }
2106
2107    private String apnListToString (ArrayList<ApnSetting> apns) {
2108        StringBuilder result = new StringBuilder();
2109        for (int i = 0, size = apns.size(); i < size; i++) {
2110            result.append('[')
2111                  .append(apns.get(i).toString())
2112                  .append(']');
2113        }
2114        return result.toString();
2115    }
2116
2117    private void setPreferredApn(int pos) {
2118        if (!mCanSetPreferApn) {
2119            log("setPreferredApn: X !canSEtPreferApn");
2120            return;
2121        }
2122
2123        log("setPreferredApn: delete");
2124        ContentResolver resolver = mPhone.getContext().getContentResolver();
2125        resolver.delete(PREFERAPN_NO_UPDATE_URI, null, null);
2126
2127        if (pos >= 0) {
2128            log("setPreferredApn: insert");
2129            ContentValues values = new ContentValues();
2130            values.put(APN_ID, pos);
2131            resolver.insert(PREFERAPN_NO_UPDATE_URI, values);
2132        }
2133    }
2134
2135    private ApnSetting getPreferredApn() {
2136        if (mAllApnSettings.isEmpty()) {
2137            log("getPreferredApn: X not found mAllApnSettings.isEmpty");
2138            return null;
2139        }
2140
2141        Cursor cursor = mPhone.getContext().getContentResolver().query(
2142                PREFERAPN_NO_UPDATE_URI, new String[] { "_id", "name", "apn" },
2143                null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
2144
2145        if (cursor != null) {
2146            mCanSetPreferApn = true;
2147        } else {
2148            mCanSetPreferApn = false;
2149        }
2150        log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
2151                + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));
2152
2153        if (mCanSetPreferApn && cursor.getCount() > 0) {
2154            int pos;
2155            cursor.moveToFirst();
2156            pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
2157            for(ApnSetting p : mAllApnSettings) {
2158                log("getPreferredApn: apnSetting=" + p);
2159                if (p.id == pos && p.canHandleType(mRequestedApnType)) {
2160                    log("getPreferredApn: X found apnSetting" + p);
2161                    cursor.close();
2162                    return p;
2163                }
2164            }
2165        }
2166
2167        if (cursor != null) {
2168            cursor.close();
2169        }
2170
2171        log("getPreferredApn: X not found");
2172        return null;
2173    }
2174
2175    @Override
2176    public void handleMessage (Message msg) {
2177        if (DBG) log("handleMessage msg=" + msg);
2178
2179        if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
2180            loge("handleMessage: Ignore GSM msgs since GSM phone is inactive");
2181            return;
2182        }
2183
2184        switch (msg.what) {
2185            case DctConstants.EVENT_RECORDS_LOADED:
2186                onRecordsLoaded();
2187                break;
2188
2189            case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
2190                onDataConnectionDetached();
2191                break;
2192
2193            case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
2194                onDataConnectionAttached();
2195                break;
2196
2197            case DctConstants.EVENT_DATA_STATE_CHANGED:
2198                onDataStateChanged((AsyncResult) msg.obj);
2199                break;
2200
2201            case DctConstants.EVENT_POLL_PDP:
2202                onPollPdp();
2203                break;
2204
2205            case DctConstants.EVENT_DO_RECOVERY:
2206                doRecovery();
2207                break;
2208
2209            case DctConstants.EVENT_APN_CHANGED:
2210                onApnChanged();
2211                break;
2212
2213            case DctConstants.EVENT_PS_RESTRICT_ENABLED:
2214                /**
2215                 * We don't need to explicitly to tear down the PDP context
2216                 * when PS restricted is enabled. The base band will deactive
2217                 * PDP context and notify us with PDP_CONTEXT_CHANGED.
2218                 * But we should stop the network polling and prevent reset PDP.
2219                 */
2220                if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
2221                stopNetStatPoll();
2222                stopDataStallAlarm();
2223                mIsPsRestricted = true;
2224                break;
2225
2226            case DctConstants.EVENT_PS_RESTRICT_DISABLED:
2227                /**
2228                 * When PS restrict is removed, we need setup PDP connection if
2229                 * PDP connection is down.
2230                 */
2231                if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
2232                mIsPsRestricted  = false;
2233                if (isConnected()) {
2234                    startNetStatPoll();
2235                    startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
2236                } else {
2237                    // TODO: Should all PDN states be checked to fail?
2238                    if (mState == DctConstants.State.FAILED) {
2239                        cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
2240                        mReregisterOnReconnectFailure = false;
2241                    }
2242                    ApnContext apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_DEFAULT);
2243                    if (apnContext != null) {
2244                        apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
2245                        trySetupData(apnContext);
2246                    } else {
2247                        loge("**** Default ApnContext not found ****");
2248                        if (Build.IS_DEBUGGABLE) {
2249                            throw new RuntimeException("Default ApnContext not found");
2250                        }
2251                    }
2252                }
2253                break;
2254
2255            case DctConstants.EVENT_TRY_SETUP_DATA:
2256                if (msg.obj instanceof ApnContext) {
2257                    onTrySetupData((ApnContext)msg.obj);
2258                } else if (msg.obj instanceof String) {
2259                    onTrySetupData((String)msg.obj);
2260                } else {
2261                    loge("EVENT_TRY_SETUP request w/o apnContext or String");
2262                }
2263                break;
2264
2265            case DctConstants.EVENT_CLEAN_UP_CONNECTION:
2266                boolean tearDown = (msg.arg1 == 0) ? false : true;
2267                if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
2268                if (msg.obj instanceof ApnContext) {
2269                    cleanUpConnection(tearDown, (ApnContext)msg.obj);
2270                } else {
2271                    loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context, call super");
2272                    super.handleMessage(msg);
2273                }
2274                break;
2275
2276            default:
2277                // handle the message in the super class DataConnectionTracker
2278                super.handleMessage(msg);
2279                break;
2280        }
2281    }
2282
2283    protected int getApnProfileID(String apnType) {
2284        if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
2285            return RILConstants.DATA_PROFILE_IMS;
2286        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_FOTA)) {
2287            return RILConstants.DATA_PROFILE_FOTA;
2288        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_CBS)) {
2289            return RILConstants.DATA_PROFILE_CBS;
2290        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IA)) {
2291            return RILConstants.DATA_PROFILE_DEFAULT; // DEFAULT for now
2292        } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_DUN)) {
2293            return RILConstants.DATA_PROFILE_TETHERED;
2294        } else {
2295            return RILConstants.DATA_PROFILE_DEFAULT;
2296        }
2297    }
2298
2299    private int getCellLocationId() {
2300        int cid = -1;
2301        CellLocation loc = mPhone.getCellLocation();
2302
2303        if (loc != null) {
2304            if (loc instanceof GsmCellLocation) {
2305                cid = ((GsmCellLocation)loc).getCid();
2306            } else if (loc instanceof CdmaCellLocation) {
2307                cid = ((CdmaCellLocation)loc).getBaseStationId();
2308            }
2309        }
2310        return cid;
2311    }
2312
2313    @Override
2314    protected void onUpdateIcc() {
2315        if (mUiccController == null ) {
2316            return;
2317        }
2318
2319        IccRecords newIccRecords = mUiccController.getIccRecords(UiccController.APP_FAM_3GPP);
2320
2321        IccRecords r = mIccRecords.get();
2322        if (r != newIccRecords) {
2323            if (r != null) {
2324                log("Removing stale icc objects.");
2325                r.unregisterForRecordsLoaded(this);
2326                mIccRecords.set(null);
2327            }
2328            if (newIccRecords != null) {
2329                log("New records found");
2330                mIccRecords.set(newIccRecords);
2331                newIccRecords.registerForRecordsLoaded(
2332                        this, DctConstants.EVENT_RECORDS_LOADED, null);
2333            }
2334        }
2335    }
2336
2337    @Override
2338    protected void log(String s) {
2339        Rlog.d(LOG_TAG, s);
2340    }
2341
2342    @Override
2343    protected void loge(String s) {
2344        Rlog.e(LOG_TAG, s);
2345    }
2346
2347    @Override
2348    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2349        pw.println("DataConnectionTracker extends:");
2350        super.dump(fd, pw, args);
2351        pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
2352        pw.println(" canSetPreferApn=" + mCanSetPreferApn);
2353        pw.println(" mApnObserver=" + mApnObserver);
2354        pw.println(" getOverallState=" + getOverallState());
2355        pw.println(" mDataConnectionAsyncChannels=%s\n" + mDataConnectionAcHashMap);
2356        pw.println(" mAttached=" + mAttached.get());
2357    }
2358}
2359