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