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