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