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