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