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