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