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