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