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