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