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