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