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