From 0412423f06ec633203b96f68b51f0720c33a2473 Mon Sep 17 00:00:00 2001 From: Haoyu Bai Date: Thu, 28 Jun 2012 15:26:19 -0700 Subject: [PATCH] Setup idletimer for network interface. Cherry-picked from commit f71ca8a5728e425de61ba794c9653dd0b04f16e3 in master. DO NOT MERGE Change-Id: I6101c7ae041b4cc1237ce7a9983753dbdfa301d3 --- .../java/android/os/INetworkManagementService.aidl | 21 ++++++++ core/java/android/provider/Settings.java | 23 +++++++++ .../com/android/server/ConnectivityService.java | 58 +++++++++++++++++++++ .../android/server/NetworkManagementService.java | 60 ++++++++++++++++++++++ 4 files changed, 162 insertions(+) diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 53bb88af5d5e..20d3ec31500d 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -323,6 +323,27 @@ interface INetworkManagementService 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 9ec47443c426..01252f0d97b8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2852,6 +2852,29 @@ public final class Settings { */ 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 6049b0510c65..375ba68d42a0 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -1625,6 +1625,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public class ConnectivityService extends IConnectivityManager.Stub { } /** + * 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 c3f3a5d47fe4..39e518671d9f 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -153,6 +153,21 @@ public class NetworkManagementService extends INetworkManagementService.Stub /** 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 mActiveIdleTimers = Maps.newHashMap(); + private volatile boolean mBandwidthControlEnabled; /** @@ -1047,6 +1062,51 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @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(); -- 2.11.0