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