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