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