Add PASSDROP_OPCODE test, ensuring counter increment

This commit introduces new testcases for PASSDROP_OPCODE. The test
verifies that the packet is passed/dropped and the counter is
incremented correctly when this opcode is executed.

This commit also contains some minor stylistic changes.

Test: TH
Change-Id: I3f84e7e5a9f9846cd67e8298a7bd1e07adddc846
diff --git a/src/android/net/apf/ApfCounterTracker.java b/src/android/net/apf/ApfCounterTracker.java
index 70c1dd7..e174821 100644
--- a/src/android/net/apf/ApfCounterTracker.java
+++ b/src/android/net/apf/ApfCounterTracker.java
@@ -17,6 +17,7 @@
 package android.net.apf;
 
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -90,6 +91,14 @@
         }
 
         /**
+         * Returns the counter sequence number from the end of the APF data segment for
+         * a given counter.
+         */
+        public int value() {
+            return this.ordinal();
+        }
+
+        /**
          * Returns the total size of the data segment in bytes.
          */
         public static int totalSize() {
@@ -97,6 +106,8 @@
         }
     }
 
+    private static final String TAG = ApfCounterTracker.class.getSimpleName();
+
     private final List<Counter> mCounterList;
     // Store the counters' value
     private final Map<Counter, Long> mCounters = new ArrayMap<>();
@@ -111,17 +122,31 @@
      */
     public static long getCounterValue(byte[] data, Counter counter)
             throws ArrayIndexOutOfBoundsException {
+        int offset = data.length + Counter.ENDIANNESS.offset();
+        int endianness = 0;
+        for (int i = 0; i < 4; i++) {
+            endianness = endianness << 8 | (data[offset + i]);
+        }
         // Follow the same wrap-around addressing scheme of the interpreter.
-        int offset = counter.offset();
-        if (offset < 0) {
-            offset = data.length + offset;
+        offset = data.length + counter.offset();
+
+        boolean isBe = true;
+        switch (endianness) {
+            case 0:
+            case 0x12345678:
+                isBe = true;
+                break;
+            case 0x78563412:
+                isBe = false;
+                break;
+            default:
+                Log.wtf(TAG, "Unknown endianness: 0x" + Integer.toHexString(endianness));
         }
 
         // Decode 32bit big-endian integer into a long so we can count up beyond 2^31.
         long value = 0;
         for (int i = 0; i < 4; i++) {
-            value = value << 8 | (data[offset] & 0xFF);
-            offset++;
+            value = value << 8 | (data[offset + (isBe ? i : 3 - i)]);
         }
         return value;
     }
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt
index c1c1e29..64072e1 100644
--- a/tests/unit/src/android/net/apf/ApfV5Test.kt
+++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -15,8 +15,13 @@
  */
 package android.net.apf
 
+import android.net.apf.ApfCounterTracker.Counter
+import android.net.apf.ApfTestUtils.DROP
 import android.net.apf.ApfTestUtils.MIN_PKT_SIZE
+import android.net.apf.ApfTestUtils.PASS
+import android.net.apf.ApfTestUtils.assertDrop
 import android.net.apf.ApfTestUtils.assertPass
+import android.net.apf.ApfTestUtils.assertVerdict
 import android.net.apf.BaseApfGenerator.DROP_LABEL
 import android.net.apf.BaseApfGenerator.IllegalInstructionException
 import android.net.apf.BaseApfGenerator.MIN_APF_VERSION
@@ -26,6 +31,7 @@
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -459,6 +465,50 @@
         assertContentEquals(byteArrayOf(33, 34, 35, 1, 2, 3), ApfJniUtils.getTransmittedPacket())
     }
 
+    @Test
+    fun testPassDrop() {
+        var program = ApfV6Generator()
+                .addDrop()
+                .addPass()
+                .generate()
+        assertDrop(MIN_APF_VERSION_IN_DEV, program, testPacket)
+
+        var dataRegion = ByteArray(Counter.totalSize()) { 0 }
+        program = ApfV6Generator()
+                .addData(byteArrayOf())
+                .addCountAndDrop(Counter.DROPPED_ETH_BROADCAST.value())
+                .generate()
+        assertVerdict(MIN_APF_VERSION_IN_DEV, DROP, program, testPacket, dataRegion)
+        var counterMap = decodeCountersIntoMap(dataRegion)
+        assertEquals(mapOf<Counter, Long>(
+                Counter.TOTAL_PACKETS to 1,
+                Counter.DROPPED_ETH_BROADCAST to 1), counterMap)
+
+        dataRegion = ByteArray(Counter.totalSize()) { 0 }
+        program = ApfV6Generator()
+                .addData(byteArrayOf())
+                .addCountAndPass(Counter.PASSED_ARP.value())
+                .generate()
+        assertVerdict(MIN_APF_VERSION_IN_DEV, PASS, program, testPacket, dataRegion)
+        counterMap = decodeCountersIntoMap(dataRegion)
+        assertEquals(mapOf<Counter, Long>(
+                Counter.TOTAL_PACKETS to 1,
+                Counter.PASSED_ARP to 1), counterMap)
+    }
+
+    private fun decodeCountersIntoMap(counterBytes: ByteArray): Map<Counter, Long> {
+        val counters = Counter::class.java.enumConstants
+        val ret = HashMap<Counter, Long>()
+        // starting from index 2 to skip the endianness mark
+        for (c in listOf(*counters).subList(2, counters.size)) {
+            val value = ApfCounterTracker.getCounterValue(counterBytes, c)
+            if (value != 0L) {
+                ret[c] = value
+            }
+        }
+        return ret
+    }
+
     private fun encodeInstruction(opcode: Int, immLength: Int, register: Int): Byte {
         val immLengthEncoding = if (immLength == 4) 3 else immLength
         return opcode.shl(3).or(immLengthEncoding.shl(1)).or(register).toByte()