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;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.SharedPreferences;
27import android.database.ContentObserver;
28import android.net.LinkCapabilities;
29import android.net.LinkProperties;
30import android.net.NetworkInfo;
31import android.net.TrafficStats;
32import android.net.wifi.WifiManager;
33import android.os.AsyncResult;
34import android.os.Bundle;
35import android.os.Handler;
36import android.os.Message;
37import android.os.Messenger;
38import android.os.SystemClock;
39import android.os.SystemProperties;
40import android.preference.PreferenceManager;
41import android.provider.Settings;
42import android.provider.Settings.SettingNotFoundException;
43import android.telephony.ServiceState;
44import android.telephony.TelephonyManager;
45import android.text.TextUtils;
46import android.util.EventLog;
47import android.util.Log;
48
49import com.android.internal.R;
50import com.android.internal.telephony.DataConnection.FailCause;
51import com.android.internal.telephony.DctConstants;
52import com.android.internal.telephony.uicc.UiccController;
53import com.android.internal.util.AsyncChannel;
54
55import java.io.FileDescriptor;
56import java.io.PrintWriter;
57import java.util.ArrayList;
58import java.util.HashMap;
59import java.util.Map.Entry;
60import java.util.Set;
61import java.util.concurrent.ConcurrentHashMap;
62import java.util.concurrent.atomic.AtomicInteger;
63import java.util.concurrent.atomic.AtomicReference;
64
65/**
66 * {@hide}
67 */
68public abstract class DataConnectionTracker extends Handler {
69    protected static final boolean DBG = true;
70    protected static final boolean VDBG = false;
71    protected static final boolean RADIO_TESTS = false;
72
73    /**
74     * Constants for the data connection activity:
75     * physical link down/up
76     */
77    protected static final int DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE = 0;
78    protected static final int DATA_CONNECTION_ACTIVE_PH_LINK_DOWN = 1;
79    protected static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2;
80
81    /** Delay between APN attempts.
82        Note the property override mechanism is there just for testing purpose only. */
83    protected static final int APN_DELAY_MILLIS =
84                                SystemProperties.getInt("persist.radio.apn_delay", 5000);
85
86    protected Object mDataEnabledLock = new Object();
87
88    // responds to the setInternalDataEnabled call - used internally to turn off data
89    // for example during emergency calls
90    protected boolean mInternalDataEnabled = true;
91
92    // responds to public (user) API to enable/disable data use
93    // independent of mInternalDataEnabled and requests for APN access
94    // persisted
95    protected boolean mUserDataEnabled = true;
96
97    // TODO: move away from static state once 5587429 is fixed.
98    protected static boolean sPolicyDataEnabled = true;
99
100    private boolean[] dataEnabled = new boolean[DctConstants.APN_NUM_TYPES];
101
102    private int enabledCount = 0;
103
104    /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
105    protected String mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
106
107    /** Retry configuration: A doubling of retry times from 5secs to 30minutes */
108    protected static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000,"
109        + "5000,10000,20000,40000,80000:5000,160000:5000,"
110        + "320000:5000,640000:5000,1280000:5000,1800000:5000";
111
112    /** Retry configuration for secondary networks: 4 tries in 20 sec */
113    protected static final String SECONDARY_DATA_RETRY_CONFIG =
114            "max_retries=3, 5000, 5000, 5000";
115
116    /** Slow poll when attempting connection recovery. */
117    protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000;
118    /** Default max failure count before attempting to network re-registration. */
119    protected static final int DEFAULT_MAX_PDP_RESET_FAIL = 3;
120
121    /**
122     * After detecting a potential connection problem, this is the max number
123     * of subsequent polls before attempting recovery.
124     */
125    protected static final int NO_RECV_POLL_LIMIT = 24;
126    // 1 sec. default polling interval when screen is on.
127    protected static final int POLL_NETSTAT_MILLIS = 1000;
128    // 10 min. default polling interval when screen is off.
129    protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
130    // 2 min for round trip time
131    protected static final int POLL_LONGEST_RTT = 120 * 1000;
132    // Default sent packets without ack which triggers initial recovery steps
133    protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
134    // how long to wait before switching back to default APN
135    protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000;
136    // system property that can override the above value
137    protected static final String APN_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore";
138    // represents an invalid IP address
139    protected static final String NULL_IP = "0.0.0.0";
140
141    // Default for the data stall alarm while non-aggressive stall detection
142    protected static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6;
143    // Default for the data stall alarm for aggressive stall detection
144    protected static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60;
145    // If attempt is less than this value we're doing first level recovery
146    protected static final int DATA_STALL_NO_RECV_POLL_LIMIT = 1;
147    // Tag for tracking stale alarms
148    protected static final String DATA_STALL_ALARM_TAG_EXTRA = "data.stall.alram.tag";
149
150    protected static final boolean DATA_STALL_SUSPECTED = true;
151    protected static final boolean DATA_STALL_NOT_SUSPECTED = false;
152
153    protected String RADIO_RESET_PROPERTY = "gsm.radioreset";
154
155
156    // TODO: See if we can remove INTENT_RECONNECT_ALARM
157    //       having to have different values for GSM and
158    //       CDMA. If so we can then remove the need for
159    //       getActionIntentReconnectAlarm.
160    protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON =
161        "reconnect_alarm_extra_reason";
162
163    // Used for debugging. Send the INTENT with an optional counter value with the number
164    // of times the setup is to fail before succeeding. If the counter isn't passed the
165    // setup will fail once. Example fail two times with FailCause.SIGNAL_LOST(-3)
166    // adb shell am broadcast \
167    //  -a com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter \
168    //  --ei fail_data_setup_counter 3 --ei fail_data_setup_fail_cause -3
169    protected static final String INTENT_SET_FAIL_DATA_SETUP_COUNTER =
170        "com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter";
171    protected static final String FAIL_DATA_SETUP_COUNTER = "fail_data_setup_counter";
172    protected int mFailDataSetupCounter = 0;
173    protected static final String FAIL_DATA_SETUP_FAIL_CAUSE = "fail_data_setup_fail_cause";
174    protected FailCause mFailDataSetupFailCause = FailCause.ERROR_UNSPECIFIED;
175
176    protected static final String DEFALUT_DATA_ON_BOOT_PROP = "net.def_data_on_boot";
177
178    // member variables
179    protected PhoneBase mPhone;
180    protected UiccController mUiccController;
181    protected AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
182    protected DctConstants.Activity mActivity = DctConstants.Activity.NONE;
183    protected DctConstants.State mState = DctConstants.State.IDLE;
184    protected Handler mDataConnectionTracker = null;
185
186    protected long mTxPkts;
187    protected long mRxPkts;
188    protected int mNetStatPollPeriod;
189    protected boolean mNetStatPollEnabled = false;
190
191    protected TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0);
192    // Used to track stale data stall alarms.
193    protected int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime();
194    // The current data stall alarm intent
195    protected PendingIntent mDataStallAlarmIntent = null;
196    // Number of packets sent since the last received packet
197    protected long mSentSinceLastRecv;
198    // Controls when a simple recovery attempt it to be tried
199    protected int mNoRecvPollCount = 0;
200
201    // wifi connection status will be updated by sticky intent
202    protected boolean mIsWifiConnected = false;
203
204    /** Intent sent when the reconnect alarm fires. */
205    protected PendingIntent mReconnectIntent = null;
206
207    /** CID of active data connection */
208    protected int mCidActive;
209
210    // When false we will not auto attach and manually attaching is required.
211    protected boolean mAutoAttachOnCreation = false;
212
213    // State of screen
214    // (TODO: Reconsider tying directly to screen, maybe this is
215    //        really a lower power mode")
216    protected boolean mIsScreenOn = true;
217
218    /** Allows the generation of unique Id's for DataConnection objects */
219    protected AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
220
221    /** The data connections. */
222    protected HashMap<Integer, DataConnection> mDataConnections =
223        new HashMap<Integer, DataConnection>();
224
225    /** The data connection async channels */
226    protected HashMap<Integer, DataConnectionAc> mDataConnectionAsyncChannels =
227        new HashMap<Integer, DataConnectionAc>();
228
229    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
230    protected HashMap<String, Integer> mApnToDataConnectionId =
231                                    new HashMap<String, Integer>();
232
233    /** Phone.APN_TYPE_* ===> ApnContext */
234    protected ConcurrentHashMap<String, ApnContext> mApnContexts =
235                                    new ConcurrentHashMap<String, ApnContext>();
236
237    /* Currently active APN */
238    protected ApnSetting mActiveApn;
239
240    /** allApns holds all apns */
241    protected ArrayList<ApnSetting> mAllApns = null;
242
243    /** preferred apn */
244    protected ApnSetting mPreferredApn = null;
245
246    /** Is packet service restricted by network */
247    protected boolean mIsPsRestricted = false;
248
249    /* Once disposed dont handle any messages */
250    protected boolean mIsDisposed = false;
251
252    protected ContentResolver mResolver;
253
254    protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
255    {
256        @Override
257        public void onReceive(Context context, Intent intent)
258        {
259            String action = intent.getAction();
260            if (DBG) log("onReceive: action=" + action);
261            if (action.equals(Intent.ACTION_SCREEN_ON)) {
262                mIsScreenOn = true;
263                stopNetStatPoll();
264                startNetStatPoll();
265                restartDataStallAlarm();
266            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
267                mIsScreenOn = false;
268                stopNetStatPoll();
269                startNetStatPoll();
270                restartDataStallAlarm();
271            } else if (action.startsWith(getActionIntentReconnectAlarm())) {
272                log("Reconnect alarm. Previous state was " + mState);
273                onActionIntentReconnectAlarm(intent);
274            } else if (action.equals(getActionIntentDataStallAlarm())) {
275                onActionIntentDataStallAlarm(intent);
276            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
277                final android.net.NetworkInfo networkInfo = (NetworkInfo)
278                        intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
279                mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
280            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
281                final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
282                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
283
284                if (!enabled) {
285                    // when WiFi got disabled, the NETWORK_STATE_CHANGED_ACTION
286                    // quit and won't report disconnected until next enabling.
287                    mIsWifiConnected = false;
288                }
289            } else if (action.equals(INTENT_SET_FAIL_DATA_SETUP_COUNTER)) {
290                mFailDataSetupCounter = intent.getIntExtra(FAIL_DATA_SETUP_COUNTER, 1);
291                mFailDataSetupFailCause = FailCause.fromInt(
292                        intent.getIntExtra(FAIL_DATA_SETUP_FAIL_CAUSE,
293                                                    FailCause.ERROR_UNSPECIFIED.getErrorCode()));
294                if (DBG) log("set mFailDataSetupCounter=" + mFailDataSetupCounter +
295                        " mFailDataSetupFailCause=" + mFailDataSetupFailCause);
296            }
297        }
298    };
299
300    private final DataRoamingSettingObserver mDataRoamingSettingObserver;
301    private Runnable mPollNetStat = new Runnable()
302    {
303        @Override
304        public void run() {
305            updateDataActivity();
306
307            if (mIsScreenOn) {
308                mNetStatPollPeriod = Settings.Global.getInt(mResolver,
309                        Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
310            } else {
311                mNetStatPollPeriod = Settings.Global.getInt(mResolver,
312                        Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
313                        POLL_NETSTAT_SCREEN_OFF_MILLIS);
314            }
315
316            if (mNetStatPollEnabled) {
317                mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
318            }
319        }
320    };
321
322    private class DataRoamingSettingObserver extends ContentObserver {
323        public DataRoamingSettingObserver(Handler handler) {
324            super(handler);
325        }
326
327        public void register(Context context) {
328            final ContentResolver resolver = context.getContentResolver();
329            resolver.registerContentObserver(
330                    Settings.Global.getUriFor(Settings.Global.DATA_ROAMING), false, this);
331        }
332
333        public void unregister(Context context) {
334            final ContentResolver resolver = context.getContentResolver();
335            resolver.unregisterContentObserver(this);
336        }
337
338        @Override
339        public void onChange(boolean selfChange) {
340            // already running on mPhone handler thread
341            handleDataOnRoamingChange();
342        }
343    }
344
345    /**
346     * Maintian the sum of transmit and receive packets.
347     *
348     * The packet counts are initizlied and reset to -1 and
349     * remain -1 until they can be updated.
350     */
351    public class TxRxSum {
352        public long txPkts;
353        public long rxPkts;
354
355        public TxRxSum() {
356            reset();
357        }
358
359        public TxRxSum(long txPkts, long rxPkts) {
360            this.txPkts = txPkts;
361            this.rxPkts = rxPkts;
362        }
363
364        public TxRxSum(TxRxSum sum) {
365            txPkts = sum.txPkts;
366            rxPkts = sum.rxPkts;
367        }
368
369        public void reset() {
370            txPkts = -1;
371            rxPkts = -1;
372        }
373
374        public String toString() {
375            return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
376        }
377
378        public void updateTxRxSum() {
379            this.txPkts = TrafficStats.getMobileTxPackets();
380            this.rxPkts = TrafficStats.getMobileRxPackets();
381        }
382    }
383
384    protected boolean isDataSetupCompleteOk(AsyncResult ar) {
385        if (ar.exception != null) {
386            if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result);
387            return false;
388        }
389        if (mFailDataSetupCounter <= 0) {
390            if (DBG) log("isDataSetupCompleteOk return true");
391            return true;
392        }
393        ar.result = mFailDataSetupFailCause;
394        if (DBG) {
395            log("isDataSetupCompleteOk return false" +
396                    " mFailDataSetupCounter=" + mFailDataSetupCounter +
397                    " mFailDataSetupFailCause=" + mFailDataSetupFailCause);
398        }
399        mFailDataSetupCounter -= 1;
400        return false;
401    }
402
403    protected void onActionIntentReconnectAlarm(Intent intent) {
404        String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
405        if (mState == DctConstants.State.FAILED) {
406            Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_CONNECTION);
407            msg.arg1 = 0; // tearDown is false
408            msg.arg2 = 0;
409            msg.obj = reason;
410            sendMessage(msg);
411        }
412        sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA));
413    }
414
415    protected void onActionIntentDataStallAlarm(Intent intent) {
416        if (VDBG) log("onActionIntentDataStallAlarm: action=" + intent.getAction());
417        Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM,
418                intent.getAction());
419        msg.arg1 = intent.getIntExtra(DATA_STALL_ALARM_TAG_EXTRA, 0);
420        sendMessage(msg);
421    }
422
423    /**
424     * Default constructor
425     */
426    protected DataConnectionTracker(PhoneBase phone) {
427        super();
428        if (DBG) log("DCT.constructor");
429        mPhone = phone;
430        mUiccController = UiccController.getInstance();
431        mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null);
432
433        IntentFilter filter = new IntentFilter();
434        filter.addAction(getActionIntentReconnectAlarm());
435        filter.addAction(Intent.ACTION_SCREEN_ON);
436        filter.addAction(Intent.ACTION_SCREEN_OFF);
437        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
438        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
439        filter.addAction(INTENT_SET_FAIL_DATA_SETUP_COUNTER);
440        filter.addAction(getActionIntentDataStallAlarm());
441
442        mUserDataEnabled = Settings.Global.getInt(
443                mPhone.getContext().getContentResolver(), Settings.Global.MOBILE_DATA, 1) == 1;
444
445        // TODO: Why is this registering the phone as the receiver of the intent
446        //       and not its own handler?
447        mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
448
449        // This preference tells us 1) initial condition for "dataEnabled",
450        // and 2) whether the RIL will setup the baseband to auto-PS attach.
451
452        dataEnabled[DctConstants.APN_DEFAULT_ID] =
453                SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP,true);
454        if (dataEnabled[DctConstants.APN_DEFAULT_ID]) {
455            enabledCount++;
456        }
457
458        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
459        mAutoAttachOnCreation = sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
460
461        // watch for changes to Settings.Global.DATA_ROAMING
462        mDataRoamingSettingObserver = new DataRoamingSettingObserver(mPhone);
463        mDataRoamingSettingObserver.register(mPhone.getContext());
464
465        mResolver = mPhone.getContext().getContentResolver();
466    }
467
468    public void dispose() {
469        if (DBG) log("DCT.dispose");
470        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
471            dcac.disconnect();
472        }
473        mDataConnectionAsyncChannels.clear();
474        mIsDisposed = true;
475        mPhone.getContext().unregisterReceiver(this.mIntentReceiver);
476        mDataRoamingSettingObserver.unregister(mPhone.getContext());
477        mUiccController.unregisterForIccChanged(this);
478    }
479
480    protected void broadcastMessenger() {
481        Intent intent = new Intent(DctConstants.ACTION_DATA_CONNECTION_TRACKER_MESSENGER);
482        intent.putExtra(DctConstants.EXTRA_MESSENGER, new Messenger(this));
483        mPhone.getContext().sendBroadcast(intent);
484    }
485
486    public DctConstants.Activity getActivity() {
487        return mActivity;
488    }
489
490    public boolean isApnTypeActive(String type) {
491        // TODO: support simultaneous with List instead
492        if (PhoneConstants.APN_TYPE_DUN.equals(type)) {
493            ApnSetting dunApn = fetchDunApn();
494            if (dunApn != null) {
495                return ((mActiveApn != null) && (dunApn.toString().equals(mActiveApn.toString())));
496            }
497        }
498        return mActiveApn != null && mActiveApn.canHandleType(type);
499    }
500
501    protected ApnSetting fetchDunApn() {
502        if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
503            log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
504            return null;
505        }
506        Context c = mPhone.getContext();
507        String apnData = Settings.Global.getString(c.getContentResolver(),
508                Settings.Global.TETHER_DUN_APN);
509        ApnSetting dunSetting = ApnSetting.fromString(apnData);
510        if (dunSetting != null) {
511            if (VDBG) log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
512            return dunSetting;
513        }
514
515        apnData = c.getResources().getString(R.string.config_tether_apndata);
516        dunSetting = ApnSetting.fromString(apnData);
517        if (VDBG) log("fetchDunApn: config_tether_apndata dunSetting=" + dunSetting);
518        return dunSetting;
519    }
520
521    public String[] getActiveApnTypes() {
522        String[] result;
523        if (mActiveApn != null) {
524            result = mActiveApn.types;
525        } else {
526            result = new String[1];
527            result[0] = PhoneConstants.APN_TYPE_DEFAULT;
528        }
529        return result;
530    }
531
532    /** TODO: See if we can remove */
533    public String getActiveApnString(String apnType) {
534        String result = null;
535        if (mActiveApn != null) {
536            result = mActiveApn.apn;
537        }
538        return result;
539    }
540
541    /**
542     * Modify {@link Settings.Global#DATA_ROAMING} value.
543     */
544    public void setDataOnRoamingEnabled(boolean enabled) {
545        if (getDataOnRoamingEnabled() != enabled) {
546            final ContentResolver resolver = mPhone.getContext().getContentResolver();
547            Settings.Global.putInt(resolver, Settings.Global.DATA_ROAMING, enabled ? 1 : 0);
548            // will trigger handleDataOnRoamingChange() through observer
549        }
550    }
551
552    /**
553     * Return current {@link Settings.Global#DATA_ROAMING} value.
554     */
555    public boolean getDataOnRoamingEnabled() {
556        try {
557            final ContentResolver resolver = mPhone.getContext().getContentResolver();
558            return Settings.Global.getInt(resolver, Settings.Global.DATA_ROAMING) != 0;
559        } catch (SettingNotFoundException snfe) {
560            return false;
561        }
562    }
563
564    private void handleDataOnRoamingChange() {
565        if (mPhone.getServiceState().getRoaming()) {
566            if (getDataOnRoamingEnabled()) {
567                resetAllRetryCounts();
568            }
569            sendMessage(obtainMessage(DctConstants.EVENT_ROAMING_ON));
570        }
571    }
572
573    // abstract methods
574    protected abstract String getActionIntentReconnectAlarm();
575    protected abstract String getActionIntentDataStallAlarm();
576    protected abstract void restartRadio();
577    protected abstract void log(String s);
578    protected abstract void loge(String s);
579    protected abstract boolean isDataAllowed();
580    protected abstract boolean isApnTypeAvailable(String type);
581    public    abstract DctConstants.State getState(String apnType);
582    protected abstract void setState(DctConstants.State s);
583    protected abstract void gotoIdleAndNotifyDataConnection(String reason);
584
585    protected abstract boolean onTrySetupData(String reason);
586    protected abstract void onRoamingOff();
587    protected abstract void onRoamingOn();
588    protected abstract void onRadioAvailable();
589    protected abstract void onRadioOffOrNotAvailable();
590    protected abstract void onDataSetupComplete(AsyncResult ar);
591    protected abstract void onDisconnectDone(int connId, AsyncResult ar);
592    protected abstract void onVoiceCallStarted();
593    protected abstract void onVoiceCallEnded();
594    protected abstract void onCleanUpConnection(boolean tearDown, int apnId, String reason);
595    protected abstract void onCleanUpAllConnections(String cause);
596    protected abstract boolean isDataPossible(String apnType);
597    protected abstract void onUpdateIcc();
598
599    @Override
600    public void handleMessage(Message msg) {
601        switch (msg.what) {
602            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
603                log("DISCONNECTED_CONNECTED: msg=" + msg);
604                DataConnectionAc dcac = (DataConnectionAc) msg.obj;
605                mDataConnectionAsyncChannels.remove(dcac.dataConnection.getDataConnectionId());
606                dcac.disconnected();
607                break;
608            }
609            case DctConstants.EVENT_ENABLE_NEW_APN:
610                onEnableApn(msg.arg1, msg.arg2);
611                break;
612
613            case DctConstants.EVENT_TRY_SETUP_DATA:
614                String reason = null;
615                if (msg.obj instanceof String) {
616                    reason = (String) msg.obj;
617                }
618                onTrySetupData(reason);
619                break;
620
621            case DctConstants.EVENT_DATA_STALL_ALARM:
622                onDataStallAlarm(msg.arg1);
623                break;
624
625            case DctConstants.EVENT_ROAMING_OFF:
626                if (getDataOnRoamingEnabled() == false) {
627                    resetAllRetryCounts();
628                }
629                onRoamingOff();
630                break;
631
632            case DctConstants.EVENT_ROAMING_ON:
633                onRoamingOn();
634                break;
635
636            case DctConstants.EVENT_RADIO_AVAILABLE:
637                onRadioAvailable();
638                break;
639
640            case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
641                onRadioOffOrNotAvailable();
642                break;
643
644            case DctConstants.EVENT_DATA_SETUP_COMPLETE:
645                mCidActive = msg.arg1;
646                onDataSetupComplete((AsyncResult) msg.obj);
647                break;
648
649            case DctConstants.EVENT_DISCONNECT_DONE:
650                log("DataConnectoinTracker.handleMessage: EVENT_DISCONNECT_DONE msg=" + msg);
651                onDisconnectDone(msg.arg1, (AsyncResult) msg.obj);
652                break;
653
654            case DctConstants.EVENT_VOICE_CALL_STARTED:
655                onVoiceCallStarted();
656                break;
657
658            case DctConstants.EVENT_VOICE_CALL_ENDED:
659                onVoiceCallEnded();
660                break;
661
662            case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS: {
663                onCleanUpAllConnections((String) msg.obj);
664                break;
665            }
666            case DctConstants.EVENT_CLEAN_UP_CONNECTION: {
667                boolean tearDown = (msg.arg1 == 0) ? false : true;
668                onCleanUpConnection(tearDown, msg.arg2, (String) msg.obj);
669                break;
670            }
671            case DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE: {
672                boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
673                onSetInternalDataEnabled(enabled);
674                break;
675            }
676            case DctConstants.EVENT_RESET_DONE: {
677                if (DBG) log("EVENT_RESET_DONE");
678                onResetDone((AsyncResult) msg.obj);
679                break;
680            }
681            case DctConstants.CMD_SET_USER_DATA_ENABLE: {
682                final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
683                if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
684                onSetUserDataEnabled(enabled);
685                break;
686            }
687            case DctConstants.CMD_SET_DEPENDENCY_MET: {
688                boolean met = (msg.arg1 == DctConstants.ENABLED) ? true : false;
689                if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
690                Bundle bundle = msg.getData();
691                if (bundle != null) {
692                    String apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
693                    if (apnType != null) {
694                        onSetDependencyMet(apnType, met);
695                    }
696                }
697                break;
698            }
699            case DctConstants.CMD_SET_POLICY_DATA_ENABLE: {
700                final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
701                onSetPolicyDataEnabled(enabled);
702                break;
703            }
704            case DctConstants.EVENT_ICC_CHANGED:
705                onUpdateIcc();
706                break;
707
708            default:
709                Log.e("DATA", "Unidentified event msg=" + msg);
710                break;
711        }
712    }
713
714    /**
715     * Report on whether data connectivity is enabled
716     *
717     * @return {@code false} if data connectivity has been explicitly disabled,
718     *         {@code true} otherwise.
719     */
720    public boolean getAnyDataEnabled() {
721        final boolean result;
722        synchronized (mDataEnabledLock) {
723            result = (mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled
724                    && (enabledCount != 0));
725        }
726        if (!result && DBG) log("getAnyDataEnabled " + result);
727        return result;
728    }
729
730    protected boolean isEmergency() {
731        final boolean result;
732        synchronized (mDataEnabledLock) {
733            result = mPhone.isInEcm() || mPhone.isInEmergencyCall();
734        }
735        log("isEmergency: result=" + result);
736        return result;
737    }
738
739    protected int apnTypeToId(String type) {
740        if (TextUtils.equals(type, PhoneConstants.APN_TYPE_DEFAULT)) {
741            return DctConstants.APN_DEFAULT_ID;
742        } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_MMS)) {
743            return DctConstants.APN_MMS_ID;
744        } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_SUPL)) {
745            return DctConstants.APN_SUPL_ID;
746        } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_DUN)) {
747            return DctConstants.APN_DUN_ID;
748        } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_HIPRI)) {
749            return DctConstants.APN_HIPRI_ID;
750        } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_IMS)) {
751            return DctConstants.APN_IMS_ID;
752        } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_FOTA)) {
753            return DctConstants.APN_FOTA_ID;
754        } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_CBS)) {
755            return DctConstants.APN_CBS_ID;
756        } else {
757            return DctConstants.APN_INVALID_ID;
758        }
759    }
760
761    protected String apnIdToType(int id) {
762        switch (id) {
763        case DctConstants.APN_DEFAULT_ID:
764            return PhoneConstants.APN_TYPE_DEFAULT;
765        case DctConstants.APN_MMS_ID:
766            return PhoneConstants.APN_TYPE_MMS;
767        case DctConstants.APN_SUPL_ID:
768            return PhoneConstants.APN_TYPE_SUPL;
769        case DctConstants.APN_DUN_ID:
770            return PhoneConstants.APN_TYPE_DUN;
771        case DctConstants.APN_HIPRI_ID:
772            return PhoneConstants.APN_TYPE_HIPRI;
773        case DctConstants.APN_IMS_ID:
774            return PhoneConstants.APN_TYPE_IMS;
775        case DctConstants.APN_FOTA_ID:
776            return PhoneConstants.APN_TYPE_FOTA;
777        case DctConstants.APN_CBS_ID:
778            return PhoneConstants.APN_TYPE_CBS;
779        default:
780            log("Unknown id (" + id + ") in apnIdToType");
781            return PhoneConstants.APN_TYPE_DEFAULT;
782        }
783    }
784
785    protected LinkProperties getLinkProperties(String apnType) {
786        int id = apnTypeToId(apnType);
787
788        if (isApnIdEnabled(id)) {
789            // TODO - remove this cdma-only hack and support multiple DCs.
790            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
791            return dcac.getLinkPropertiesSync();
792        } else {
793            return new LinkProperties();
794        }
795    }
796
797    protected LinkCapabilities getLinkCapabilities(String apnType) {
798        int id = apnTypeToId(apnType);
799        if (isApnIdEnabled(id)) {
800            // TODO - remove this cdma-only hack and support multiple DCs.
801            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
802            return dcac.getLinkCapabilitiesSync();
803        } else {
804            return new LinkCapabilities();
805        }
806    }
807
808    // tell all active apns of the current condition
809    protected void notifyDataConnection(String reason) {
810        for (int id = 0; id < DctConstants.APN_NUM_TYPES; id++) {
811            if (dataEnabled[id]) {
812                mPhone.notifyDataConnection(reason, apnIdToType(id));
813            }
814        }
815        notifyOffApnsOfAvailability(reason);
816    }
817
818    // a new APN has gone active and needs to send events to catch up with the
819    // current condition
820    private void notifyApnIdUpToCurrent(String reason, int apnId) {
821        switch (mState) {
822            case IDLE:
823            case INITING:
824                break;
825            case CONNECTING:
826            case SCANNING:
827                mPhone.notifyDataConnection(reason, apnIdToType(apnId),
828                        PhoneConstants.DataState.CONNECTING);
829                break;
830            case CONNECTED:
831            case DISCONNECTING:
832                mPhone.notifyDataConnection(reason, apnIdToType(apnId),
833                        PhoneConstants.DataState.CONNECTING);
834                mPhone.notifyDataConnection(reason, apnIdToType(apnId),
835                        PhoneConstants.DataState.CONNECTED);
836                break;
837        }
838    }
839
840    // since we normally don't send info to a disconnected APN, we need to do this specially
841    private void notifyApnIdDisconnected(String reason, int apnId) {
842        mPhone.notifyDataConnection(reason, apnIdToType(apnId),
843                PhoneConstants.DataState.DISCONNECTED);
844    }
845
846    // disabled apn's still need avail/unavail notificiations - send them out
847    protected void notifyOffApnsOfAvailability(String reason) {
848        if (DBG) log("notifyOffApnsOfAvailability - reason= " + reason);
849        for (int id = 0; id < DctConstants.APN_NUM_TYPES; id++) {
850            if (!isApnIdEnabled(id)) {
851                notifyApnIdDisconnected(reason, id);
852            }
853        }
854    }
855
856    public boolean isApnTypeEnabled(String apnType) {
857        if (apnType == null) {
858            return false;
859        } else {
860            return isApnIdEnabled(apnTypeToId(apnType));
861        }
862    }
863
864    protected synchronized boolean isApnIdEnabled(int id) {
865        if (id != DctConstants.APN_INVALID_ID) {
866            return dataEnabled[id];
867        }
868        return false;
869    }
870
871    /**
872     * Ensure that we are connected to an APN of the specified type.
873     *
874     * @param type the APN type (currently the only valid values are
875     *            {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
876     * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
877     *         {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
878     *         broadcast will be sent by the ConnectivityManager when a
879     *         connection to the APN has been established.
880     */
881    public synchronized int enableApnType(String type) {
882        int id = apnTypeToId(type);
883        if (id == DctConstants.APN_INVALID_ID) {
884            return PhoneConstants.APN_REQUEST_FAILED;
885        }
886
887        if (DBG) {
888            log("enableApnType(" + type + "), isApnTypeActive = " + isApnTypeActive(type)
889                    + ", isApnIdEnabled =" + isApnIdEnabled(id) + " and state = " + mState);
890        }
891
892        if (!isApnTypeAvailable(type)) {
893            if (DBG) log("type not available");
894            return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
895        }
896
897        if (isApnIdEnabled(id)) {
898            return PhoneConstants.APN_ALREADY_ACTIVE;
899        } else {
900            setEnabled(id, true);
901        }
902        return PhoneConstants.APN_REQUEST_STARTED;
903    }
904
905    /**
906     * The APN of the specified type is no longer needed. Ensure that if use of
907     * the default APN has not been explicitly disabled, we are connected to the
908     * default APN.
909     *
910     * @param type the APN type. The only valid values are currently
911     *            {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
912     * @return Success is indicated by {@code PhoneConstants.APN_ALREADY_ACTIVE} or
913     *         {@code PhoneConstants.APN_REQUEST_STARTED}. In the latter case, a
914     *         broadcast will be sent by the ConnectivityManager when a
915     *         connection to the APN has been disconnected. A {@code
916     *         PhoneConstants.APN_REQUEST_FAILED} is returned if the type parameter is
917     *         invalid or if the apn wasn't enabled.
918     */
919    public synchronized int disableApnType(String type) {
920        if (DBG) log("disableApnType(" + type + ")");
921        int id = apnTypeToId(type);
922        if (id == DctConstants.APN_INVALID_ID) {
923            return PhoneConstants.APN_REQUEST_FAILED;
924        }
925        if (isApnIdEnabled(id)) {
926            setEnabled(id, false);
927            if (isApnTypeActive(PhoneConstants.APN_TYPE_DEFAULT)) {
928                if (dataEnabled[DctConstants.APN_DEFAULT_ID]) {
929                    return PhoneConstants.APN_ALREADY_ACTIVE;
930                } else {
931                    return PhoneConstants.APN_REQUEST_STARTED;
932                }
933            } else {
934                return PhoneConstants.APN_REQUEST_STARTED;
935            }
936        } else {
937            return PhoneConstants.APN_REQUEST_FAILED;
938        }
939    }
940
941    protected void setEnabled(int id, boolean enable) {
942        if (DBG) {
943            log("setEnabled(" + id + ", " + enable + ") with old state = " + dataEnabled[id]
944                    + " and enabledCount = " + enabledCount);
945        }
946        Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN);
947        msg.arg1 = id;
948        msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
949        sendMessage(msg);
950    }
951
952    protected void onEnableApn(int apnId, int enabled) {
953        if (DBG) {
954            log("EVENT_APN_ENABLE_REQUEST apnId=" + apnId + ", apnType=" + apnIdToType(apnId) +
955                    ", enabled=" + enabled + ", dataEnabled = " + dataEnabled[apnId] +
956                    ", enabledCount = " + enabledCount + ", isApnTypeActive = " +
957                    isApnTypeActive(apnIdToType(apnId)));
958        }
959        if (enabled == DctConstants.ENABLED) {
960            synchronized (this) {
961                if (!dataEnabled[apnId]) {
962                    dataEnabled[apnId] = true;
963                    enabledCount++;
964                }
965            }
966            String type = apnIdToType(apnId);
967            if (!isApnTypeActive(type)) {
968                mRequestedApnType = type;
969                onEnableNewApn();
970            } else {
971                notifyApnIdUpToCurrent(Phone.REASON_APN_SWITCHED, apnId);
972            }
973        } else {
974            // disable
975            boolean didDisable = false;
976            synchronized (this) {
977                if (dataEnabled[apnId]) {
978                    dataEnabled[apnId] = false;
979                    enabledCount--;
980                    didDisable = true;
981                }
982            }
983            if (didDisable) {
984                if ((enabledCount == 0) || (apnId == DctConstants.APN_DUN_ID)) {
985                    mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
986                    onCleanUpConnection(true, apnId, Phone.REASON_DATA_DISABLED);
987                }
988
989                // send the disconnect msg manually, since the normal route wont send
990                // it (it's not enabled)
991                notifyApnIdDisconnected(Phone.REASON_DATA_DISABLED, apnId);
992                if (dataEnabled[DctConstants.APN_DEFAULT_ID] == true
993                        && !isApnTypeActive(PhoneConstants.APN_TYPE_DEFAULT)) {
994                    // TODO - this is an ugly way to restore the default conn - should be done
995                    // by a real contention manager and policy that disconnects the lower pri
996                    // stuff as enable requests come in and pops them back on as we disable back
997                    // down to the lower pri stuff
998                    mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
999                    onEnableNewApn();
1000                }
1001            }
1002        }
1003    }
1004
1005    /**
1006     * Called when we switch APNs.
1007     *
1008     * mRequestedApnType is set prior to call
1009     * To be overridden.
1010     */
1011    protected void onEnableNewApn() {
1012    }
1013
1014    /**
1015     * Called when EVENT_RESET_DONE is received so goto
1016     * IDLE state and send notifications to those interested.
1017     *
1018     * TODO - currently unused.  Needs to be hooked into DataConnection cleanup
1019     * TODO - needs to pass some notion of which connection is reset..
1020     */
1021    protected void onResetDone(AsyncResult ar) {
1022        if (DBG) log("EVENT_RESET_DONE");
1023        String reason = null;
1024        if (ar.userObj instanceof String) {
1025            reason = (String) ar.userObj;
1026        }
1027        gotoIdleAndNotifyDataConnection(reason);
1028    }
1029
1030    /**
1031     * Prevent mobile data connections from being established, or once again
1032     * allow mobile data connections. If the state toggles, then either tear
1033     * down or set up data, as appropriate to match the new state.
1034     *
1035     * @param enable indicates whether to enable ({@code true}) or disable (
1036     *            {@code false}) data
1037     * @return {@code true} if the operation succeeded
1038     */
1039    public boolean setInternalDataEnabled(boolean enable) {
1040        if (DBG)
1041            log("setInternalDataEnabled(" + enable + ")");
1042
1043        Message msg = obtainMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE);
1044        msg.arg1 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
1045        sendMessage(msg);
1046        return true;
1047    }
1048
1049    protected void onSetInternalDataEnabled(boolean enabled) {
1050        synchronized (mDataEnabledLock) {
1051            mInternalDataEnabled = enabled;
1052            if (enabled) {
1053                log("onSetInternalDataEnabled: changed to enabled, try to setup data call");
1054                resetAllRetryCounts();
1055                onTrySetupData(Phone.REASON_DATA_ENABLED);
1056            } else {
1057                log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections");
1058                cleanUpAllConnections(null);
1059            }
1060        }
1061    }
1062
1063    public void cleanUpAllConnections(String cause) {
1064        Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS);
1065        msg.obj = cause;
1066        sendMessage(msg);
1067    }
1068
1069    public abstract boolean isDisconnected();
1070
1071    protected void onSetUserDataEnabled(boolean enabled) {
1072        synchronized (mDataEnabledLock) {
1073            final boolean prevEnabled = getAnyDataEnabled();
1074            if (mUserDataEnabled != enabled) {
1075                mUserDataEnabled = enabled;
1076                Settings.Global.putInt(mPhone.getContext().getContentResolver(),
1077                        Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
1078                if (getDataOnRoamingEnabled() == false &&
1079                        mPhone.getServiceState().getRoaming() == true) {
1080                    if (enabled) {
1081                        notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
1082                    } else {
1083                        notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
1084                    }
1085                }
1086                if (prevEnabled != getAnyDataEnabled()) {
1087                    if (!prevEnabled) {
1088                        resetAllRetryCounts();
1089                        onTrySetupData(Phone.REASON_DATA_ENABLED);
1090                    } else {
1091                        onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
1092                    }
1093                }
1094            }
1095        }
1096    }
1097
1098    protected void onSetDependencyMet(String apnType, boolean met) {
1099    }
1100
1101    protected void onSetPolicyDataEnabled(boolean enabled) {
1102        synchronized (mDataEnabledLock) {
1103            final boolean prevEnabled = getAnyDataEnabled();
1104            if (sPolicyDataEnabled != enabled) {
1105                sPolicyDataEnabled = enabled;
1106                if (prevEnabled != getAnyDataEnabled()) {
1107                    if (!prevEnabled) {
1108                        resetAllRetryCounts();
1109                        onTrySetupData(Phone.REASON_DATA_ENABLED);
1110                    } else {
1111                        onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
1112                    }
1113                }
1114            }
1115        }
1116    }
1117
1118    protected String getReryConfig(boolean forDefault) {
1119        int nt = mPhone.getServiceState().getNetworkType();
1120
1121        if ((nt == TelephonyManager.NETWORK_TYPE_CDMA) ||
1122            (nt == TelephonyManager.NETWORK_TYPE_1xRTT) ||
1123            (nt == TelephonyManager.NETWORK_TYPE_EVDO_0) ||
1124            (nt == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
1125            (nt == TelephonyManager.NETWORK_TYPE_EVDO_B) ||
1126            (nt == TelephonyManager.NETWORK_TYPE_EHRPD)) {
1127            // CDMA variant
1128            return SystemProperties.get("ro.cdma.data_retry_config");
1129        } else {
1130            // Use GSM varient for all others.
1131            if (forDefault) {
1132                return SystemProperties.get("ro.gsm.data_retry_config");
1133            } else {
1134                return SystemProperties.get("ro.gsm.2nd_data_retry_config");
1135            }
1136        }
1137    }
1138
1139    protected void resetAllRetryCounts() {
1140        for (ApnContext ac : mApnContexts.values()) {
1141            ac.setRetryCount(0);
1142        }
1143        for (DataConnection dc : mDataConnections.values()) {
1144            dc.resetRetryCount();
1145        }
1146    }
1147
1148    protected void resetPollStats() {
1149        mTxPkts = -1;
1150        mRxPkts = -1;
1151        mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
1152    }
1153
1154    protected abstract DctConstants.State getOverallState();
1155
1156    protected void startNetStatPoll() {
1157        if (getOverallState() == DctConstants.State.CONNECTED && mNetStatPollEnabled == false) {
1158            if (DBG) log("startNetStatPoll");
1159            resetPollStats();
1160            mNetStatPollEnabled = true;
1161            mPollNetStat.run();
1162        }
1163    }
1164
1165    protected void stopNetStatPoll() {
1166        mNetStatPollEnabled = false;
1167        removeCallbacks(mPollNetStat);
1168        if (DBG) log("stopNetStatPoll");
1169    }
1170
1171    public void updateDataActivity() {
1172        long sent, received;
1173
1174        DctConstants.Activity newActivity;
1175
1176        TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
1177        TxRxSum curTxRxSum = new TxRxSum();
1178        curTxRxSum.updateTxRxSum();
1179        mTxPkts = curTxRxSum.txPkts;
1180        mRxPkts = curTxRxSum.rxPkts;
1181
1182        if (VDBG) {
1183            log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum);
1184        }
1185
1186        if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) {
1187            sent = mTxPkts - preTxRxSum.txPkts;
1188            received = mRxPkts - preTxRxSum.rxPkts;
1189
1190            if (VDBG)
1191                log("updateDataActivity: sent=" + sent + " received=" + received);
1192            if (sent > 0 && received > 0) {
1193                newActivity = DctConstants.Activity.DATAINANDOUT;
1194            } else if (sent > 0 && received == 0) {
1195                newActivity = DctConstants.Activity.DATAOUT;
1196            } else if (sent == 0 && received > 0) {
1197                newActivity = DctConstants.Activity.DATAIN;
1198            } else {
1199                newActivity = (mActivity == DctConstants.Activity.DORMANT) ?
1200                        mActivity : DctConstants.Activity.NONE;
1201            }
1202
1203            if (mActivity != newActivity && mIsScreenOn) {
1204                if (VDBG)
1205                    log("updateDataActivity: newActivity=" + newActivity);
1206                mActivity = newActivity;
1207                mPhone.notifyDataActivity();
1208            }
1209        }
1210    }
1211
1212    // Recovery action taken in case of data stall
1213    protected static class RecoveryAction {
1214        public static final int GET_DATA_CALL_LIST      = 0;
1215        public static final int CLEANUP                 = 1;
1216        public static final int REREGISTER              = 2;
1217        public static final int RADIO_RESTART           = 3;
1218        public static final int RADIO_RESTART_WITH_PROP = 4;
1219
1220        private static boolean isAggressiveRecovery(int value) {
1221            return ((value == RecoveryAction.CLEANUP) ||
1222                    (value == RecoveryAction.REREGISTER) ||
1223                    (value == RecoveryAction.RADIO_RESTART) ||
1224                    (value == RecoveryAction.RADIO_RESTART_WITH_PROP));
1225        }
1226    }
1227
1228    public int getRecoveryAction() {
1229        int action = Settings.System.getInt(mPhone.getContext().getContentResolver(),
1230                "radio.data.stall.recovery.action", RecoveryAction.GET_DATA_CALL_LIST);
1231        if (VDBG) log("getRecoveryAction: " + action);
1232        return action;
1233    }
1234    public void putRecoveryAction(int action) {
1235        Settings.System.putInt(mPhone.getContext().getContentResolver(),
1236                "radio.data.stall.recovery.action", action);
1237        if (VDBG) log("putRecoveryAction: " + action);
1238    }
1239
1240    protected boolean isConnected() {
1241        return false;
1242    }
1243
1244    protected void doRecovery() {
1245        if (getOverallState() == DctConstants.State.CONNECTED) {
1246            // Go through a series of recovery steps, each action transitions to the next action
1247            int recoveryAction = getRecoveryAction();
1248            switch (recoveryAction) {
1249            case RecoveryAction.GET_DATA_CALL_LIST:
1250                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
1251                        mSentSinceLastRecv);
1252                if (DBG) log("doRecovery() get data call list");
1253                mPhone.mCM.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
1254                putRecoveryAction(RecoveryAction.CLEANUP);
1255                break;
1256            case RecoveryAction.CLEANUP:
1257                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv);
1258                if (DBG) log("doRecovery() cleanup all connections");
1259                cleanUpAllConnections(Phone.REASON_PDP_RESET);
1260                putRecoveryAction(RecoveryAction.REREGISTER);
1261                break;
1262            case RecoveryAction.REREGISTER:
1263                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
1264                        mSentSinceLastRecv);
1265                if (DBG) log("doRecovery() re-register");
1266                mPhone.getServiceStateTracker().reRegisterNetwork(null);
1267                putRecoveryAction(RecoveryAction.RADIO_RESTART);
1268                break;
1269            case RecoveryAction.RADIO_RESTART:
1270                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
1271                        mSentSinceLastRecv);
1272                if (DBG) log("restarting radio");
1273                putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
1274                restartRadio();
1275                break;
1276            case RecoveryAction.RADIO_RESTART_WITH_PROP:
1277                // This is in case radio restart has not recovered the data.
1278                // It will set an additional "gsm.radioreset" property to tell
1279                // RIL or system to take further action.
1280                // The implementation of hard reset recovery action is up to OEM product.
1281                // Once RADIO_RESET property is consumed, it is expected to set back
1282                // to false by RIL.
1283                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1);
1284                if (DBG) log("restarting radio with gsm.radioreset to true");
1285                SystemProperties.set(RADIO_RESET_PROPERTY, "true");
1286                // give 1 sec so property change can be notified.
1287                try {
1288                    Thread.sleep(1000);
1289                } catch (InterruptedException e) {}
1290                restartRadio();
1291                putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
1292                break;
1293            default:
1294                throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
1295                    recoveryAction);
1296            }
1297        }
1298    }
1299
1300    private void updateDataStallInfo() {
1301        long sent, received;
1302
1303        TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
1304        mDataStallTxRxSum.updateTxRxSum();
1305
1306        if (VDBG) {
1307            log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
1308                    " preTxRxSum=" + preTxRxSum);
1309        }
1310
1311        sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
1312        received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
1313
1314        if (RADIO_TESTS) {
1315            if (SystemProperties.getBoolean("radio.test.data.stall", false)) {
1316                log("updateDataStallInfo: radio.test.data.stall true received = 0;");
1317                received = 0;
1318            }
1319        }
1320        if ( sent > 0 && received > 0 ) {
1321            if (VDBG) log("updateDataStallInfo: IN/OUT");
1322            mSentSinceLastRecv = 0;
1323            putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
1324        } else if (sent > 0 && received == 0) {
1325            if (mPhone.getState() == PhoneConstants.State.IDLE) {
1326                mSentSinceLastRecv += sent;
1327            } else {
1328                mSentSinceLastRecv = 0;
1329            }
1330            if (DBG) {
1331                log("updateDataStallInfo: OUT sent=" + sent +
1332                        " mSentSinceLastRecv=" + mSentSinceLastRecv);
1333            }
1334        } else if (sent == 0 && received > 0) {
1335            if (VDBG) log("updateDataStallInfo: IN");
1336            mSentSinceLastRecv = 0;
1337            putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
1338        } else {
1339            if (VDBG) log("updateDataStallInfo: NONE");
1340        }
1341    }
1342
1343    protected void onDataStallAlarm(int tag) {
1344        if (mDataStallAlarmTag != tag) {
1345            if (DBG) {
1346                log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag);
1347            }
1348            return;
1349        }
1350        updateDataStallInfo();
1351
1352        int hangWatchdogTrigger = Settings.Global.getInt(mResolver,
1353                Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
1354                NUMBER_SENT_PACKETS_OF_HANG);
1355
1356        boolean suspectedStall = DATA_STALL_NOT_SUSPECTED;
1357        if (mSentSinceLastRecv >= hangWatchdogTrigger) {
1358            if (DBG) {
1359                log("onDataStallAlarm: tag=" + tag + " do recovery action=" + getRecoveryAction());
1360            }
1361            suspectedStall = DATA_STALL_SUSPECTED;
1362            sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
1363        } else {
1364            if (VDBG) {
1365                log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) +
1366                    " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger);
1367            }
1368        }
1369        startDataStallAlarm(suspectedStall);
1370    }
1371
1372    protected void startDataStallAlarm(boolean suspectedStall) {
1373        int nextAction = getRecoveryAction();
1374        int delayInMs;
1375
1376        // If screen is on or data stall is currently suspected, set the alarm
1377        // with an aggresive timeout.
1378        if (mIsScreenOn || suspectedStall || RecoveryAction.isAggressiveRecovery(nextAction)) {
1379            delayInMs = Settings.Global.getInt(mResolver,
1380                                       Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
1381                                       DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
1382        } else {
1383            delayInMs = Settings.Global.getInt(mResolver,
1384                                       Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
1385                                       DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
1386        }
1387
1388        mDataStallAlarmTag += 1;
1389        if (VDBG) {
1390            log("startDataStallAlarm: tag=" + mDataStallAlarmTag +
1391                    " delay=" + (delayInMs / 1000) + "s");
1392        }
1393        AlarmManager am =
1394            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1395
1396        Intent intent = new Intent(getActionIntentDataStallAlarm());
1397        intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag);
1398        mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
1399                PendingIntent.FLAG_UPDATE_CURRENT);
1400        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1401                SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
1402    }
1403
1404    protected void stopDataStallAlarm() {
1405        AlarmManager am =
1406            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1407
1408        if (VDBG) {
1409            log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag +
1410                    " mDataStallAlarmIntent=" + mDataStallAlarmIntent);
1411        }
1412        mDataStallAlarmTag += 1;
1413        if (mDataStallAlarmIntent != null) {
1414            am.cancel(mDataStallAlarmIntent);
1415            mDataStallAlarmIntent = null;
1416        }
1417    }
1418
1419    protected void restartDataStallAlarm() {
1420        if (isConnected() == false) return;
1421        // To be called on screen status change.
1422        // Do not cancel the alarm if it is set with aggressive timeout.
1423        int nextAction = getRecoveryAction();
1424
1425        if (RecoveryAction.isAggressiveRecovery(nextAction)) {
1426            if (DBG) log("data stall recovery action is pending. not resetting the alarm.");
1427            return;
1428        }
1429        stopDataStallAlarm();
1430        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1431    }
1432
1433    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1434        pw.println("DataConnectionTracker:");
1435        pw.println(" RADIO_TESTS=" + RADIO_TESTS);
1436        pw.println(" mInternalDataEnabled=" + mInternalDataEnabled);
1437        pw.println(" mUserDataEnabled=" + mUserDataEnabled);
1438        pw.println(" sPolicyDataEnabed=" + sPolicyDataEnabled);
1439        pw.println(" dataEnabled:");
1440        for(int i=0; i < dataEnabled.length; i++) {
1441            pw.printf("  dataEnabled[%d]=%b\n", i, dataEnabled[i]);
1442        }
1443        pw.flush();
1444        pw.println(" enabledCount=" + enabledCount);
1445        pw.println(" mRequestedApnType=" + mRequestedApnType);
1446        pw.println(" mPhone=" + mPhone.getPhoneName());
1447        pw.println(" mActivity=" + mActivity);
1448        pw.println(" mState=" + mState);
1449        pw.println(" mTxPkts=" + mTxPkts);
1450        pw.println(" mRxPkts=" + mRxPkts);
1451        pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod);
1452        pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled);
1453        pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum);
1454        pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag);
1455        pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
1456        pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
1457        pw.println(" mResolver=" + mResolver);
1458        pw.println(" mIsWifiConnected=" + mIsWifiConnected);
1459        pw.println(" mReconnectIntent=" + mReconnectIntent);
1460        pw.println(" mCidActive=" + mCidActive);
1461        pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation);
1462        pw.println(" mIsScreenOn=" + mIsScreenOn);
1463        pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
1464        pw.flush();
1465        pw.println(" ***************************************");
1466        Set<Entry<Integer, DataConnection> > mDcSet = mDataConnections.entrySet();
1467        pw.println(" mDataConnections: count=" + mDcSet.size());
1468        for (Entry<Integer, DataConnection> entry : mDcSet) {
1469            pw.printf(" *** mDataConnection[%d] \n", entry.getKey());
1470            entry.getValue().dump(fd, pw, args);
1471        }
1472        pw.println(" ***************************************");
1473        pw.flush();
1474        Set<Entry<String, Integer>> mApnToDcIdSet = mApnToDataConnectionId.entrySet();
1475        pw.println(" mApnToDataConnectonId size=" + mApnToDcIdSet.size());
1476        for (Entry<String, Integer> entry : mApnToDcIdSet) {
1477            pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue());
1478        }
1479        pw.println(" ***************************************");
1480        pw.flush();
1481        if (mApnContexts != null) {
1482            Set<Entry<String, ApnContext>> mApnContextsSet = mApnContexts.entrySet();
1483            pw.println(" mApnContexts size=" + mApnContextsSet.size());
1484            for (Entry<String, ApnContext> entry : mApnContextsSet) {
1485                entry.getValue().dump(fd, pw, args);
1486            }
1487            pw.println(" ***************************************");
1488        } else {
1489            pw.println(" mApnContexts=null");
1490        }
1491        pw.flush();
1492        pw.println(" mActiveApn=" + mActiveApn);
1493        if (mAllApns != null) {
1494            pw.println(" mAllApns size=" + mAllApns.size());
1495            for (int i=0; i < mAllApns.size(); i++) {
1496                pw.printf(" mAllApns[%d]: %s\n", i, mAllApns.get(i));
1497            }
1498            pw.flush();
1499        } else {
1500            pw.println(" mAllApns=null");
1501        }
1502        pw.println(" mPreferredApn=" + mPreferredApn);
1503        pw.println(" mIsPsRestricted=" + mIsPsRestricted);
1504        pw.println(" mIsDisposed=" + mIsDisposed);
1505        pw.println(" mIntentReceiver=" + mIntentReceiver);
1506        pw.println(" mDataRoamingSettingObserver=" + mDataRoamingSettingObserver);
1507        pw.flush();
1508    }
1509}
1510