Migrate bandwidth control to NMS, omit history.

Both stats and policy make NMS calls that depend on bandwidth control
being enabled, so move enable/disable into NMS and drop calls when
disabled.  This avoids throwing heavy ISE exceptions when disabled.

Only include recent data when writing NetworkStatsHistory as part of
dumpsys call.  Introduce manual poll event for Settings UI.

Bug: 4982115, 4770435, 4515856
Change-Id: I257820b057af2f0f99c736fb4f61e55b9fdc3e66
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index ae9aa05..0548250 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -33,4 +33,7 @@
     /** Return usage summary per UID for traffic that matches template. */
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
 
+    /** Force update of statistics. */
+    void forceUpdate();
+
 }
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index ff6e220..dd2945c 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -279,10 +279,17 @@
         return (long) (start + (r.nextFloat() * (end - start)));
     }
 
-    public void dump(String prefix, PrintWriter pw) {
+    public void dump(String prefix, PrintWriter pw, boolean fullHistory) {
         pw.print(prefix);
         pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
-        for (int i = 0; i < bucketCount; i++) {
+
+        final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
+        if (start > 0) {
+            pw.print(prefix);
+            pw.print("  (omitting "); pw.print(start); pw.println(" buckets)");
+        }
+
+        for (int i = start; i < bucketCount; i++) {
             pw.print(prefix);
             pw.print("  bucketStart="); pw.print(bucketStart[i]);
             pw.print(" rx="); pw.print(rx[i]);
@@ -293,7 +300,7 @@
     @Override
     public String toString() {
         final CharArrayWriter writer = new CharArrayWriter();
-        dump("", new PrintWriter(writer));
+        dump("", new PrintWriter(writer), false);
         return writer.toString();
     }
 
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c9b6121..fcf4796 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -241,6 +241,4 @@
      */
     int getInterfaceTxThrottle(String iface);
 
-    void setBandwidthControlEnabled(boolean enabled);
-
 }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index adc6570..1c150f8 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,10 +16,11 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
-import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.provider.Settings.Secure.NETSTATS_ENABLED;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -35,6 +36,7 @@
 import android.os.INetworkManagementService;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -123,6 +125,8 @@
     /** Set of UIDs with active reject rules. */
     private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
 
+    private boolean mBandwidthControlEnabled;
+
     /**
      * Constructs a new NetworkManagementService instance
      *
@@ -161,6 +165,29 @@
         return new NetworkManagementService(context, procRoot);
     }
 
+    public void systemReady() {
+
+        // only enable bandwidth control when support exists, and requested by
+        // system setting.
+        // TODO: eventually migrate to be always enabled
+        final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
+        final boolean shouldEnable =
+                Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 0) != 0;
+
+        mBandwidthControlEnabled = false;
+        if (hasKernelSupport && shouldEnable) {
+            Slog.d(TAG, "enabling bandwidth control");
+            try {
+                mConnector.doCommand("bandwidth enable");
+                mBandwidthControlEnabled = true;
+            } catch (NativeDaemonConnectorException e) {
+                Slog.e(TAG, "problem enabling bandwidth controls", e);
+            }
+        } else {
+            Slog.d(TAG, "not enabling bandwidth control");
+        }
+    }
+
     public void registerObserver(INetworkManagementEventObserver obs) {
         Slog.d(TAG, "Registering observer");
         mObservers.add(obs);
@@ -919,7 +946,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
 
-        if (mProcStatsNetfilter.exists()) {
+        if (mBandwidthControlEnabled) {
             return getNetworkStatsDetailNetfilter(UID_ALL);
         } else {
             return getNetworkStatsDetailUidstat(UID_ALL);
@@ -930,6 +957,10 @@
     public void setInterfaceQuota(String iface, long quota) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
+        // silently discard when control disabled
+        // TODO: eventually migrate to be always enabled
+        if (!mBandwidthControlEnabled) return;
+
         synchronized (mInterfaceQuota) {
             if (mInterfaceQuota.contains(iface)) {
                 // TODO: eventually consider throwing
@@ -953,6 +984,10 @@
     public void removeInterfaceQuota(String iface) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
+        // silently discard when control disabled
+        // TODO: eventually migrate to be always enabled
+        if (!mBandwidthControlEnabled) return;
+
         synchronized (mInterfaceQuota) {
             if (!mInterfaceQuota.contains(iface)) {
                 // TODO: eventually consider throwing
@@ -976,6 +1011,10 @@
     public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
+        // silently discard when control disabled
+        // TODO: eventually migrate to be always enabled
+        if (!mBandwidthControlEnabled) return;
+
         synchronized (mUidRejectOnQuota) {
             final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
             if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
@@ -1011,7 +1050,7 @@
                     android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
         }
 
-        if (mProcStatsNetfilter.exists()) {
+        if (mBandwidthControlEnabled) {
             return getNetworkStatsDetailNetfilter(uid);
         } else {
             return getNetworkStatsDetailUidstat(uid);
@@ -1151,12 +1190,6 @@
         return getInterfaceThrottle(iface, false);
     }
 
-    @Override
-    public void setBandwidthControlEnabled(boolean enabled) {
-        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-        mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable")));
-    }
-
     /**
      * Split given line into {@link ArrayList}.
      */
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dbfd145..8c7e279 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -523,6 +523,7 @@
         // These are needed to propagate to the runnable below.
         final Context contextF = context;
         final BatteryService batteryF = battery;
+        final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
         final ConnectivityService connectivityF = connectivity;
@@ -550,6 +551,7 @@
 
                 startSystemUi(contextF);
                 if (batteryF != null) batteryF.systemReady();
+                if (networkManagementF != null) networkManagementF.systemReady();
                 if (networkStatsF != null) networkStatsF.systemReady();
                 if (networkPolicyF != null) networkPolicyF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 1f2ec2c..2a17cbe 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1044,8 +1044,11 @@
 
         // TODO: only dispatch when rules actually change
 
-        // record rule locally to dispatch to new listeners
-        mUidRules.put(uid, uidRules);
+        if (uidRules == RULE_ALLOW_ALL) {
+            mUidRules.delete(uid);
+        } else {
+            mUidRules.put(uid, uidRules);
+        }
 
         final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
         setUidNetworkRules(uid, rejectMetered);
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 7610a11..b4bd176 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -134,7 +134,6 @@
      * Settings that can be changed externally.
      */
     public interface NetworkStatsSettings {
-        public boolean getEnabled();
         public long getPollInterval();
         public long getPersistThreshold();
         public long getNetworkBucketDuration();
@@ -207,20 +206,6 @@
     }
 
     public void systemReady() {
-        if (mSettings.getEnabled()) {
-            try {
-                // enable low-level bandwidth stats and control
-                // TODO: consider shipping with this enabled by default
-                mNetworkManager.setBandwidthControlEnabled(true);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "problem talking to netd while enabling bandwidth controls", e);
-            } catch (NativeDaemonConnectorException ndce) {
-                Slog.e(TAG, "problem enabling bandwidth controls", ndce);
-            }
-        } else {
-            Slog.w(TAG, "detailed network stats disabled");
-        }
-
         synchronized (mStatsLock) {
             // read historical network stats from disk, since policy service
             // might need them right away. we delay loading detailed UID stats
@@ -389,6 +374,15 @@
         }
     }
 
+    @Override
+    public void forceUpdate() {
+        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+
+        synchronized (mStatsLock) {
+            performPollLocked(true, false);
+        }
+    }
+
     /**
      * Receiver that watches for {@link IConnectivityManager} to claim network
      * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
@@ -905,6 +899,8 @@
             argSet.add(arg);
         }
 
+        final boolean fullHistory = argSet.contains("full");
+
         synchronized (mStatsLock) {
             // TODO: remove this testing code, since it corrupts stats
             if (argSet.contains("generate")) {
@@ -930,7 +926,7 @@
             for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
                 final NetworkStatsHistory history = mNetworkStats.get(ident);
                 pw.print("  ident="); pw.println(ident.toString());
-                history.dump("  ", pw);
+                history.dump("  ", pw, fullHistory);
             }
 
             if (argSet.contains("detail")) {
@@ -950,7 +946,7 @@
                         final NetworkStatsHistory history = uidStats.valueAt(i);
                         pw.print("    UID="); pw.print(uid);
                         pw.print(" tag="); pw.println(tag);
-                        history.dump("    ", pw);
+                        history.dump("    ", pw, fullHistory);
                     }
                 }
             }
@@ -1058,15 +1054,6 @@
             return Settings.Secure.getLong(mResolver, name, def);
         }
 
-        public boolean getEnabled() {
-            if (!new File("/proc/net/xt_qtaguid/ctrl").exists()) {
-                Slog.w(TAG, "kernel does not support bandwidth control");
-                return false;
-            }
-            // TODO: once things stabilize, enable by default.
-            // For now: ./vendor/google/tools/override-gservices secure:netstats_enabled=1
-            return Settings.Secure.getInt(mResolver, NETSTATS_ENABLED, 0) != 0;
-        }
         public long getPollInterval() {
             return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
         }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 903f2b0..f2c28bb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -610,9 +610,6 @@
         mAlarmManager.setInexactRepeating(
                 eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
         expectLastCall().atLeastOnce();
-
-        mNetManager.setBandwidthControlEnabled(true);
-        expectLastCall().atLeastOnce();
     }
 
     private void expectNetworkState(NetworkState... state) throws Exception {
@@ -633,7 +630,6 @@
 
     private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory)
             throws Exception {
-        expect(mSettings.getEnabled()).andReturn(true).anyTimes();
         expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
         expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes();
         expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes();