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