Updated EWRITE opcode logic to reflect latest design.

* design doc: go/apf-v6-proposal

Bug: 293811969
Test: TH
Change-Id: I0ed11f5397232a2656559b4b515fe70915619f2f
diff --git a/src/android/net/apf/ApfGenerator.java b/src/android/net/apf/ApfGenerator.java
index ee09e2c..80832a6 100644
--- a/src/android/net/apf/ApfGenerator.java
+++ b/src/android/net/apf/ApfGenerator.java
@@ -119,9 +119,12 @@
         // "e.g. discard"
         TRANSMIT(37),
         DISCARD(37),
-        EWRITE1(38), // Write 1 byte from register to the output buffer, e.g. "EWRITE1 R0"
-        EWRITE2(39), // Write 2 bytes from register to the output buffer, e.g. "EWRITE2 R0"
-        EWRITE4(40), // Write 4 bytes from register to the output buffer, e.g. "EWRITE4 R0"
+        // Write 1, 2 or 4 byte value from register to the output buffer and auto-increment the
+        // output buffer pointer.
+        // e.g. "ewrite1 r0"
+        EWRITE1(38),
+        EWRITE2(39),
+        EWRITE4(40),
         // Copy the data from input packet to output buffer. The source offset is encoded as [Rx
         // + second imm]. The copy length is encoded in the third imm. "e.g. EPKTCOPY [R0 + 5], 5"
         EPKTCOPY(41),
@@ -1065,34 +1068,32 @@
         return append(new Instruction(Opcodes.WRITE).overrideLenField(4).addU32(val));
     }
 
-    // TODO: add back when support EWRITE opcode
-//    /**
-//     * Add an instruction to the end of the program to write 1, 2 or 4 bytes value from register
-//     * to output buffer.
-//     *
-//     * @param register the register contains the value to be written
-//     * @param size the size of the value
-//     * @return the ApfGenerator object
-//     * @throws IllegalInstructionException throws when size is not 1, 2 or 4
-//     */
-//    public ApfGenerator addWrite(Register register, byte size)
-//            throws IllegalInstructionException {
-//        requireApfVersion(5);
-//        if (!(size == 1 || size == 2 || size == 4)) {
-//            throw new IllegalInstructionException(
-//                    "length field must be 1, 2 or 4");
-//        }
-//        Instruction instruction = new Instruction(Opcodes.EXT, register);
-//        if (size == 1) {
-//            instruction.addUnsignedImm(ExtendedOpcodes.EWRITE1.value);
-//        } else if (size == 2) {
-//            instruction.addUnsignedImm(ExtendedOpcodes.EWRITE2.value);
-//        } else {
-//            instruction.addUnsignedImm(ExtendedOpcodes.EWRITE4.value);
-//        }
-//        addInstruction(instruction);
-//        return this;
-//    }
+    /**
+     * Add an instruction to the end of the program to write 1 byte value from register to output
+     * buffer.
+     */
+    public ApfGenerator addWriteU8(Register reg) throws IllegalInstructionException {
+        requireApfVersion(MIN_APF_VERSION_IN_DEV);
+        return append(new Instruction(ExtendedOpcodes.EWRITE1, reg));
+    }
+
+    /**
+     * Add an instruction to the end of the program to write 2 byte value from register to output
+     * buffer.
+     */
+    public ApfGenerator addWriteU16(Register reg) throws IllegalInstructionException {
+        requireApfVersion(MIN_APF_VERSION_IN_DEV);
+        return append(new Instruction(ExtendedOpcodes.EWRITE2, reg));
+    }
+
+    /**
+     * Add an instruction to the end of the program to write 4 byte value from register to output
+     * buffer.
+     */
+    public ApfGenerator addWriteU32(Register reg) throws IllegalInstructionException {
+        requireApfVersion(MIN_APF_VERSION_IN_DEV);
+        return append(new Instruction(ExtendedOpcodes.EWRITE4, reg));
+    }
 
     /**
      * Add an instruction to the end of the program to copy data from APF program/data region to
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt
index 318c243..e64c866 100644
--- a/tests/unit/src/android/net/apf/ApfV5Test.kt
+++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -16,6 +16,8 @@
 package android.net.apf
 
 import android.net.apf.ApfGenerator.IllegalInstructionException
+import android.net.apf.ApfGenerator.Register.R0
+import android.net.apf.ApfGenerator.Register.R1
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import java.lang.IllegalArgumentException
@@ -47,6 +49,12 @@
         assertFailsWith<IllegalInstructionException> { gen.addWrite4(100) }
         assertFailsWith<IllegalInstructionException> { gen.addPacketCopy(100, 100) }
         assertFailsWith<IllegalInstructionException> { gen.addDataCopy(100, 100) }
+        assertFailsWith<IllegalInstructionException> { gen.addWriteU8(R0) }
+        assertFailsWith<IllegalInstructionException> { gen.addWriteU16(R0) }
+        assertFailsWith<IllegalInstructionException> { gen.addWriteU32(R0) }
+        assertFailsWith<IllegalInstructionException> { gen.addWriteU8(R1) }
+        assertFailsWith<IllegalInstructionException> { gen.addWriteU16(R1) }
+        assertFailsWith<IllegalInstructionException> { gen.addWriteU32(R1) }
     }
 
     @Test
@@ -171,21 +179,31 @@
                 "      20: write 0x00000000",
                 "      25: write 0x80000000"),
         ApfJniUtils.disassembleApf(program))
-        // TODO: add back the following test case when implementing EWRITE opcodes.
-//        gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
-//        gen.addWrite(ApfGenerator.Register.R0, 1)
-//        gen.addWrite(ApfGenerator.Register.R0, 2)
-//        gen.addWrite(ApfGenerator.Register.R0, 4)
-//        program = gen.generate()
-//        assertContentEquals(byteArrayOf(
-//                encodeInstruction(21, 1, 0), 38,
-//                encodeInstruction(21, 1, 0), 39,
-//                encodeInstruction(21, 1, 0), 40
-//        ), program)
+
+        gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
+        gen.addWriteU8(R0)
+        gen.addWriteU16(R0)
+        gen.addWriteU32(R0)
+        gen.addWriteU8(R1)
+        gen.addWriteU16(R1)
+        gen.addWriteU32(R1)
+        program = gen.generate()
+        assertContentEquals(byteArrayOf(
+                encodeInstruction(21, 1, 0), 38,
+                encodeInstruction(21, 1, 0), 39,
+                encodeInstruction(21, 1, 0), 40,
+                encodeInstruction(21, 1, 1), 38,
+                encodeInstruction(21, 1, 1), 39,
+                encodeInstruction(21, 1, 1), 40
+        ), program)
+        // TODO: add back disassembling test check after we update the apf_disassembler
 //        assertContentEquals(arrayOf(
-//                "       0: write r0, 1",
-//                "       2: write r0, 2",
-//                "       4: write r0, 4"), ApfJniUtils.disassembleApf(program))
+//                "       0: ewrite1 r0",
+//                "       2: ewrite2 r0",
+//                "       4: ewrite4 r0",
+//                "       6: ewrite1 r1",
+//                "       8: ewrite2 r1",
+//                "      10: ewrite4 r1"), ApfJniUtils.disassembleApf(program))
 
         gen = ApfGenerator(ApfGenerator.MIN_APF_VERSION_IN_DEV)
         gen.addDataCopy(0, 10)