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