GsmDataConnectionTracker.java revision 421c72b6773582dd1473ace44c42d4e0ee1287e3
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        setState(State.CONNECTED);
752        phone.notifyDataConnection(reason);
753        startNetStatPoll();
754        // reset reconnect timer
755        mRetryMgr.resetRetryCount();
756        mReregisterOnReconnectFailure = false;
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.Gservices.getInt(mResolver,
782                    Settings.Gservices.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
783                    DEFAULT_MAX_PDP_RESET_FAIL);
784            if (mPdpResetCount < maxPdpReset) {
785                mPdpResetCount++;
786                EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, sentSinceLastRecv);
787                cleanUpConnection(true, Phone.REASON_PDP_RESET);
788            } else {
789                mPdpResetCount = 0;
790                EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_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        phone.mCM.setRadioPower(false, null);
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            try {
842                txPkts = netstat.getMobileTxPackets();
843                rxPkts = netstat.getMobileRxPackets();
844            } catch (RemoteException e) {
845                txPkts = 0;
846                rxPkts = 0;
847            }
848
849            //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
850
851            if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
852                sent = txPkts - preTxPkts;
853                received = rxPkts - preRxPkts;
854
855                if ( sent > 0 && received > 0 ) {
856                    sentSinceLastRecv = 0;
857                    newActivity = Activity.DATAINANDOUT;
858                    mPdpResetCount = 0;
859                } else if (sent > 0 && received == 0) {
860                    if (phone.getState() == Phone.State.IDLE) {
861                        sentSinceLastRecv += sent;
862                    } else {
863                        sentSinceLastRecv = 0;
864                    }
865                    newActivity = Activity.DATAOUT;
866                } else if (sent == 0 && received > 0) {
867                    sentSinceLastRecv = 0;
868                    newActivity = Activity.DATAIN;
869                    mPdpResetCount = 0;
870                } else if (sent == 0 && received == 0) {
871                    newActivity = Activity.NONE;
872                } else {
873                    sentSinceLastRecv = 0;
874                    newActivity = Activity.NONE;
875                }
876
877                if (activity != newActivity && mIsScreenOn) {
878                    activity = newActivity;
879                    phone.notifyDataActivity();
880                }
881            }
882
883            int watchdogTrigger = Settings.Gservices.getInt(mResolver,
884                    Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
885                    NUMBER_SENT_PACKETS_OF_HANG);
886
887            if (sentSinceLastRecv >= watchdogTrigger) {
888                // we already have NUMBER_SENT_PACKETS sent without ack
889                if (mNoRecvPollCount == 0) {
890                    EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED,
891                            sentSinceLastRecv);
892                }
893
894                int noRecvPollLimit = Settings.Gservices.getInt(mResolver,
895                        Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT);
896
897                if (mNoRecvPollCount < noRecvPollLimit) {
898                    // It's possible the PDP context went down and we weren't notified.
899                    // Start polling the context list in an attempt to recover.
900                    if (DBG) log("no DATAIN in a while; polling PDP");
901                    phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
902
903                    mNoRecvPollCount++;
904
905                    // Slow down the poll interval to let things happen
906                    netStatPollPeriod = Settings.Gservices.getInt(mResolver,
907                            Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
908                            POLL_NETSTAT_SLOW_MILLIS);
909                } else {
910                    if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) +
911                                        " pkts since last received");
912                    // We've exceeded the threshold.  Run ping test as a final check;
913                    // it will proceed with recovery if ping fails.
914                    stopNetStatPoll();
915                    Thread pingTest = new Thread() {
916                        public void run() {
917                            runPingTest();
918                        }
919                    };
920                    mPingTestActive = true;
921                    pingTest.start();
922                }
923            } else {
924                mNoRecvPollCount = 0;
925                if (mIsScreenOn) {
926                    netStatPollPeriod = Settings.Gservices.getInt(mResolver,
927                            Settings.Gservices.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
928                } else {
929                    netStatPollPeriod = Settings.Gservices.getInt(mResolver,
930                            Settings.Gservices.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
931                            POLL_NETSTAT_SCREEN_OFF_MILLIS);
932                }
933            }
934
935            if (netStatPollEnabled) {
936                mDataConnectionTracker.postDelayed(this, netStatPollPeriod);
937            }
938        }
939    };
940
941    private void runPingTest () {
942        int status = -1;
943        try {
944            String address = Settings.Gservices.getString(mResolver,
945                    Settings.Gservices.PDP_WATCHDOG_PING_ADDRESS);
946            int deadline = Settings.Gservices.getInt(mResolver,
947                        Settings.Gservices.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE);
948            if (DBG) log("pinging " + address + " for " + deadline + "s");
949            if (address != null && !NULL_IP.equals(address)) {
950                Process p = Runtime.getRuntime()
951                                .exec("ping -c 1 -i 1 -w "+ deadline + " " + address);
952                status = p.waitFor();
953            }
954        } catch (IOException e) {
955            Log.w(LOG_TAG, "ping failed: IOException");
956        } catch (Exception e) {
957            Log.w(LOG_TAG, "exception trying to ping");
958        }
959
960        if (status == 0) {
961            // ping succeeded.  False alarm.  Reset netStatPoll.
962            // ("-1" for this event indicates a false alarm)
963            EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, -1);
964            mPdpResetCount = 0;
965            sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL));
966        } else {
967            // ping failed.  Proceed with recovery.
968            sendMessage(obtainMessage(EVENT_START_RECOVERY));
969        }
970    }
971
972    /**
973     * Returns true if the last fail cause is something that
974     * seems like it deserves an error notification.
975     * Transient errors are ignored
976     */
977    private boolean shouldPostNotification(PdpConnection.FailCause  cause) {
978        boolean shouldPost = true;
979        // TODO CHECK
980        // if (dataLink != null) {
981        //    shouldPost = dataLink.getLastLinkExitCode() != DataLink.EXIT_OPEN_FAILED;
982        //}
983        return (shouldPost && cause != PdpConnection.FailCause.UNKNOWN);
984    }
985
986    /**
987     * Return true if data connection need to be setup after disconnected due to
988     * reason.
989     *
990     * @param reason the reason why data is disconnected
991     * @return true if try setup data connection is need for this reason
992     */
993    private boolean retryAfterDisconnected(String reason) {
994        boolean retry = true;
995
996        if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
997            retry = false;
998        }
999        return retry;
1000    }
1001
1002    private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
1003        if (state == State.FAILED) {
1004            if (!mRetryMgr.isRetryNeeded()) {
1005                if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1006                    // if no more retries on a secondary APN attempt, tell the world and revert.
1007                    phone.notifyDataConnection(Phone.REASON_APN_FAILED);
1008                    onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
1009                    return;
1010                }
1011                if (mReregisterOnReconnectFailure) {
1012                    // We've re-registerd once now just retry forever.
1013                    mRetryMgr.retryForeverUsingLastTimeout();
1014                } else {
1015                    // Try to re-register to the network.
1016                    Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
1017                    mReregisterOnReconnectFailure = true;
1018                    mGsmPhone.mSST.reRegisterNetwork(null);
1019                    mRetryMgr.resetRetryCount();
1020                    return;
1021                }
1022            }
1023
1024            int nextReconnectDelay = mRetryMgr.getRetryTimer();
1025            Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for "
1026                    + (nextReconnectDelay / 1000) + "s");
1027
1028            AlarmManager am =
1029                (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
1030            Intent intent = new Intent(INTENT_RECONNECT_ALARM);
1031            intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
1032            mReconnectIntent = PendingIntent.getBroadcast(
1033                    phone.getContext(), 0, intent, 0);
1034            am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1035                    SystemClock.elapsedRealtime() + nextReconnectDelay,
1036                    mReconnectIntent);
1037
1038            mRetryMgr.increaseRetryCount();
1039
1040            if (!shouldPostNotification(lastFailCauseCode)) {
1041                Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification "
1042                                + "-- likely transient error");
1043            } else {
1044                notifyNoData(lastFailCauseCode);
1045            }
1046        }
1047    }
1048
1049    private void notifyNoData(PdpConnection.FailCause lastFailCauseCode) {
1050        setState(State.FAILED);
1051    }
1052
1053    protected void onRecordsLoaded() {
1054        createAllApnList();
1055        if (state == State.FAILED) {
1056            cleanUpConnection(false, null);
1057        }
1058        sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
1059    }
1060
1061    @Override
1062    protected void onEnableNewApn() {
1063        // change our retry manager to use the appropriate numbers for the new APN
1064        if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1065            mRetryMgr = mDefaultRetryManager;
1066        } else {
1067            mRetryMgr = mSecondaryRetryManager;
1068        }
1069        mRetryMgr.resetRetryCount();
1070
1071        // TODO:  To support simultaneous PDP contexts, this should really only call
1072        // cleanUpConnection if it needs to free up a PdpConnection.
1073        cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
1074    }
1075
1076    protected boolean onTrySetupData(String reason) {
1077        return trySetupData(reason);
1078    }
1079
1080    @Override
1081    protected void onRoamingOff() {
1082        trySetupData(Phone.REASON_ROAMING_OFF);
1083    }
1084
1085    @Override
1086    protected void onRoamingOn() {
1087        if (getDataOnRoamingEnabled()) {
1088            trySetupData(Phone.REASON_ROAMING_ON);
1089        } else {
1090            if (DBG) log("Tear down data connection on roaming.");
1091            cleanUpConnection(true, Phone.REASON_ROAMING_ON);
1092        }
1093    }
1094
1095    protected void onRadioAvailable() {
1096        if (phone.getSimulatedRadioControl() != null) {
1097            // Assume data is connected on the simulator
1098            // FIXME  this can be improved
1099            setState(State.CONNECTED);
1100            phone.notifyDataConnection(null);
1101
1102            Log.i(LOG_TAG, "We're on the simulator; assuming data is connected");
1103        }
1104
1105        if (state != State.IDLE) {
1106            cleanUpConnection(true, null);
1107        }
1108    }
1109
1110    protected void onRadioOffOrNotAvailable() {
1111        // Make sure our reconnect delay starts at the initial value
1112        // next time the radio comes on
1113        mRetryMgr.resetRetryCount();
1114        mReregisterOnReconnectFailure = false;
1115
1116        if (phone.getSimulatedRadioControl() != null) {
1117            // Assume data is connected on the simulator
1118            // FIXME  this can be improved
1119            Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless");
1120        } else {
1121            if (DBG) log("Radio is off and clean up all connection");
1122            // TODO: Should we reset mRequestedApnType to "default"?
1123            cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF);
1124        }
1125    }
1126
1127    protected void onDataSetupComplete(AsyncResult ar) {
1128        String reason = null;
1129        if (ar.userObj instanceof String) {
1130            reason = (String) ar.userObj;
1131        }
1132
1133        if (ar.exception == null) {
1134            // everything is setup
1135            if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
1136                SystemProperties.set("gsm.defaultpdpcontext.active", "true");
1137                        if (canSetPreferApn && preferredApn == null) {
1138                            Log.d(LOG_TAG, "PREFERED APN is null");
1139                            preferredApn = mActiveApn;
1140                            setPreferredApn(preferredApn.id);
1141                        }
1142            } else {
1143                SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1144            }
1145            notifyDefaultData(reason);
1146
1147            // TODO: For simultaneous PDP support, we need to build another
1148            // trigger another TRY_SETUP_DATA for the next APN type.  (Note
1149            // that the existing connection may service that type, in which
1150            // case we should try the next type, etc.
1151        } else {
1152            PdpConnection.FailCause cause;
1153            cause = (PdpConnection.FailCause) (ar.result);
1154            if(DBG) log("PDP setup failed " + cause);
1155                    // Log this failure to the Event Logs.
1156            if (cause.isEventLoggable()) {
1157                int cid = -1;
1158                GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
1159                if (loc != null) cid = loc.getCid();
1160
1161                EventLog.List val = new EventLog.List(
1162                        cause.ordinal(), cid,
1163                        TelephonyManager.getDefault().getNetworkType());
1164                EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val);
1165            }
1166
1167            // No try for permanent failure
1168            if (cause.isPermanentFail()) {
1169                notifyNoData(cause);
1170                if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1171                    phone.notifyDataConnection(Phone.REASON_APN_FAILED);
1172                    onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
1173                }
1174                return;
1175            }
1176
1177            waitingApns.remove(0);
1178            if (waitingApns.isEmpty()) {
1179                // No more to try, start delayed retry
1180                startDelayedRetry(cause, reason);
1181            } else {
1182                // we still have more apns to try
1183                setState(State.SCANNING);
1184                // Wait a bit before trying the next APN, so that
1185                // we're not tying up the RIL command channel
1186                sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS);
1187            }
1188        }
1189    }
1190
1191    protected void onDisconnectDone(AsyncResult ar) {
1192        String reason = null;
1193        if(DBG) log("EVENT_DISCONNECT_DONE");
1194        if (ar.userObj instanceof String) {
1195           reason = (String) ar.userObj;
1196        }
1197        setState(State.IDLE);
1198        phone.notifyDataConnection(reason);
1199        mActiveApn = null;
1200        if (retryAfterDisconnected(reason)) {
1201            trySetupData(reason);
1202        }
1203    }
1204
1205    protected void onPollPdp() {
1206        if (state == State.CONNECTED) {
1207            // only poll when connected
1208            phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1209            sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
1210        }
1211    }
1212
1213    protected void onVoiceCallStarted() {
1214        if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) {
1215            stopNetStatPoll();
1216            phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1217        }
1218    }
1219
1220    protected void onVoiceCallEnded() {
1221        if (state == State.CONNECTED) {
1222            if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
1223                startNetStatPoll();
1224                phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1225            } else {
1226                // clean slate after call end.
1227                resetPollStats();
1228            }
1229        } else {
1230            // reset reconnect timer
1231            mRetryMgr.resetRetryCount();
1232            mReregisterOnReconnectFailure = false;
1233            // in case data setup was attempted when we were on a voice call
1234            trySetupData(Phone.REASON_VOICE_CALL_ENDED);
1235        }
1236    }
1237
1238    protected void onCleanUpConnection(boolean tearDown, String reason) {
1239        cleanUpConnection(tearDown, reason);
1240    }
1241
1242    /**
1243     * Based on the sim operator numeric, create a list for all possible pdps
1244     * with all apns associated with that pdp
1245     *
1246     *
1247     */
1248    private void createAllApnList() {
1249        allApns = new ArrayList<ApnSetting>();
1250        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
1251
1252        if (operator != null) {
1253            String selection = "numeric = '" + operator + "'";
1254
1255            Cursor cursor = phone.getContext().getContentResolver().query(
1256                    Telephony.Carriers.CONTENT_URI, null, selection, null, null);
1257
1258            if (cursor != null) {
1259                if (cursor.getCount() > 0) {
1260                    allApns = createApnList(cursor);
1261                    // TODO: Figure out where this fits in.  This basically just
1262                    // writes the pap-secrets file.  No longer tied to PdpConnection
1263                    // object.  Not used on current platform (no ppp).
1264                    //PdpConnection pdp = pdpList.get(pdp_name);
1265                    //if (pdp != null && pdp.dataLink != null) {
1266                    //    pdp.dataLink.setPasswordInfo(cursor);
1267                    //}
1268                }
1269                cursor.close();
1270            }
1271        }
1272
1273        if (allApns.isEmpty()) {
1274            if (DBG) log("No APN found for carrier: " + operator);
1275            preferredApn = null;
1276            notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN);
1277        } else {
1278            preferredApn = getPreferredApn();
1279            Log.d(LOG_TAG, "Get PreferredAPN");
1280            if (preferredApn != null && !preferredApn.numeric.equals(operator)) {
1281                preferredApn = null;
1282                setPreferredApn(-1);
1283            }
1284        }
1285    }
1286
1287    private void createAllPdpList() {
1288        pdpList = new ArrayList<DataConnection>();
1289        DataConnection pdp;
1290
1291        for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) {
1292            pdp = new PdpConnection(mGsmPhone);
1293            pdpList.add(pdp);
1294         }
1295    }
1296
1297    private void destroyAllPdpList() {
1298        if(pdpList != null) {
1299            PdpConnection pdp;
1300            pdpList.removeAll(pdpList);
1301        }
1302    }
1303
1304    /**
1305     *
1306     * @return waitingApns list to be used to create PDP
1307     *          error when waitingApns.isEmpty()
1308     */
1309    private ArrayList<ApnSetting> buildWaitingApns() {
1310        ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
1311        String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
1312
1313        if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1314            if (canSetPreferApn && preferredApn != null) {
1315                Log.i(LOG_TAG, "Preferred APN:" + operator + ":"
1316                        + preferredApn.numeric + ":" + preferredApn);
1317                if (preferredApn.numeric.equals(operator)) {
1318                    Log.i(LOG_TAG, "Waiting APN set to preferred APN");
1319                    apnList.add(preferredApn);
1320                    return apnList;
1321                } else {
1322                    setPreferredApn(-1);
1323                    preferredApn = null;
1324                }
1325            }
1326        }
1327
1328        if (allApns != null) {
1329            for (ApnSetting apn : allApns) {
1330                if (apn.canHandleType(mRequestedApnType)) {
1331                    apnList.add(apn);
1332                }
1333            }
1334        }
1335        return apnList;
1336    }
1337
1338    /**
1339     * Get next apn in waitingApns
1340     * @return the first apn found in waitingApns, null if none
1341     */
1342    private ApnSetting getNextApn() {
1343        ArrayList<ApnSetting> list = waitingApns;
1344        ApnSetting apn = null;
1345
1346        if (list != null) {
1347            if (!list.isEmpty()) {
1348                apn = list.get(0);
1349            }
1350        }
1351        return apn;
1352    }
1353
1354    private String apnListToString (ArrayList<ApnSetting> apns) {
1355        StringBuilder result = new StringBuilder();
1356        for (int i = 0, size = apns.size(); i < size; i++) {
1357            result.append('[')
1358                  .append(apns.get(i).toString())
1359                  .append(']');
1360        }
1361        return result.toString();
1362    }
1363
1364    private void startDelayedRetry(PdpConnection.FailCause cause, String reason) {
1365        notifyNoData(cause);
1366        reconnectAfterFail(cause, reason);
1367    }
1368
1369    private void setPreferredApn(int pos) {
1370        if (!canSetPreferApn) {
1371            return;
1372        }
1373
1374        ContentResolver resolver = phone.getContext().getContentResolver();
1375        resolver.delete(PREFERAPN_URI, null, null);
1376
1377        if (pos >= 0) {
1378            ContentValues values = new ContentValues();
1379            values.put(APN_ID, pos);
1380            resolver.insert(PREFERAPN_URI, values);
1381        }
1382    }
1383
1384    private ApnSetting getPreferredApn() {
1385        if (allApns.isEmpty()) {
1386            return null;
1387        }
1388
1389        Cursor cursor = phone.getContext().getContentResolver().query(
1390                PREFERAPN_URI, new String[] { "_id", "name", "apn" },
1391                null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
1392
1393        if (cursor != null) {
1394            canSetPreferApn = true;
1395        } else {
1396            canSetPreferApn = false;
1397        }
1398
1399        if (canSetPreferApn && cursor.getCount() > 0) {
1400            int pos;
1401            cursor.moveToFirst();
1402            pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
1403            for(ApnSetting p:allApns) {
1404                if (p.id == pos && p.canHandleType(mRequestedApnType)) {
1405                    cursor.close();
1406                    return p;
1407                }
1408            }
1409        }
1410
1411        if (cursor != null) {
1412            cursor.close();
1413        }
1414
1415        return null;
1416    }
1417
1418    public void handleMessage (Message msg) {
1419        if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg);
1420
1421        if (!mGsmPhone.mIsTheCurrentActivePhone) {
1422            Log.d(LOG_TAG, "Ignore GSM msgs since GSM phone is inactive");
1423            return;
1424        }
1425
1426        switch (msg.what) {
1427            case EVENT_RECORDS_LOADED:
1428                onRecordsLoaded();
1429                break;
1430
1431            case EVENT_GPRS_DETACHED:
1432                onGprsDetached();
1433                break;
1434
1435            case EVENT_GPRS_ATTACHED:
1436                onGprsAttached();
1437                break;
1438
1439            case EVENT_DATA_STATE_CHANGED:
1440                onPdpStateChanged((AsyncResult) msg.obj, false);
1441                break;
1442
1443            case EVENT_GET_PDP_LIST_COMPLETE:
1444                onPdpStateChanged((AsyncResult) msg.obj, true);
1445                break;
1446
1447            case EVENT_POLL_PDP:
1448                onPollPdp();
1449                break;
1450
1451            case EVENT_START_NETSTAT_POLL:
1452                mPingTestActive = false;
1453                startNetStatPoll();
1454                break;
1455
1456            case EVENT_START_RECOVERY:
1457                mPingTestActive = false;
1458                doRecovery();
1459                break;
1460
1461            case EVENT_APN_CHANGED:
1462                onApnChanged();
1463                break;
1464
1465            case EVENT_PS_RESTRICT_ENABLED:
1466                /**
1467                 * We don't need to explicitly to tear down the PDP context
1468                 * when PS restricted is enabled. The base band will deactive
1469                 * PDP context and notify us with PDP_CONTEXT_CHANGED.
1470                 * But we should stop the network polling and prevent reset PDP.
1471                 */
1472                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
1473                stopNetStatPoll();
1474                mIsPsRestricted = true;
1475                break;
1476
1477            case EVENT_PS_RESTRICT_DISABLED:
1478                /**
1479                 * When PS restrict is removed, we need setup PDP connection if
1480                 * PDP connection is down.
1481                 */
1482                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
1483                mIsPsRestricted  = false;
1484                if (state == State.CONNECTED) {
1485                    startNetStatPoll();
1486                } else {
1487                    if (state == State.FAILED) {
1488                        cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED);
1489                        mRetryMgr.resetRetryCount();
1490                        mReregisterOnReconnectFailure = false;
1491                    }
1492                    trySetupData(Phone.REASON_PS_RESTRICT_ENABLED);
1493                }
1494                break;
1495
1496            default:
1497                // handle the message in the super class DataConnectionTracker
1498                super.handleMessage(msg);
1499                break;
1500        }
1501    }
1502
1503    protected void log(String s) {
1504        Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
1505    }
1506
1507}
1508