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