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