Setup idletimer for network interface.
Cherry-picked from commit f71ca8a5728e425de61ba794c9653dd0b04f16e3 in
master. DO NOT MERGE
Change-Id: I6101c7ae041b4cc1237ce7a9983753dbdfa301d3
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 53bb88a..20d3ec3 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -323,6 +323,27 @@
int getInterfaceTxThrottle(String iface);
/**
+ * Sets idletimer for an interface.
+ *
+ * This either initializes a new idletimer or increases its
+ * reference-counting if an idletimer already exists for given
+ * {@code iface}.
+ *
+ * {@code label} usually represents the network type of {@code iface}.
+ * Caller should ensure that {@code label} for an {@code iface} remains the
+ * same for all calls to addIdleTimer.
+ *
+ * Every {@code addIdleTimer} should be paired with a
+ * {@link removeIdleTimer} to cleanup when the network disconnects.
+ */
+ void addIdleTimer(String iface, int timeout, String label);
+
+ /**
+ * Removes idletimer for an interface.
+ */
+ void removeIdleTimer(String iface);
+
+ /**
* Sets the name of the default interface in the DNS resolver.
*/
void setDefaultInterfaceForDns(String iface);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9ec4744..01252f0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2852,6 +2852,29 @@
*/
public static final String TETHER_DUN_APN = "tether_dun_apn";
+ /** Inactivity timeout to track mobile data activity.
+ *
+ * If set to a positive integer, it indicates the inactivity timeout value in seconds to
+ * infer the data activity of mobile network. After a period of no activity on mobile
+ * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
+ * intent is fired to indicate a transition of network status from "active" to "idle". Any
+ * subsequent activity on mobile networks triggers the firing of {@code
+ * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
+ *
+ * Network activity refers to transmitting or receiving data on the network interfaces.
+ *
+ * Tracking is disabled if set to zero or negative value.
+ *
+ * @hide
+ */
+ public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
+
+ /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
+ * but for Wifi network.
+ * @hide
+ */
+ public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
+
/**
* No longer supported.
*/
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 6049b05..375ba68 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1625,6 +1625,10 @@
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+
+ // Remove idletimer previously setup in {@code handleConnect}
+ removeDataActivityTracking(prevNetType);
+
/*
* If the disconnected network is not the active one, then don't report
* this as a loss of connectivity. What probably happened is that we're
@@ -1905,6 +1909,8 @@
private void handleConnect(NetworkInfo info) {
final int type = info.getType();
+ setupDataActivityTracking(type);
+
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
final NetworkStateTracker thisNet = mNetTrackers[type];
@@ -1977,6 +1983,58 @@
}
/**
+ * Setup data activity tracking for the given network interface.
+ *
+ * Every {@code setupDataActivityTracking} should be paired with a
+ * {@link removeDataActivityTracking} for cleanup.
+ */
+ private void setupDataActivityTracking(int type) {
+ final NetworkStateTracker thisNet = mNetTrackers[type];
+ final String iface = thisNet.getLinkProperties().getInterfaceName();
+
+ final int timeout;
+
+ if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_MOBILE,
+ 0);
+ // Canonicalize mobile network type
+ type = ConnectivityManager.TYPE_MOBILE;
+ } else if (ConnectivityManager.TYPE_WIFI == type) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_WIFI,
+ 0);
+ } else {
+ // do not track any other networks
+ timeout = 0;
+ }
+
+ if (timeout > 0 && iface != null) {
+ try {
+ mNetd.addIdleTimer(iface, timeout, Integer.toString(type));
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Remove data activity tracking when network disconnects.
+ */
+ private void removeDataActivityTracking(int type) {
+ final NetworkStateTracker net = mNetTrackers[type];
+ final String iface = net.getLinkProperties().getInterfaceName();
+
+ if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) ||
+ ConnectivityManager.TYPE_WIFI == type)) {
+ try {
+ // the call fails silently if no idletimer setup for this interface
+ mNetd.removeIdleTimer(iface);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
* After a change in the connectivity state of a network. We're mainly
* concerned with making sure that the list of DNS servers is set up
* according to which networks are connected, and ensuring that the
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index c3f3a5d..39e5186 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -153,6 +153,21 @@
/** Set of UIDs with active reject rules. */
private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+ private Object mIdleTimerLock = new Object();
+ /** Set of interfaces with active idle timers. */
+ private static class IdleTimerParams {
+ public final int timeout;
+ public final String label;
+ public int networkCount;
+
+ IdleTimerParams(int timeout, String label) {
+ this.timeout = timeout;
+ this.label = label;
+ this.networkCount = 1;
+ }
+ }
+ private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
+
private volatile boolean mBandwidthControlEnabled;
/**
@@ -1047,6 +1062,51 @@
}
@Override
+ public void addIdleTimer(String iface, int timeout, String label) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ if (DBG) Slog.d(TAG, "Adding idletimer");
+
+ synchronized (mIdleTimerLock) {
+ IdleTimerParams params = mActiveIdleTimers.get(iface);
+ if (params != null) {
+ // the interface already has idletimer, update network count
+ params.networkCount++;
+ return;
+ }
+
+ try {
+ mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), label);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, label));
+ }
+ }
+
+ @Override
+ public void removeIdleTimer(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ if (DBG) Slog.d(TAG, "Removing idletimer");
+
+ synchronized (mIdleTimerLock) {
+ IdleTimerParams params = mActiveIdleTimers.get(iface);
+ if (params == null || --(params.networkCount) > 0) {
+ return;
+ }
+
+ try {
+ mConnector.execute("idletimer", "remove", iface,
+ Integer.toString(params.timeout), params.label);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ mActiveIdleTimers.remove(iface);
+ }
+ }
+
+ @Override
public NetworkStats getNetworkStatsSummaryDev() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return mStatsFactory.readNetworkStatsSummaryDev();