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