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