Add dpm force-network-logs command to force network logs retrieval.

This is useful because logs are batched every 1.5-2hrs if there are
not enough logs to fill up a batch. The command is throttled at
10 seconds as a spam prevention measure.

Bug: 62251154
Test: in adb shell run dpm force-network-logs. Observe a new batch
being created in the phone directory
/storage/emulated/0/Android/data/com.afwsamples.testdpc/files/. Also
observe a fresh batch is being displayed on the phone in the TestDPC
app, under "Retrieve network logs".
Change-Id: I5ff9d5c78497ea81533b5248816b4d6e160d338f
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 7c1a555..376b13c 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -46,6 +46,7 @@
     private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
     private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
     private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+    private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
     private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
 
     private IDevicePolicyManager mDevicePolicyManager;
@@ -84,6 +85,9 @@
                 "feature development to prevent triggering restriction on setting freeze " +
                 "periods.\n" +
                 "\n" +
+                "dpm " + COMMAND_FORCE_NETWORK_LOGS + ": makes all network logs available to " +
+                "the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
+                "\n" +
                 "dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
                 "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
     }
@@ -114,6 +118,9 @@
             case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
                 runClearFreezePeriodRecord();
                 break;
+            case COMMAND_FORCE_NETWORK_LOGS:
+                runForceNetworkLogs();
+                break;
             case COMMAND_FORCE_SECURITY_LOGS:
                 runForceSecurityLogs();
                 break;
@@ -122,6 +129,18 @@
         }
     }
 
+    private void runForceNetworkLogs() throws RemoteException, InterruptedException {
+        while (true) {
+            final long toWait = mDevicePolicyManager.forceNetworkLogs();
+            if (toWait == 0) {
+                break;
+            }
+            System.out.println("We have to wait for " + toWait + " milliseconds...");
+            Thread.sleep(toWait);
+        }
+        System.out.println("Success");
+    }
+
     private void runForceSecurityLogs() throws RemoteException, InterruptedException {
         while (true) {
             final long toWait = mDevicePolicyManager.forceSecurityLogs();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ff38c1f..7ba4447 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8300,6 +8300,22 @@
     }
 
     /**
+     * Makes all accumulated network logs available to DPC in a new batch.
+     * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+     * @hide
+     */
+    public long forceNetworkLogs() {
+        if (mService == null) {
+            return -1;
+        }
+        try {
+            return mService.forceNetworkLogs();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Forces a batch of security logs to be fetched from logd and makes it available for DPC.
      * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
      * @hide
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 37508cd..e3b160f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -347,6 +347,7 @@
     boolean isSecurityLoggingEnabled(in ComponentName admin);
     ParceledListSlice retrieveSecurityLogs(in ComponentName admin);
     ParceledListSlice retrievePreRebootSecurityLogs(in ComponentName admin);
+    long forceNetworkLogs();
     long forceSecurityLogs();
 
     boolean isUninstallInQueue(String packageName);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1c9782f..84de6b4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -152,6 +152,11 @@
     }
 
     @Override
+    public long forceNetworkLogs() {
+        return 0;
+    }
+
+    @Override
     public long forceSecurityLogs() {
         return 0;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 414cf47..20147d2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10694,7 +10694,6 @@
                     return false;
                 }
         }
-
         return false;
     }
 
@@ -12282,6 +12281,20 @@
         }
     }
 
+    @Override
+    public long forceNetworkLogs() {
+        enforceShell("forceNetworkLogs");
+        synchronized (getLockObject()) {
+            if (!isNetworkLoggingEnabledInternalLocked()) {
+                throw new IllegalStateException("logging is not available");
+            }
+            if (mNetworkLogger != null) {
+                return mNetworkLogger.forceBatchFinalization();
+            }
+            return 0;
+        }
+    }
+
     /** Pauses security and network logging if there are unaffiliated users on the device */
     private void maybePauseDeviceWideLoggingLocked() {
         if (!areAllUsersAffiliatedWithDeviceLocked()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 0967652..4514492 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -187,4 +187,8 @@
     List<NetworkEvent> retrieveLogs(long batchToken) {
         return mNetworkLoggingHandler.retrieveFullLogBatch(batchToken);
     }
+
+    long forceBatchFinalization() {
+        return mNetworkLoggingHandler.forceBatchFinalization();
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index f91f959..0a7070f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.server.devicepolicy;
 
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
 import android.app.admin.DeviceAdminReceiver;
@@ -33,6 +35,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A Handler class for managing network logging on a background thread.
@@ -60,6 +63,12 @@
     /** Delay after which older batches get discarded after a retrieval. */
     private static final long RETRIEVED_BATCH_DISCARD_DELAY_MS = 5 * 60 * 1000; // 5m
 
+    /** Throttle batch finalization to 10 seconds.*/
+    private static final long FORCE_FETCH_THROTTLE_NS = TimeUnit.SECONDS.toNanos(10);
+    /** Timestamp of the last call to finalise a batch. Used for throttling forced finalization.*/
+    @GuardedBy("this")
+    private long mLastFinalizationNanos = -1;
+
     /** Do not call into mDpm with locks held */
     private final DevicePolicyManagerService mDpm;
     private final AlarmManager mAlarmManager;
@@ -155,6 +164,26 @@
                 + "ms from now.");
     }
 
+    /**
+     * Forces batch finalisation. Throttled to 10 seconds per batch finalisation.
+     * @return the number of milliseconds to wait until batch finalisation can be forced.
+     */
+    long forceBatchFinalization() {
+        Bundle notificationExtras;
+        synchronized (this) {
+            final long toWaitNanos =
+                mLastFinalizationNanos + FORCE_FETCH_THROTTLE_NS - System.nanoTime();
+            if (toWaitNanos > 0) {
+                return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
+            }
+            notificationExtras = finalizeBatchAndBuildDeviceOwnerMessageLocked();
+        }
+        if (notificationExtras != null) {
+            notifyDeviceOwner(notificationExtras);
+        }
+        return 0;
+    }
+
     synchronized void pause() {
         Slog.d(TAG, "Paused network logging");
         mPaused = true;
@@ -192,6 +221,7 @@
     @GuardedBy("this")
     /** @returns extras if a message should be sent to the device owner */
     private Bundle finalizeBatchAndBuildDeviceOwnerMessageLocked() {
+        mLastFinalizationNanos = System.nanoTime();
         Bundle notificationExtras = null;
         if (mNetworkEvents.size() > 0) {
             // Assign ids to the events.