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