[WifiVendorHal] RingBufferLogging

Test: unit tests added
Bug: 34902105

Change-Id: I6411ad720fe537bd2b97b22b180762c4c9e48145
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 32b1768..165285b 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -2678,6 +2678,11 @@
         int readBytes;
         int writtenRecords;
 
+        // Bit masks for interpreting |flag|
+        public static final int HAS_BINARY_ENTRIES = (1 << 0);
+        public static final int HAS_ASCII_ENTRIES = (1 << 1);
+        public static final int HAS_PER_PACKET_ENTRIES = (1 << 2);
+
         @Override
         public String toString() {
             return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index b099eda..1c2502b 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -25,6 +25,8 @@
 import android.hardware.wifi.V1_0.StaRoamingConfig;
 import android.hardware.wifi.V1_0.StaRoamingState;
 import android.hardware.wifi.V1_0.WifiDebugHostWakeReasonStats;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferFlags;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
 import android.hardware.wifi.V1_0.WifiStatus;
 import android.hardware.wifi.V1_0.WifiStatusCode;
 import android.net.apf.ApfCapabilities;
@@ -43,6 +45,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.wifi.util.BitMask;
 import com.android.server.wifi.util.NativeUtil;
 
 import java.util.ArrayList;
@@ -671,7 +674,24 @@
     public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxIntervalInSec,
                                           int minDataSizeInBytes, String ringName) {
         kilroy();
-        throw new UnsupportedOperationException();
+        synchronized (sLock) {
+            try {
+                if (mIWifiChip == null) return false;
+                kilroy();
+                // note - flags are not used
+                WifiStatus status = mIWifiChip.startLoggingToDebugRingBuffer(
+                        ringName,
+                        verboseLevel,
+                        maxIntervalInSec,
+                        minDataSizeInBytes
+                );
+                return status.code == WifiStatusCode.SUCCESS;
+            } catch (RemoteException e) {
+                kilroy();
+                handleRemoteException(e);
+                return false;
+            }
+        }
     }
 
     /**
@@ -736,11 +756,81 @@
     }
 
     /**
+     * Creates RingBufferStatus from the Hal version
+     */
+    private static WifiNative.RingBufferStatus ringBufferStatus(WifiDebugRingBufferStatus h) {
+        WifiNative.RingBufferStatus ans = new WifiNative.RingBufferStatus();
+        ans.name = h.ringName;
+        ans.flag = frameworkRingBufferFlagsFromHal(h.flags);
+        ans.ringBufferId = h.ringId;
+        ans.ringBufferByteSize = h.sizeInBytes;
+        ans.verboseLevel = h.verboseLevel;
+        // Remaining fields are unavailable
+        //  writtenBytes;
+        //  readBytes;
+        //  writtenRecords;
+        return ans;
+    }
+
+    /**
+     * Translates a hal wifiDebugRingBufferFlag to the WifiNative version
+     */
+    private static int frameworkRingBufferFlagsFromHal(int wifiDebugRingBufferFlag) {
+        BitMask checkoff = new BitMask(wifiDebugRingBufferFlag);
+        int flags = 0;
+        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_BINARY_ENTRIES)) {
+            flags |= WifiNative.RingBufferStatus.HAS_BINARY_ENTRIES;
+        }
+        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_ASCII_ENTRIES)) {
+            flags |= WifiNative.RingBufferStatus.HAS_ASCII_ENTRIES;
+        }
+        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_PER_PACKET_ENTRIES)) {
+            flags |= WifiNative.RingBufferStatus.HAS_PER_PACKET_ENTRIES;
+        }
+        if (checkoff.value != 0) {
+            throw new IllegalArgumentException("Unknown WifiDebugRingBufferFlag " + checkoff.value);
+        }
+        return flags;
+    }
+
+    /**
+     * Creates array of RingBufferStatus from the Hal version
+     */
+    private static WifiNative.RingBufferStatus[] makeRingBufferStatusArray(
+            ArrayList<WifiDebugRingBufferStatus> ringBuffers) {
+        WifiNative.RingBufferStatus[] ans = new WifiNative.RingBufferStatus[ringBuffers.size()];
+        int i = 0;
+        for (WifiDebugRingBufferStatus b : ringBuffers) {
+            ans[i++] = ringBufferStatus(b);
+        }
+        return ans;
+    }
+
+    /**
      * API to get the status of all ring buffers supported by driver
      */
     public WifiNative.RingBufferStatus[] getRingBufferStatus() {
         kilroy();
-        throw new UnsupportedOperationException();
+        class AnswerBox {
+            public WifiNative.RingBufferStatus[] value = null;
+        }
+        AnswerBox ans = new AnswerBox();
+        synchronized (sLock) {
+            if (mIWifiChip == null) return null;
+            try {
+                kilroy();
+                mIWifiChip.getDebugRingBuffersStatus((status, ringBuffers) -> {
+                    kilroy();
+                    if (status.code != WifiStatusCode.SUCCESS) return;
+                    ans.value = makeRingBufferStatusArray(ringBuffers);
+                });
+            } catch (RemoteException e) {
+                kilroy();
+                handleRemoteException(e);
+                return null;
+            }
+        }
+        return ans.value;
     }
 
     /**
@@ -749,7 +839,17 @@
      */
     public boolean getRingBufferData(String ringName) {
         kilroy();
-        throw new UnsupportedOperationException();
+        synchronized (sLock) {
+            try {
+                if (mIWifiChip == null) return false;
+                kilroy();
+                WifiStatus status = mIWifiChip.forceDumpToDebugRingBuffer(ringName);
+                return status.code == WifiStatusCode.SUCCESS;
+            } catch (RemoteException e) {
+                handleRemoteException(e);
+                return false;
+            }
+        }
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index eabcad0..3a4feb1 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -23,20 +23,22 @@
 import android.hardware.wifi.V1_0.IWifiRttController;
 import android.hardware.wifi.V1_0.IWifiStaIface;
 import android.hardware.wifi.V1_0.StaApfPacketFilterCapabilities;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferFlags;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferVerboseLevel;
 import android.hardware.wifi.V1_0.WifiStatus;
 import android.hardware.wifi.V1_0.WifiStatusCode;
 import android.net.apf.ApfCapabilities;
 import android.net.wifi.WifiManager;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.RemoteException;
 
 import com.android.server.wifi.util.NativeUtil;
 
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.RemoteException;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -497,4 +499,108 @@
 
         verify(mIWifiApIface).setCountryCode(eq(expected));
     }
+
+    /**
+     * Test that startLoggingToDebugRingBuffer is plumbed to chip
+     *
+     * A call before the vendor hal is started should just return false.
+     * After starting in STA mode, the call should succeed, and pass ther right things down.
+     */
+    @Test
+    public void testStartLoggingRingBuffer() throws Exception {
+        when(mIWifiChip.startLoggingToDebugRingBuffer(
+                any(String.class), anyInt(), anyInt(), anyInt()
+        )).thenReturn(mWifiStatusSuccess);
+
+        assertFalse(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 0, 0, "One"));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
+        assertTrue(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 11, 3000, "One"));
+
+        verify(mIWifiChip).startLoggingToDebugRingBuffer("One", 1, 11, 3000);
+    }
+
+    /**
+     * Same test as testStartLoggingRingBuffer, but in AP mode rather than STA.
+     */
+    @Test
+    public void testStartLoggingRingBufferOnAp() throws Exception {
+        when(mIWifiChip.startLoggingToDebugRingBuffer(
+                any(String.class), anyInt(), anyInt(), anyInt()
+        )).thenReturn(mWifiStatusSuccess);
+
+        assertFalse(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 0, 0, "One"));
+        assertTrue(mWifiVendorHal.startVendorHalAp());
+        assertTrue(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 11, 3000, "One"));
+
+        verify(mIWifiChip).startLoggingToDebugRingBuffer("One", 1, 11, 3000);
+    }
+
+    /**
+     * Test that getRingBufferStatus gets and translates its stuff correctly
+     */
+    @Test
+    public void testRingBufferStatus() throws Exception {
+        WifiDebugRingBufferStatus one = new WifiDebugRingBufferStatus();
+        one.ringName = "One";
+        one.flags = WifiDebugRingBufferFlags.HAS_BINARY_ENTRIES;
+        one.ringId = 5607371;
+        one.sizeInBytes = 54321;
+        one.freeSizeInBytes = 42;
+        one.verboseLevel = WifiDebugRingBufferVerboseLevel.VERBOSE;
+        String oneExpect = "name: One flag: 1 ringBufferId: 5607371 ringBufferByteSize: 54321"
+                + " verboseLevel: 2 writtenBytes: 0 readBytes: 0 writtenRecords: 0";
+
+        WifiDebugRingBufferStatus two = new WifiDebugRingBufferStatus();
+        two.ringName = "Two";
+        two.flags = WifiDebugRingBufferFlags.HAS_ASCII_ENTRIES
+                | WifiDebugRingBufferFlags.HAS_PER_PACKET_ENTRIES;
+        two.ringId = 4512470;
+        two.sizeInBytes = 300;
+        two.freeSizeInBytes = 42;
+        two.verboseLevel = WifiDebugRingBufferVerboseLevel.DEFAULT;
+
+        ArrayList<WifiDebugRingBufferStatus> halBufferStatus = new ArrayList<>(2);
+        halBufferStatus.add(one);
+        halBufferStatus.add(two);
+
+        WifiNative.RingBufferStatus[] actual;
+
+        doAnswer(new AnswerWithArguments() {
+            public void answer(IWifiChip.getDebugRingBuffersStatusCallback cb)
+                    throws RemoteException {
+                cb.onValues(mWifiStatusSuccess, halBufferStatus);
+            }
+        }).when(mIWifiChip).getDebugRingBuffersStatus(any(
+                IWifiChip.getDebugRingBuffersStatusCallback.class));
+
+        assertTrue(mWifiVendorHal.startVendorHalSta());
+        actual = mWifiVendorHal.getRingBufferStatus();
+
+        assertEquals(halBufferStatus.size(), actual.length);
+        assertEquals(oneExpect, actual[0].toString());
+        assertEquals(two.ringId, actual[1].ringBufferId);
+
+    }
+
+    /**
+     * Test that getRingBufferData calls forceDumpToDebugRingBuffer
+     *
+     * Try once before hal start, and twice after (one success, one failure).
+     */
+    @Test
+    public void testForceRingBufferDump() throws Exception {
+        when(mIWifiChip.forceDumpToDebugRingBuffer(eq("Gunk"))).thenReturn(mWifiStatusSuccess);
+        when(mIWifiChip.forceDumpToDebugRingBuffer(eq("Glop"))).thenReturn(mWifiStatusFailure);
+
+        assertFalse(mWifiVendorHal.getRingBufferData("Gunk")); // hal not started
+
+        assertTrue(mWifiVendorHal.startVendorHalSta());
+
+        assertTrue(mWifiVendorHal.getRingBufferData("Gunk")); // mocked call succeeds
+        assertFalse(mWifiVendorHal.getRingBufferData("Glop")); // mocked call fails
+
+        verify(mIWifiChip).forceDumpToDebugRingBuffer("Gunk");
+        verify(mIWifiChip).forceDumpToDebugRingBuffer("Glop");
+    }
+
 }