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