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