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