GsmDataConnectionTracker.java revision c03fa5014912684367174ff3cce664deb29f5e0e
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.gsm;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
22import android.content.ContentResolver;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.SharedPreferences;
28import android.database.ContentObserver;
29import android.database.Cursor;
30import android.net.ConnectivityManager;
31import android.net.IConnectivityManager;
32import android.net.NetworkInfo;
33import android.net.Uri;
34import android.net.wifi.WifiManager;
35import android.os.AsyncResult;
36import android.os.INetStatService;
37import android.os.Message;
38import android.os.RemoteException;
39import android.os.ServiceManager;
40import android.os.SystemClock;
41import android.os.SystemProperties;
42import android.preference.PreferenceManager;
43import android.provider.Settings;
44import android.provider.Telephony;
45import android.telephony.ServiceState;
46import android.telephony.TelephonyManager;
47import android.telephony.gsm.GsmCellLocation;
48import android.text.TextUtils;
49import android.util.EventLog;
50import android.util.Log;
51
52import com.android.internal.telephony.DataCallState;
53import com.android.internal.telephony.DataConnection;
54import com.android.internal.telephony.DataConnectionTracker;
55import com.android.internal.telephony.Phone;
56import com.android.internal.telephony.RetryManager;
57import com.android.internal.telephony.EventLogTags;
58import com.android.internal.telephony.DataConnection.FailCause;
59
60import java.io.IOException;
61import java.util.ArrayList;
62
63/**
64 * {@hide}
65 */
66public final class GsmDataConnectionTracker extends DataConnectionTracker {
67    protected final String LOG_TAG = "GSM";
68
69    private GSMPhone mGsmPhone;
70    /**
71     * Handles changes to the APN db.
72     */
73    private class ApnChangeObserver extends ContentObserver {
74        public ApnChangeObserver () {
75            super(mDataConnectionTracker);
76        }
77
78        @Override
79        public void onChange(boolean selfChange) {
80            sendMessage(obtainMessage(EVENT_APN_CHANGED));
81        }
82    }
83
84    //***** Instance Variables
85
86    INetStatService netstat;
87    // Indicates baseband will not auto-attach
88    private boolean noAutoAttach = false;
89
90    private boolean mReregisterOnReconnectFailure = false;
91    private ContentResolver mResolver;
92
93    private boolean mPingTestActive = false;
94    // Count of PDP reset attempts; reset when we see incoming,
95    // call reRegisterNetwork, or pingTest succeeds.
96    private int mPdpResetCount = 0;
97    private boolean mIsScreenOn = true;
98
99    /** Delay between APN attempts */
100    protected static final int APN_DELAY_MILLIS = 5000;
101
102    //useful for debugging
103    boolean failNextConnect = false;
104
105    /**
106     * allApns holds all apns for this sim spn, retrieved from
107     * the Carrier DB.
108     *
109     * Create once after simcard info is loaded
110     */
111    private ArrayList<ApnSetting> allApns = null;
112
113    /**
114     * waitingApns holds all apns that are waiting to be connected
115     *
116     * It is a subset of allApns and has the same format
117     */
118    private ArrayList<ApnSetting> waitingApns = null;
119
120    private ApnSetting preferredApn = null;
121
122    /* Currently active APN */
123    protected ApnSetting mActiveApn;
124
125    /**
126     * pdpList holds all the PDP connection, i.e. IP Link in GPRS
127     */
128    private ArrayList<DataConnection> pdpList;
129
130    /** Currently active DataConnection */
131    private GsmDataConnection mActivePdp;
132
133    /** Is packet service restricted by network */
134    private boolean mIsPsRestricted = false;
135
136    //***** Constants
137
138    // TODO: Increase this to match the max number of simultaneous
139    // PDP contexts we plan to support.
140    /**
141     * Pool size of DataConnection objects.
142     */
143    private static final int PDP_CONNECTION_POOL_SIZE = 1;
144
145    private static final int POLL_PDP_MILLIS = 5 * 1000;
146
147    private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
148    private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason";
149
150    static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
151    static final String APN_ID = "apn_id";
152    private boolean canSetPreferApn = false;
153
154    // for tracking retrys on the default APN
155    private RetryManager mDefaultRetryManager;
156    // for tracking retrys on a secondary APN
157    private RetryManager mSecondaryRetryManager;
158
159    BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
160    {
161        @Override
162        public void onReceive(Context context, Intent intent)
163        {
164            String action = intent.getAction();
165            if (action.equals(Intent.ACTION_SCREEN_ON)) {
166                mIsScreenOn = true;
167                stopNetStatPoll();
168                startNetStatPoll();
169            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
170                mIsScreenOn = false;
171                stopNetStatPoll();
172                startNetStatPoll();
173            } else if (action.equals((INTENT_RECONNECT_ALARM))) {
174                Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state);
175
176                String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
177                if (state == State.FAILED) {
178                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
179                    msg.arg1 = 0; // tearDown is false
180                    msg.obj = (String) reason;
181                    sendMessage(msg);
182                }
183                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
184            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
185                final android.net.NetworkInfo networkInfo = (NetworkInfo)
186                        intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
187                mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
188            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
189                final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
190                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
191
192                if (!enabled) {
193                    // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION
194                    // quit and wont report disconnected til next enalbing.
195                    mIsWifiConnected = false;
196                }
197            }
198        }
199    };
200
201    /** Watches for changes to the APN db. */
202    private ApnChangeObserver apnObserver;
203
204    //***** Constructor
205
206    GsmDataConnectionTracker(GSMPhone p) {
207        super(p);
208        mGsmPhone = p;
209        p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
210        p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
211        p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
212        p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
213        p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
214        p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
215        p.mSST.registerForGprsAttached(this, EVENT_GPRS_ATTACHED, null);
216        p.mSST.registerForGprsDetached(this, EVENT_GPRS_DETACHED, null);
217        p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null);
218        p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
219        p.mSST.registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null);
220        p.mSST.registerForPsRestrictedDisabled(this, EVENT_PS_RESTRICT_DISABLED, null);
221
222        this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat"));
223
224        IntentFilter filter = new IntentFilter();
225        filter.addAction(INTENT_RECONNECT_ALARM);
226        filter.addAction(Intent.ACTION_SCREEN_ON);
227        filter.addAction(Intent.ACTION_SCREEN_OFF);
228        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
229        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
230
231        // TODO: Why is this registering the phone as the receiver of the intent
232        //       and not its own handler?
233        p.getContext().registerReceiver(mIntentReceiver, filter, null, p);
234
235
236        mDataConnectionTracker = this;
237        mResolver = phone.getContext().getContentResolver();
238
239        apnObserver = new ApnChangeObserver();
240        p.getContext().getContentResolver().registerContentObserver(
241                Telephony.Carriers.CONTENT_URI, true, apnObserver);
242
243        createAllPdpList();
244
245        // This preference tells us 1) initial condition for "dataEnabled",
246        // and 2) whether the RIL will setup the baseband to auto-PS attach.
247        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
248        boolean dataEnabledSetting = true;
249        try {
250            dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager.
251                getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled();
252        } catch (Exception e) {
253            // nothing to do - use the old behavior and leave data on
254        }
255        dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false) &&
256                dataEnabledSetting;
257        if (dataEnabled[APN_DEFAULT_ID]) {
258            enabledCount++;
259        }
260        noAutoAttach = !dataEnabled[APN_DEFAULT_ID];
261
262        if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
263            if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) {
264                // Should never happen, log an error and default to a simple linear sequence.
265                Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG="
266                        + DEFAULT_DATA_RETRY_CONFIG);
267                mRetryMgr.configure(20, 2000, 1000);
268            }
269        }
270
271        mDefaultRetryManager = mRetryMgr;
272        mSecondaryRetryManager = new RetryManager();
273
274        if (!mSecondaryRetryManager.configure(SystemProperties.get(
275                "ro.gsm.2nd_data_retry_config"))) {
276            if (!mSecondaryRetryManager.configure(SECONDARY_DATA_RETRY_CONFIG)) {
277                // Should never happen, log an error and default to a simple sequence.
278                Log.e(LOG_TAG, "Could note configure using SECONDARY_DATA_RETRY_CONFIG="
279                        + SECONDARY_DATA_RETRY_CONFIG);
280                mSecondaryRetryManager.configure("max_retries=3, 333, 333, 333");
281            }
282        }
283    }
284
285    public void dispose() {
286        //Unregister for all events
287        phone.mCM.unregisterForAvailable(this);
288        phone.mCM.unregisterForOffOrNotAvailable(this);
289        mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this);
290        phone.mCM.unregisterForDataStateChanged(this);
291        mGsmPhone.mCT.unregisterForVoiceCallEnded(this);
292        mGsmPhone.mCT.unregisterForVoiceCallStarted(this);
293        mGsmPhone.mSST.unregisterForGprsAttached(this);
294        mGsmPhone.mSST.unregisterForGprsDetached(this);
295        mGsmPhone.mSST.unregisterForRoamingOn(this);
296        mGsmPhone.mSST.unregisterForRoamingOff(this);
297        mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this);
298        mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this);
299
300        phone.getContext().unregisterReceiver(this.mIntentReceiver);
301        phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver);
302
303        destroyAllPdpList();
304    }
305
306    protected void finalize() {
307        if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized");
308    }
309
310    protected void setState(State s) {
311        if (DBG) log ("setState: " + s);
312        if (state != s) {
313            EventLog.writeEvent(EventLogTags.GSM_DATA_STATE_CHANGE, state.toString(), s.toString());
314            state = s;
315        }
316
317        if (state == State.FAILED) {
318            if (waitingApns != null)
319                waitingApns.clear(); // when teardown the connection and set to IDLE
320        }
321    }
322
323    public String[] getActiveApnTypes() {
324        String[] result;
325        if (mActiveApn != null) {
326            result = mActiveApn.types;
327        } else {
328            result = new String[1];
329            result[0] = Phone.APN_TYPE_DEFAULT;
330        }
331        return result;
332    }
333
334    protected String getActiveApnString() {
335        String result = null;
336        if (mActiveApn != null) {
337            result = mActiveApn.apn;
338        }
339        return result;
340    }
341
342    /**
343     * The data connection is expected to be setup while device
344     *  1. has sim card
345     *  2. registered to gprs service
346     *  3. user doesn't explicitly disable data service
347     *  4. wifi is not on
348     *
349     * @return false while no data connection if all above requirements are met.
350     */
351    public boolean isDataConnectionAsDesired() {
352        boolean roaming = phone.getServiceState().getRoaming();
353
354        if (mGsmPhone.mSIMRecords.getRecordsLoaded() &&
355                mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE &&
356                (!roaming || getDataOnRoamingEnabled()) &&
357            !mIsWifiConnected &&
358            !mIsPsRestricted ) {
359            return (state == State.CONNECTED);
360        }
361        return true;
362    }
363
364    @Override
365    protected boolean isApnTypeActive(String type) {
366        // TODO: support simultaneous with List instead
367        return mActiveApn != null && mActiveApn.canHandleType(type);
368    }
369
370    @Override
371    protected boolean isApnTypeAvailable(String type) {
372        if (allApns != null) {
373            for (ApnSetting apn : allApns) {
374                if (apn.canHandleType(type)) {
375                    return true;
376                }
377            }
378        }
379        return false;
380    }
381
382    /**
383     * Formerly this method was ArrayList<GsmDataConnection> getAllPdps()
384     */
385    public ArrayList<DataConnection> getAllDataConnections() {
386        ArrayList<DataConnection> pdps = (ArrayList<DataConnection>)pdpList.clone();
387        return pdps;
388    }
389
390    private boolean isDataAllowed() {
391        boolean roaming = phone.getServiceState().getRoaming();
392        return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()) &&
393                mMasterDataEnabled;
394    }
395
396    //****** Called from ServiceStateTracker
397    /**
398     * Invoked when ServiceStateTracker observes a transition from GPRS
399     * attach to detach.
400     */
401    protected void onGprsDetached() {
402        /*
403         * We presently believe it is unnecessary to tear down the PDP context
404         * when GPRS detaches, but we should stop the network polling.
405         */
406        stopNetStatPoll();
407        phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED);
408    }
409
410    private void onGprsAttached() {
411        if (state == State.CONNECTED) {
412            startNetStatPoll();
413            phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED);
414        } else {
415            if (state == State.FAILED) {
416                cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED);
417                mRetryMgr.resetRetryCount();
418            }
419            trySetupData(Phone.REASON_GPRS_ATTACHED);
420        }
421    }
422
423    private boolean trySetupData(String reason) {
424        if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
425
426        Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
427
428        if (phone.getSimulatedRadioControl() != null) {
429            // Assume data is connected on the simulator
430            // FIXME  this can be improved
431            setState(State.CONNECTED);
432            phone.notifyDataConnection(reason);
433
434            Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected");
435            return true;
436        }
437
438        int gprsState = mGsmPhone.mSST.getCurrentGprsState();
439        boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState();
440
441        if ((state == State.IDLE || state == State.SCANNING)
442                && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach)
443                && mGsmPhone.mSIMRecords.getRecordsLoaded()
444                && phone.getState() == Phone.State.IDLE
445                && isDataAllowed()
446                && !mIsPsRestricted
447                && desiredPowerState ) {
448
449            if (state == State.IDLE) {
450                waitingApns = buildWaitingApns();
451                if (waitingApns.isEmpty()) {
452                    if (DBG) log("No APN found");
453                    notifyNoData(GsmDataConnection.FailCause.MISSING_UKNOWN_APN);
454                    return false;
455                } else {
456                    log ("Create from allApns : " + apnListToString(allApns));
457                }
458            }
459
460            if (DBG) {
461                log ("Setup waitngApns : " + apnListToString(waitingApns));
462            }
463            return setupData(reason);
464        } else {
465            if (DBG)
466                log("trySetupData: Not ready for data: " +
467                    " dataState=" + state +
468                    " gprsState=" + gprsState +
469                    " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() +
470                    " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() +
471                    " phoneState=" + phone.getState() +
472                    " isDataAllowed=" + isDataAllowed() +
473                    " dataEnabled=" + getAnyDataEnabled() +
474                    " roaming=" + phone.getServiceState().getRoaming() +
475                    " dataOnRoamingEnable=" + getDataOnRoamingEnabled() +
476                    " ps restricted=" + mIsPsRestricted +
477                    " desiredPowerState=" + desiredPowerState +
478                    " MasterDataEnabled=" + mMasterDataEnabled);
479            return false;
480        }
481    }
482
483    /**
484     * If tearDown is true, this only tears down a CONNECTED session. Presently,
485     * there is no mechanism for abandoning an INITING/CONNECTING session,
486     * but would likely involve cancelling pending async requests or
487     * setting a flag or new state to ignore them when they came in
488     * @param tearDown true if the underlying GsmDataConnection should be
489     * disconnected.
490     * @param reason reason for the clean up.
491     */
492    private void cleanUpConnection(boolean tearDown, String reason) {
493        if (DBG) log("Clean up connection due to " + reason);
494
495        // Clear the reconnect alarm, if set.
496        if (mReconnectIntent != null) {
497            AlarmManager am =
498                (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
499            am.cancel(mReconnectIntent);
500            mReconnectIntent = null;
501        }
502
503        setState(State.DISCONNECTING);
504
505        for (DataConnection conn : pdpList) {
506            if (tearDown) {
507                Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason);
508                conn.disconnect(msg);
509            } else {
510                conn.reset();
511            }
512        }
513        stopNetStatPoll();
514
515        if (!tearDown) {
516            setState(State.IDLE);
517            phone.notifyDataConnection(reason);
518            mActiveApn = null;
519        }
520    }
521
522    /**
523     * @param types comma delimited list of APN types
524     * @return array of APN types
525     */
526    private String[] parseTypes(String types) {
527        String[] result;
528        // If unset, set to DEFAULT.
529        if (types == null || types.equals("")) {
530            result = new String[1];
531            result[0] = Phone.APN_TYPE_ALL;
532        } else {
533            result = types.split(",");
534        }
535        return result;
536    }
537
538    private ArrayList<ApnSetting> createApnList(Cursor cursor) {
539        ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
540        if (cursor.moveToFirst()) {
541            do {
542                String[] types = parseTypes(
543                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
544                ApnSetting apn = new ApnSetting(
545                        cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
546                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
547                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
548                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
549                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)),
550                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
551                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)),
552                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)),
553                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
554                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
555                        cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
556                        cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
557                        types);
558                result.add(apn);
559            } while (cursor.moveToNext());
560        }
561        return result;
562    }
563
564    private GsmDataConnection findFreePdp() {
565        for (DataConnection conn : pdpList) {
566            GsmDataConnection pdp = (GsmDataConnection) conn;
567            if (pdp.isInactive()) {
568                return pdp;
569            }
570        }
571        return null;
572    }
573
574    private boolean setupData(String reason) {
575        ApnSetting apn;
576        GsmDataConnection pdp;
577
578        apn = getNextApn();
579        if (apn == null) return false;
580        pdp = findFreePdp();
581        if (pdp == null) {
582            if (DBG) log("setupData: No free GsmDataConnection found!");
583            return false;
584        }
585        mActiveApn = apn;
586        mActivePdp = pdp;
587
588        Message msg = obtainMessage();
589        msg.what = EVENT_DATA_SETUP_COMPLETE;
590        msg.obj = reason;
591        pdp.connect(msg, apn);
592
593        setState(State.INITING);
594        phone.notifyDataConnection(reason);
595        return true;
596    }
597
598    protected String getInterfaceName(String apnType) {
599        if (mActivePdp != null &&
600                (apnType == null ||
601                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
602            return mActivePdp.getInterface();
603        }
604        return null;
605    }
606
607    protected String getIpAddress(String apnType) {
608        if (mActivePdp != null &&
609                (apnType == null ||
610                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
611            return mActivePdp.getIpAddress();
612        }
613        return null;
614    }
615
616    public String getGateway(String apnType) {
617        if (mActivePdp != null &&
618                (apnType == null ||
619                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
620            return mActivePdp.getGatewayAddress();
621        }
622        return null;
623    }
624
625    protected String[] getDnsServers(String apnType) {
626        if (mActivePdp != null &&
627                (apnType == null ||
628                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
629            return mActivePdp.getDnsServers();
630        }
631        return null;
632    }
633
634    private boolean
635    pdpStatesHasCID (ArrayList<DataCallState> states, int cid) {
636        for (int i = 0, s = states.size() ; i < s ; i++) {
637            if (states.get(i).cid == cid) return true;
638        }
639
640        return false;
641    }
642
643    private boolean
644    pdpStatesHasActiveCID (ArrayList<DataCallState> states, int cid) {
645        for (int i = 0, s = states.size() ; i < s ; i++) {
646            if ((states.get(i).cid == cid) && (states.get(i).active != 0)) {
647                return true;
648            }
649        }
650
651        return false;
652    }
653
654    /**
655     * Handles changes to the APN database.
656     */
657    private void onApnChanged() {
658        boolean isConnected;
659
660        isConnected = (state != State.IDLE && state != State.FAILED);
661
662        // The "current" may no longer be valid.  MMS depends on this to send properly.
663        mGsmPhone.updateCurrentCarrierInProvider();
664
665        // TODO: It'd be nice to only do this if the changed entrie(s)
666        // match the current operator.
667        createAllApnList();
668        if (state != State.DISCONNECTING) {
669            cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED);
670            if (!isConnected) {
671                // reset reconnect timer
672                mRetryMgr.resetRetryCount();
673                mReregisterOnReconnectFailure = false;
674                trySetupData(Phone.REASON_APN_CHANGED);
675            }
676        }
677    }
678
679    /**
680     * @param explicitPoll if true, indicates that *we* polled for this
681     * update while state == CONNECTED rather than having it delivered
682     * via an unsolicited response (which could have happened at any
683     * previous state
684     */
685    protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) {
686        ArrayList<DataCallState> pdpStates;
687
688        pdpStates = (ArrayList<DataCallState>)(ar.result);
689
690        if (ar.exception != null) {
691            // This is probably "radio not available" or something
692            // of that sort. If so, the whole connection is going
693            // to come down soon anyway
694            return;
695        }
696
697        if (state == State.CONNECTED) {
698            // The way things are supposed to work, the PDP list
699            // should not contain the CID after it disconnects.
700            // However, the way things really work, sometimes the PDP
701            // context is still listed with active = false, which
702            // makes it hard to distinguish an activating context from
703            // an activated-and-then deactivated one.
704            if (!pdpStatesHasCID(pdpStates, cidActive)) {
705                // It looks like the PDP context has deactivated.
706                // Tear everything down and try to reconnect.
707
708                Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting");
709
710                // Add an event log when the network drops PDP
711                GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
712                EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP,
713                        loc != null ? loc.getCid() : -1,
714                        TelephonyManager.getDefault().getNetworkType());
715
716                cleanUpConnection(true, null);
717                return;
718            } else if (!pdpStatesHasActiveCID(pdpStates, cidActive)) {
719                // Here, we only consider this authoritative if we asked for the
720                // PDP list. If it was an unsolicited response, we poll again
721                // to make sure everyone agrees on the initial state.
722
723                if (!explicitPoll) {
724                    // We think it disconnected but aren't sure...poll from our side
725                    phone.mCM.getPDPContextList(
726                            this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
727                } else {
728                    Log.i(LOG_TAG, "PDP connection has dropped (active=false case). "
729                                    + " Reconnecting");
730
731                    // Log the network drop on the event log.
732                    GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
733                    EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP,
734                            loc != null ? loc.getCid() : -1,
735                            TelephonyManager.getDefault().getNetworkType());
736
737                    cleanUpConnection(true, null);
738                }
739            }
740        }
741    }
742
743    private void notifyDefaultData(String reason) {
744        setState(State.CONNECTED);
745        phone.notifyDataConnection(reason);
746        startNetStatPoll();
747        // reset reconnect timer
748        mRetryMgr.resetRetryCount();
749        mReregisterOnReconnectFailure = false;
750    }
751
752    /**
753     * This is a kludge to deal with the fact that
754     * the PDP state change notification doesn't always work
755     * with certain RIL impl's/basebands
756     *
757     */
758    private void startPeriodicPdpPoll() {
759        removeMessages(EVENT_POLL_PDP);
760
761        sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
762    }
763
764    private void resetPollStats() {
765        txPkts = -1;
766        rxPkts = -1;
767        sentSinceLastRecv = 0;
768        netStatPollPeriod = POLL_NETSTAT_MILLIS;
769        mNoRecvPollCount = 0;
770    }
771
772    private void doRecovery() {
773        if (state == State.CONNECTED) {
774            int maxPdpReset = Settings.Secure.getInt(mResolver,
775                    Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
776                    DEFAULT_MAX_PDP_RESET_FAIL);
777            if (mPdpResetCount < maxPdpReset) {
778                mPdpResetCount++;
779                EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, sentSinceLastRecv);
780                cleanUpConnection(true, Phone.REASON_PDP_RESET);
781            } else {
782                mPdpResetCount = 0;
783                EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, sentSinceLastRecv);
784                mGsmPhone.mSST.reRegisterNetwork(null);
785            }
786            // TODO: Add increasingly drastic recovery steps, eg,
787            // reset the radio, reset the device.
788        }
789    }
790
791    protected void startNetStatPoll() {
792        if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) {
793            Log.d(LOG_TAG, "[DataConnection] Start poll NetStat");
794            resetPollStats();
795            netStatPollEnabled = true;
796            mPollNetStat.run();
797        }
798    }
799
800    protected void stopNetStatPoll() {
801        netStatPollEnabled = false;
802        removeCallbacks(mPollNetStat);
803        Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat");
804    }
805
806    protected void restartRadio() {
807        Log.d(LOG_TAG, "************TURN OFF RADIO**************");
808        cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
809        mGsmPhone.mSST.powerOffRadioSafely();
810        /* Note: no need to call setRadioPower(true).  Assuming the desired
811         * radio power state is still ON (as tracked by ServiceStateTracker),
812         * ServiceStateTracker will call setRadioPower when it receives the
813         * RADIO_STATE_CHANGED notification for the power off.  And if the
814         * desired power state has changed in the interim, we don't want to
815         * override it with an unconditional power on.
816         */
817
818        int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
819        SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
820    }
821
822    private Runnable mPollNetStat = new Runnable()
823    {
824
825        public void run() {
826            long sent, received;
827            long preTxPkts = -1, preRxPkts = -1;
828
829            Activity newActivity;
830
831            preTxPkts = txPkts;
832            preRxPkts = rxPkts;
833
834            try {
835                txPkts = netstat.getMobileTxPackets();
836                rxPkts = netstat.getMobileRxPackets();
837            } catch (RemoteException e) {
838                txPkts = 0;
839                rxPkts = 0;
840            }
841
842            //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
843
844            if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
845                sent = txPkts - preTxPkts;
846                received = rxPkts - preRxPkts;
847
848                if ( sent > 0 && received > 0 ) {
849                    sentSinceLastRecv = 0;
850                    newActivity = Activity.DATAINANDOUT;
851                    mPdpResetCount = 0;
852                } else if (sent > 0 && received == 0) {
853                    if (phone.getState() == Phone.State.IDLE) {
854                        sentSinceLastRecv += sent;
855                    } else {
856                        sentSinceLastRecv = 0;
857                    }
858                    newActivity = Activity.DATAOUT;
859                } else if (sent == 0 && received > 0) {
860                    sentSinceLastRecv = 0;
861                    newActivity = Activity.DATAIN;
862                    mPdpResetCount = 0;
863                } else if (sent == 0 && received == 0) {
864                    newActivity = Activity.NONE;
865                } else {
866                    sentSinceLastRecv = 0;
867                    newActivity = Activity.NONE;
868                }
869
870                if (activity != newActivity && mIsScreenOn) {
871                    activity = newActivity;
872                    phone.notifyDataActivity();
873                }
874            }
875
876            int watchdogTrigger = Settings.Secure.getInt(mResolver,
877                    Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
878                    NUMBER_SENT_PACKETS_OF_HANG);
879
880            if (sentSinceLastRecv >= watchdogTrigger) {
881                // we already have NUMBER_SENT_PACKETS sent without ack
882                if (mNoRecvPollCount == 0) {
883                    EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
884                            sentSinceLastRecv);
885                }
886
887                int noRecvPollLimit = Settings.Secure.getInt(mResolver,
888                        Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT);
889
890                if (mNoRecvPollCount < noRecvPollLimit) {
891                    // It's possible the PDP context went down and we weren't notified.
892                    // Start polling the context list in an attempt to recover.
893                    if (DBG) log("no DATAIN in a while; polling PDP");
894                    phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
895
896                    mNoRecvPollCount++;
897
898                    // Slow down the poll interval to let things happen
899                    netStatPollPeriod = Settings.Secure.getInt(mResolver,
900                            Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
901                            POLL_NETSTAT_SLOW_MILLIS);
902                } else {
903                    if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) +
904                                        " pkts since last received");
905                    // We've exceeded the threshold.  Run ping test as a final check;
906                    // it will proceed with recovery if ping fails.
907                    stopNetStatPoll();
908                    Thread pingTest = new Thread() {
909                        public void run() {
910                            runPingTest();
911                        }
912                    };
913                    mPingTestActive = true;
914                    pingTest.start();
915                }
916            } else {
917                mNoRecvPollCount = 0;
918                if (mIsScreenOn) {
919                    netStatPollPeriod = Settings.Secure.getInt(mResolver,
920                            Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
921                } else {
922                    netStatPollPeriod = Settings.Secure.getInt(mResolver,
923                            Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
924                            POLL_NETSTAT_SCREEN_OFF_MILLIS);
925                }
926            }
927
928            if (netStatPollEnabled) {
929                mDataConnectionTracker.postDelayed(this, netStatPollPeriod);
930            }
931        }
932    };
933
934    private void runPingTest () {
935        int status = -1;
936        try {
937            String address = Settings.Secure.getString(mResolver,
938                    Settings.Secure.PDP_WATCHDOG_PING_ADDRESS);
939            int deadline = Settings.Secure.getInt(mResolver,
940                        Settings.Secure.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE);
941            if (DBG) log("pinging " + address + " for " + deadline + "s");
942            if (address != null && !NULL_IP.equals(address)) {
943                Process p = Runtime.getRuntime()
944                                .exec("ping -c 1 -i 1 -w "+ deadline + " " + address);
945                status = p.waitFor();
946            }
947        } catch (IOException e) {
948            Log.w(LOG_TAG, "ping failed: IOException");
949        } catch (Exception e) {
950            Log.w(LOG_TAG, "exception trying to ping");
951        }
952
953        if (status == 0) {
954            // ping succeeded.  False alarm.  Reset netStatPoll.
955            // ("-1" for this event indicates a false alarm)
956            EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1);
957            mPdpResetCount = 0;
958            sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL));
959        } else {
960            // ping failed.  Proceed with recovery.
961            sendMessage(obtainMessage(EVENT_START_RECOVERY));
962        }
963    }
964
965    /**
966     * Returns true if the last fail cause is something that
967     * seems like it deserves an error notification.
968     * Transient errors are ignored
969     */
970    private boolean shouldPostNotification(GsmDataConnection.FailCause  cause) {
971        return (cause != GsmDataConnection.FailCause.UNKNOWN);
972    }
973
974    /**
975     * Return true if data connection need to be setup after disconnected due to
976     * reason.
977     *
978     * @param reason the reason why data is disconnected
979     * @return true if try setup data connection is need for this reason
980     */
981    private boolean retryAfterDisconnected(String reason) {
982        boolean retry = true;
983
984        if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
985            retry = false;
986        }
987        return retry;
988    }
989
990    private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
991        if (state == State.FAILED) {
992            if (!mRetryMgr.isRetryNeeded()) {
993                if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
994                    // if no more retries on a secondary APN attempt, tell the world and revert.
995                    phone.notifyDataConnection(Phone.REASON_APN_FAILED);
996                    onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
997                    return;
998                }
999                if (mReregisterOnReconnectFailure) {
1000                    // We've re-registerd once now just retry forever.
1001                    mRetryMgr.retryForeverUsingLastTimeout();
1002                } else {
1003                    // Try to re-register to the network.
1004                    Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
1005                    mReregisterOnReconnectFailure = true;
1006                    mGsmPhone.mSST.reRegisterNetwork(null);
1007                    mRetryMgr.resetRetryCount();
1008                    return;
1009                }
1010            }
1011
1012            int nextReconnectDelay = mRetryMgr.getRetryTimer();
1013            Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for "
1014                    + (nextReconnectDelay / 1000) + "s");
1015
1016            AlarmManager am =
1017                (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
1018            Intent intent = new Intent(INTENT_RECONNECT_ALARM);
1019            intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
1020            mReconnectIntent = PendingIntent.getBroadcast(
1021                    phone.getContext(), 0, intent, 0);
1022            am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1023                    SystemClock.elapsedRealtime() + nextReconnectDelay,
1024                    mReconnectIntent);
1025
1026            mRetryMgr.increaseRetryCount();
1027
1028            if (!shouldPostNotification(lastFailCauseCode)) {
1029                Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification "
1030                                + "-- likely transient error");
1031            } else {
1032                notifyNoData(lastFailCauseCode);
1033            }
1034        }
1035    }
1036
1037    private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode) {
1038        setState(State.FAILED);
1039    }
1040
1041    protected void onRecordsLoaded() {
1042        createAllApnList();
1043        if (state == State.FAILED) {
1044            cleanUpConnection(false, null);
1045        }
1046        sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
1047    }
1048
1049    @Override
1050    protected void onEnableNewApn() {
1051        // change our retry manager to use the appropriate numbers for the new APN
1052        if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1053            mRetryMgr = mDefaultRetryManager;
1054        } else {
1055            mRetryMgr = mSecondaryRetryManager;
1056        }
1057        mRetryMgr.resetRetryCount();
1058
1059        // TODO:  To support simultaneous PDP contexts, this should really only call
1060        // cleanUpConnection if it needs to free up a GsmDataConnection.
1061        cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
1062    }
1063
1064    protected boolean onTrySetupData(String reason) {
1065        return trySetupData(reason);
1066    }
1067
1068    @Override
1069    protected void onRoamingOff() {
1070        trySetupData(Phone.REASON_ROAMING_OFF);
1071    }
1072
1073    @Override
1074    protected void onRoamingOn() {
1075        if (getDataOnRoamingEnabled()) {
1076            trySetupData(Phone.REASON_ROAMING_ON);
1077        } else {
1078            if (DBG) log("Tear down data connection on roaming.");
1079            cleanUpConnection(true, Phone.REASON_ROAMING_ON);
1080        }
1081    }
1082
1083    protected void onRadioAvailable() {
1084        if (phone.getSimulatedRadioControl() != null) {
1085            // Assume data is connected on the simulator
1086            // FIXME  this can be improved
1087            setState(State.CONNECTED);
1088            phone.notifyDataConnection(null);
1089
1090            Log.i(LOG_TAG, "We're on the simulator; assuming data is connected");
1091        }
1092
1093        if (state != State.IDLE) {
1094            cleanUpConnection(true, null);
1095        }
1096    }
1097
1098    protected void onRadioOffOrNotAvailable() {
1099        // Make sure our reconnect delay starts at the initial value
1100        // next time the radio comes on
1101        mRetryMgr.resetRetryCount();
1102        mReregisterOnReconnectFailure = false;
1103
1104        if (phone.getSimulatedRadioControl() != null) {
1105            // Assume data is connected on the simulator
1106            // FIXME  this can be improved
1107            Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless");
1108        } else {
1109            if (DBG) log("Radio is off and clean up all connection");
1110            // TODO: Should we reset mRequestedApnType to "default"?
1111            cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF);
1112        }
1113    }
1114
1115    protected void onDataSetupComplete(AsyncResult ar) {
1116        String reason = null;
1117        if (ar.userObj instanceof String) {
1118            reason = (String) ar.userObj;
1119        }
1120
1121        if (ar.exception == null) {
1122            // everything is setup
1123            if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
1124                SystemProperties.set("gsm.defaultpdpcontext.active", "true");
1125                        if (canSetPreferApn && preferredApn == null) {
1126                            Log.d(LOG_TAG, "PREFERED APN is null");
1127                            preferredApn = mActiveApn;
1128                            setPreferredApn(preferredApn.id);
1129                        }
1130            } else {
1131                SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1132            }
1133            notifyDefaultData(reason);
1134
1135            // TODO: For simultaneous PDP support, we need to build another
1136            // trigger another TRY_SETUP_DATA for the next APN type.  (Note
1137            // that the existing connection may service that type, in which
1138            // case we should try the next type, etc.
1139        } else {
1140            GsmDataConnection.FailCause cause;
1141            cause = (GsmDataConnection.FailCause) (ar.result);
1142            if(DBG) log("PDP setup failed " + cause);
1143                    // Log this failure to the Event Logs.
1144            if (cause.isEventLoggable()) {
1145                GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
1146                EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1147                        cause.ordinal(), loc != null ? loc.getCid() : -1,
1148                        TelephonyManager.getDefault().getNetworkType());
1149            }
1150
1151            // No try for permanent failure
1152            if (cause.isPermanentFail()) {
1153                notifyNoData(cause);
1154                if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1155                    phone.notifyDataConnection(Phone.REASON_APN_FAILED);
1156                    onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
1157                }
1158                return;
1159            }
1160
1161            waitingApns.remove(0);
1162            if (waitingApns.isEmpty()) {
1163                // No more to try, start delayed retry
1164                startDelayedRetry(cause, reason);
1165            } else {
1166                // we still have more apns to try
1167                setState(State.SCANNING);
1168                // Wait a bit before trying the next APN, so that
1169                // we're not tying up the RIL command channel
1170                sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS);
1171            }
1172        }
1173    }
1174
1175    protected void onDisconnectDone(AsyncResult ar) {
1176        String reason = null;
1177        if(DBG) log("EVENT_DISCONNECT_DONE");
1178        if (ar.userObj instanceof String) {
1179           reason = (String) ar.userObj;
1180        }
1181        setState(State.IDLE);
1182        phone.notifyDataConnection(reason);
1183        mActiveApn = null;
1184        if (retryAfterDisconnected(reason)) {
1185            trySetupData(reason);
1186        }
1187    }
1188
1189    protected void onPollPdp() {
1190        if (state == State.CONNECTED) {
1191            // only poll when connected
1192            phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1193            sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
1194        }
1195    }
1196
1197    protected void onVoiceCallStarted() {
1198        if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) {
1199            stopNetStatPoll();
1200            phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1201        }
1202    }
1203
1204    protected void onVoiceCallEnded() {
1205        if (state == State.CONNECTED) {
1206            if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
1207                startNetStatPoll();
1208                phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1209            } else {
1210                // clean slate after call end.
1211                resetPollStats();
1212            }
1213        } else {
1214            // reset reconnect timer
1215            mRetryMgr.resetRetryCount();
1216            mReregisterOnReconnectFailure = false;
1217            // in case data setup was attempted when we were on a voice call
1218            trySetupData(Phone.REASON_VOICE_CALL_ENDED);
1219        }
1220    }
1221
1222    protected void onCleanUpConnection(boolean tearDown, String reason) {
1223        cleanUpConnection(tearDown, reason);
1224    }
1225
1226    /**
1227     * Based on the sim operator numeric, create a list for all possible pdps
1228     * with all apns associated with that pdp
1229     *
1230     *
1231     */
1232    private void createAllApnList() {
1233        allApns = new ArrayList<ApnSetting>();
1234        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
1235
1236        if (operator != null) {
1237            String selection = "numeric = '" + operator + "'";
1238
1239            Cursor cursor = phone.getContext().getContentResolver().query(
1240                    Telephony.Carriers.CONTENT_URI, null, selection, null, null);
1241
1242            if (cursor != null) {
1243                if (cursor.getCount() > 0) {
1244                    allApns = createApnList(cursor);
1245                    // TODO: Figure out where this fits in.  This basically just
1246                    // writes the pap-secrets file.  No longer tied to GsmDataConnection
1247                    // object.  Not used on current platform (no ppp).
1248                    //GsmDataConnection pdp = pdpList.get(pdp_name);
1249                    //if (pdp != null && pdp.dataLink != null) {
1250                    //    pdp.dataLink.setPasswordInfo(cursor);
1251                    //}
1252                }
1253                cursor.close();
1254            }
1255        }
1256
1257        if (allApns.isEmpty()) {
1258            if (DBG) log("No APN found for carrier: " + operator);
1259            preferredApn = null;
1260            notifyNoData(GsmDataConnection.FailCause.MISSING_UKNOWN_APN);
1261        } else {
1262            preferredApn = getPreferredApn();
1263            Log.d(LOG_TAG, "Get PreferredAPN");
1264            if (preferredApn != null && !preferredApn.numeric.equals(operator)) {
1265                preferredApn = null;
1266                setPreferredApn(-1);
1267            }
1268        }
1269    }
1270
1271    private void createAllPdpList() {
1272        pdpList = new ArrayList<DataConnection>();
1273        DataConnection pdp;
1274
1275        for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) {
1276            pdp = GsmDataConnection.makeDataConnection(mGsmPhone);
1277            pdpList.add(pdp);
1278         }
1279    }
1280
1281    private void destroyAllPdpList() {
1282        if(pdpList != null) {
1283            GsmDataConnection pdp;
1284            pdpList.removeAll(pdpList);
1285        }
1286    }
1287
1288    /**
1289     *
1290     * @return waitingApns list to be used to create PDP
1291     *          error when waitingApns.isEmpty()
1292     */
1293    private ArrayList<ApnSetting> buildWaitingApns() {
1294        ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
1295        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
1296
1297        if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1298            if (canSetPreferApn && preferredApn != null) {
1299                Log.i(LOG_TAG, "Preferred APN:" + operator + ":"
1300                        + preferredApn.numeric + ":" + preferredApn);
1301                if (preferredApn.numeric.equals(operator)) {
1302                    Log.i(LOG_TAG, "Waiting APN set to preferred APN");
1303                    apnList.add(preferredApn);
1304                    return apnList;
1305                } else {
1306                    setPreferredApn(-1);
1307                    preferredApn = null;
1308                }
1309            }
1310        }
1311
1312        if (allApns != null) {
1313            for (ApnSetting apn : allApns) {
1314                if (apn.canHandleType(mRequestedApnType)) {
1315                    apnList.add(apn);
1316                }
1317            }
1318        }
1319        return apnList;
1320    }
1321
1322    /**
1323     * Get next apn in waitingApns
1324     * @return the first apn found in waitingApns, null if none
1325     */
1326    private ApnSetting getNextApn() {
1327        ArrayList<ApnSetting> list = waitingApns;
1328        ApnSetting apn = null;
1329
1330        if (list != null) {
1331            if (!list.isEmpty()) {
1332                apn = list.get(0);
1333            }
1334        }
1335        return apn;
1336    }
1337
1338    private String apnListToString (ArrayList<ApnSetting> apns) {
1339        StringBuilder result = new StringBuilder();
1340        for (int i = 0, size = apns.size(); i < size; i++) {
1341            result.append('[')
1342                  .append(apns.get(i).toString())
1343                  .append(']');
1344        }
1345        return result.toString();
1346    }
1347
1348    private void startDelayedRetry(GsmDataConnection.FailCause cause, String reason) {
1349        notifyNoData(cause);
1350        reconnectAfterFail(cause, reason);
1351    }
1352
1353    private void setPreferredApn(int pos) {
1354        if (!canSetPreferApn) {
1355            return;
1356        }
1357
1358        ContentResolver resolver = phone.getContext().getContentResolver();
1359        resolver.delete(PREFERAPN_URI, null, null);
1360
1361        if (pos >= 0) {
1362            ContentValues values = new ContentValues();
1363            values.put(APN_ID, pos);
1364            resolver.insert(PREFERAPN_URI, values);
1365        }
1366    }
1367
1368    private ApnSetting getPreferredApn() {
1369        if (allApns.isEmpty()) {
1370            return null;
1371        }
1372
1373        Cursor cursor = phone.getContext().getContentResolver().query(
1374                PREFERAPN_URI, new String[] { "_id", "name", "apn" },
1375                null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
1376
1377        if (cursor != null) {
1378            canSetPreferApn = true;
1379        } else {
1380            canSetPreferApn = false;
1381        }
1382
1383        if (canSetPreferApn && cursor.getCount() > 0) {
1384            int pos;
1385            cursor.moveToFirst();
1386            pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
1387            for(ApnSetting p:allApns) {
1388                if (p.id == pos && p.canHandleType(mRequestedApnType)) {
1389                    cursor.close();
1390                    return p;
1391                }
1392            }
1393        }
1394
1395        if (cursor != null) {
1396            cursor.close();
1397        }
1398
1399        return null;
1400    }
1401
1402    public void handleMessage (Message msg) {
1403        if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg);
1404
1405        if (!mGsmPhone.mIsTheCurrentActivePhone) {
1406            Log.d(LOG_TAG, "Ignore GSM msgs since GSM phone is inactive");
1407            return;
1408        }
1409
1410        switch (msg.what) {
1411            case EVENT_RECORDS_LOADED:
1412                onRecordsLoaded();
1413                break;
1414
1415            case EVENT_GPRS_DETACHED:
1416                onGprsDetached();
1417                break;
1418
1419            case EVENT_GPRS_ATTACHED:
1420                onGprsAttached();
1421                break;
1422
1423            case EVENT_DATA_STATE_CHANGED:
1424                onPdpStateChanged((AsyncResult) msg.obj, false);
1425                break;
1426
1427            case EVENT_GET_PDP_LIST_COMPLETE:
1428                onPdpStateChanged((AsyncResult) msg.obj, true);
1429                break;
1430
1431            case EVENT_POLL_PDP:
1432                onPollPdp();
1433                break;
1434
1435            case EVENT_START_NETSTAT_POLL:
1436                mPingTestActive = false;
1437                startNetStatPoll();
1438                break;
1439
1440            case EVENT_START_RECOVERY:
1441                mPingTestActive = false;
1442                doRecovery();
1443                break;
1444
1445            case EVENT_APN_CHANGED:
1446                onApnChanged();
1447                break;
1448
1449            case EVENT_PS_RESTRICT_ENABLED:
1450                /**
1451                 * We don't need to explicitly to tear down the PDP context
1452                 * when PS restricted is enabled. The base band will deactive
1453                 * PDP context and notify us with PDP_CONTEXT_CHANGED.
1454                 * But we should stop the network polling and prevent reset PDP.
1455                 */
1456                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
1457                stopNetStatPoll();
1458                mIsPsRestricted = true;
1459                break;
1460
1461            case EVENT_PS_RESTRICT_DISABLED:
1462                /**
1463                 * When PS restrict is removed, we need setup PDP connection if
1464                 * PDP connection is down.
1465                 */
1466                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
1467                mIsPsRestricted  = false;
1468                if (state == State.CONNECTED) {
1469                    startNetStatPoll();
1470                } else {
1471                    if (state == State.FAILED) {
1472                        cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED);
1473                        mRetryMgr.resetRetryCount();
1474                        mReregisterOnReconnectFailure = false;
1475                    }
1476                    trySetupData(Phone.REASON_PS_RESTRICT_ENABLED);
1477                }
1478                break;
1479
1480            default:
1481                // handle the message in the super class DataConnectionTracker
1482                super.handleMessage(msg);
1483                break;
1484        }
1485    }
1486
1487    protected void log(String s) {
1488        Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
1489    }
1490
1491}
1492