Merge "Add integration test to verify the incomplete IPv6 neighbors." into main
diff --git a/Android.bp b/Android.bp
index 5d5202a..d4008aa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -414,7 +414,7 @@
     name: "ApfGeneratorLib",
     defaults: ["NetworkStackReleaseApiLevel"],
     srcs: [
-        "src/android/net/apf/ApfConstant.java",
+        "src/android/net/apf/ApfConstants.java",
         "src/android/net/apf/ApfCounterTracker.java",
         "src/android/net/apf/ApfV4Generator.java",
         "src/android/net/apf/ApfV4GeneratorBase.java",
diff --git a/src/android/net/apf/ApfConstant.java b/src/android/net/apf/ApfConstants.java
similarity index 98%
rename from src/android/net/apf/ApfConstant.java
rename to src/android/net/apf/ApfConstants.java
index 68580be..f5f083e 100644
--- a/src/android/net/apf/ApfConstant.java
+++ b/src/android/net/apf/ApfConstants.java
@@ -18,9 +18,9 @@
 /**
  * The class which declares constants used in ApfFilter and unit tests.
  */
-public final class ApfConstant {
+public final class ApfConstants {
 
-    private ApfConstant() {}
+    private ApfConstants() {}
     public static final int ETH_HEADER_LEN = 14;
     public static final int ETH_DEST_ADDR_OFFSET = 0;
     public static final int ETH_ETHERTYPE_OFFSET = 12;
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java
index 6679793..ef77db6 100644
--- a/src/android/net/apf/ApfFilter.java
+++ b/src/android/net/apf/ApfFilter.java
@@ -16,47 +16,47 @@
 
 package android.net.apf;
 
-import static android.net.apf.ApfConstant.APF_MAX_ETH_TYPE_BLACK_LIST_LEN;
-import static android.net.apf.ApfConstant.ARP_HEADER_OFFSET;
-import static android.net.apf.ApfConstant.ARP_IPV4_HEADER;
-import static android.net.apf.ApfConstant.ARP_OPCODE_OFFSET;
-import static android.net.apf.ApfConstant.ARP_OPCODE_REPLY;
-import static android.net.apf.ApfConstant.ARP_OPCODE_REQUEST;
-import static android.net.apf.ApfConstant.ARP_SOURCE_IP_ADDRESS_OFFSET;
-import static android.net.apf.ApfConstant.ARP_TARGET_IP_ADDRESS_OFFSET;
-import static android.net.apf.ApfConstant.DHCP_CLIENT_MAC_OFFSET;
-import static android.net.apf.ApfConstant.DHCP_CLIENT_PORT;
-import static android.net.apf.ApfConstant.ECHO_PORT;
-import static android.net.apf.ApfConstant.ETH_DEST_ADDR_OFFSET;
-import static android.net.apf.ApfConstant.ETH_ETHERTYPE_OFFSET;
-import static android.net.apf.ApfConstant.ETH_HEADER_LEN;
-import static android.net.apf.ApfConstant.ETH_MULTICAST_MDNS_V4_MAC_ADDRESS;
-import static android.net.apf.ApfConstant.ETH_MULTICAST_MDNS_V6_MAC_ADDRESS;
-import static android.net.apf.ApfConstant.ETH_TYPE_MAX;
-import static android.net.apf.ApfConstant.ETH_TYPE_MIN;
-import static android.net.apf.ApfConstant.FIXED_ARP_REPLY_HEADER;
-import static android.net.apf.ApfConstant.ICMP6_TYPE_OFFSET;
-import static android.net.apf.ApfConstant.IPPROTO_HOPOPTS;
-import static android.net.apf.ApfConstant.IPV4_ANY_HOST_ADDRESS;
-import static android.net.apf.ApfConstant.IPV4_BROADCAST_ADDRESS;
-import static android.net.apf.ApfConstant.IPV4_DEST_ADDR_OFFSET;
-import static android.net.apf.ApfConstant.IPV4_FRAGMENT_MORE_FRAGS_MASK;
-import static android.net.apf.ApfConstant.IPV4_FRAGMENT_OFFSET_MASK;
-import static android.net.apf.ApfConstant.IPV4_FRAGMENT_OFFSET_OFFSET;
-import static android.net.apf.ApfConstant.IPV4_PROTOCOL_OFFSET;
-import static android.net.apf.ApfConstant.IPV4_TOTAL_LENGTH_OFFSET;
-import static android.net.apf.ApfConstant.IPV6_ALL_NODES_ADDRESS;
-import static android.net.apf.ApfConstant.IPV6_DEST_ADDR_OFFSET;
-import static android.net.apf.ApfConstant.IPV6_FLOW_LABEL_LEN;
-import static android.net.apf.ApfConstant.IPV6_FLOW_LABEL_OFFSET;
-import static android.net.apf.ApfConstant.IPV6_HEADER_LEN;
-import static android.net.apf.ApfConstant.IPV6_NEXT_HEADER_OFFSET;
-import static android.net.apf.ApfConstant.IPV6_SRC_ADDR_OFFSET;
-import static android.net.apf.ApfConstant.MDNS_PORT;
-import static android.net.apf.ApfConstant.MDNS_QDCOUNT_OFFSET;
-import static android.net.apf.ApfConstant.MDNS_QNAME_OFFSET;
-import static android.net.apf.ApfConstant.TCP_HEADER_SIZE_OFFSET;
-import static android.net.apf.ApfConstant.TCP_UDP_DESTINATION_PORT_OFFSET;
+import static android.net.apf.ApfConstants.APF_MAX_ETH_TYPE_BLACK_LIST_LEN;
+import static android.net.apf.ApfConstants.ARP_HEADER_OFFSET;
+import static android.net.apf.ApfConstants.ARP_IPV4_HEADER;
+import static android.net.apf.ApfConstants.ARP_OPCODE_OFFSET;
+import static android.net.apf.ApfConstants.ARP_OPCODE_REPLY;
+import static android.net.apf.ApfConstants.ARP_OPCODE_REQUEST;
+import static android.net.apf.ApfConstants.ARP_SOURCE_IP_ADDRESS_OFFSET;
+import static android.net.apf.ApfConstants.ARP_TARGET_IP_ADDRESS_OFFSET;
+import static android.net.apf.ApfConstants.DHCP_CLIENT_MAC_OFFSET;
+import static android.net.apf.ApfConstants.DHCP_CLIENT_PORT;
+import static android.net.apf.ApfConstants.ECHO_PORT;
+import static android.net.apf.ApfConstants.ETH_DEST_ADDR_OFFSET;
+import static android.net.apf.ApfConstants.ETH_ETHERTYPE_OFFSET;
+import static android.net.apf.ApfConstants.ETH_HEADER_LEN;
+import static android.net.apf.ApfConstants.ETH_MULTICAST_MDNS_V4_MAC_ADDRESS;
+import static android.net.apf.ApfConstants.ETH_MULTICAST_MDNS_V6_MAC_ADDRESS;
+import static android.net.apf.ApfConstants.ETH_TYPE_MAX;
+import static android.net.apf.ApfConstants.ETH_TYPE_MIN;
+import static android.net.apf.ApfConstants.FIXED_ARP_REPLY_HEADER;
+import static android.net.apf.ApfConstants.ICMP6_TYPE_OFFSET;
+import static android.net.apf.ApfConstants.IPPROTO_HOPOPTS;
+import static android.net.apf.ApfConstants.IPV4_ANY_HOST_ADDRESS;
+import static android.net.apf.ApfConstants.IPV4_BROADCAST_ADDRESS;
+import static android.net.apf.ApfConstants.IPV4_DEST_ADDR_OFFSET;
+import static android.net.apf.ApfConstants.IPV4_FRAGMENT_MORE_FRAGS_MASK;
+import static android.net.apf.ApfConstants.IPV4_FRAGMENT_OFFSET_MASK;
+import static android.net.apf.ApfConstants.IPV4_FRAGMENT_OFFSET_OFFSET;
+import static android.net.apf.ApfConstants.IPV4_PROTOCOL_OFFSET;
+import static android.net.apf.ApfConstants.IPV4_TOTAL_LENGTH_OFFSET;
+import static android.net.apf.ApfConstants.IPV6_ALL_NODES_ADDRESS;
+import static android.net.apf.ApfConstants.IPV6_DEST_ADDR_OFFSET;
+import static android.net.apf.ApfConstants.IPV6_FLOW_LABEL_LEN;
+import static android.net.apf.ApfConstants.IPV6_FLOW_LABEL_OFFSET;
+import static android.net.apf.ApfConstants.IPV6_HEADER_LEN;
+import static android.net.apf.ApfConstants.IPV6_NEXT_HEADER_OFFSET;
+import static android.net.apf.ApfConstants.IPV6_SRC_ADDR_OFFSET;
+import static android.net.apf.ApfConstants.MDNS_PORT;
+import static android.net.apf.ApfConstants.MDNS_QDCOUNT_OFFSET;
+import static android.net.apf.ApfConstants.MDNS_QNAME_OFFSET;
+import static android.net.apf.ApfConstants.TCP_HEADER_SIZE_OFFSET;
+import static android.net.apf.ApfConstants.TCP_UDP_DESTINATION_PORT_OFFSET;
 import static android.net.apf.BaseApfGenerator.MemorySlot;
 import static android.net.apf.BaseApfGenerator.Register.R0;
 import static android.net.apf.BaseApfGenerator.Register.R1;
@@ -1974,7 +1974,7 @@
         ApfV4GeneratorBase<?> gen;
         if (SdkLevel.isAtLeastV()
                 && ApfV6Generator.supportsVersion(mApfCapabilities.apfVersionSupported)) {
-            gen = new ApfV6Generator();
+            gen = new ApfV6Generator(mApfCapabilities.maximumApfProgramSize);
         } else {
             gen = new ApfV4Generator(mApfCapabilities.apfVersionSupported);
         }
diff --git a/src/android/net/apf/ApfV4Generator.java b/src/android/net/apf/ApfV4Generator.java
index 84824b2..6892d2c 100644
--- a/src/android/net/apf/ApfV4Generator.java
+++ b/src/android/net/apf/ApfV4Generator.java
@@ -15,6 +15,7 @@
  */
 package android.net.apf;
 
+import static android.net.apf.BaseApfGenerator.Rbit.Rbit1;
 import static android.net.apf.BaseApfGenerator.Register.R0;
 import static android.net.apf.BaseApfGenerator.Register.R1;
 
@@ -73,8 +74,8 @@
     }
 
     @Override
-    void addArithR1(Opcodes opcode) {
-        append(new Instruction(opcode, R1));
+    void addR0ArithR1(Opcodes opcode) {
+        append(new Instruction(opcode, Rbit1));  // APFv2/4: R0 op= R1
     }
 
     /**
@@ -88,7 +89,7 @@
     @Override
     public ApfV4Generator addCountAndPass(ApfCounterTracker.Counter counter) {
         checkPassCounterRange(counter);
-        return maybeAddLoadR1CounterOffset(counter).addJump(mCountAndPassLabel);
+        return maybeAddLoadCounterOffset(R1, counter).addJump(mCountAndPassLabel);
     }
 
     /**
@@ -102,31 +103,31 @@
     @Override
     public ApfV4Generator addCountAndDrop(ApfCounterTracker.Counter counter) {
         checkDropCounterRange(counter);
-        return maybeAddLoadR1CounterOffset(counter).addJump(mCountAndDropLabel);
+        return maybeAddLoadCounterOffset(R1, counter).addJump(mCountAndDropLabel);
     }
 
     @Override
     public ApfV4Generator addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt) {
         checkDropCounterRange(cnt);
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0Equals(val, mCountAndDropLabel);
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0Equals(val, mCountAndDropLabel);
     }
 
     @Override
     public ApfV4Generator addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt) {
         checkPassCounterRange(cnt);
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0Equals(val, mCountAndPassLabel);
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0Equals(val, mCountAndPassLabel);
     }
 
     @Override
     public ApfV4Generator addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) {
         checkDropCounterRange(cnt);
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0NotEquals(val, mCountAndDropLabel);
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0NotEquals(val, mCountAndDropLabel);
     }
 
     @Override
     public ApfV4Generator addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) {
         checkPassCounterRange(cnt);
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0NotEquals(val, mCountAndPassLabel);
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0NotEquals(val, mCountAndPassLabel);
     }
 
     @Override
@@ -135,7 +136,7 @@
         if (val <= 0) {
             throw new IllegalArgumentException("val must > 0, current val: " + val);
         }
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0LessThan(val, mCountAndDropLabel);
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0LessThan(val, mCountAndDropLabel);
     }
 
     @Override
@@ -144,14 +145,14 @@
         if (val <= 0) {
             throw new IllegalArgumentException("val must > 0, current val: " + val);
         }
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0LessThan(val, mCountAndPassLabel);
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0LessThan(val, mCountAndPassLabel);
     }
 
     @Override
     public ApfV4Generator addCountAndDropIfBytesAtR0NotEqual(byte[] bytes,
             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
         checkDropCounterRange(cnt);
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfBytesAtR0NotEqual(bytes,
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfBytesAtR0NotEqual(bytes,
                 mCountAndDropLabel);
     }
 
@@ -159,7 +160,7 @@
     public ApfV4Generator addCountAndPassIfBytesAtR0NotEqual(byte[] bytes,
             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
         checkPassCounterRange(cnt);
-        return maybeAddLoadR1CounterOffset(cnt).addJumpIfBytesAtR0NotEqual(bytes,
+        return maybeAddLoadCounterOffset(R1, cnt).addJumpIfBytesAtR0NotEqual(bytes,
                 mCountAndPassLabel);
     }
 
@@ -224,12 +225,14 @@
                 .addJump(DROP_LABEL);
     }
 
+    /**
+     * This function is no-op in APFv4
+     */
+    @Override
+    void updateExceptionBufferSize(int programSize) { }
+
     private ApfV4Generator maybeAddLoadCounterOffset(Register reg, ApfCounterTracker.Counter cnt) {
         if (mVersion < 4) return self();
         return addLoadImmediate(reg, cnt.offset());
     }
-
-    private ApfV4Generator maybeAddLoadR1CounterOffset(ApfCounterTracker.Counter counter) {
-        return maybeAddLoadCounterOffset(R1, counter);
-    }
 }
diff --git a/src/android/net/apf/ApfV4GeneratorBase.java b/src/android/net/apf/ApfV4GeneratorBase.java
index be3da08..6dcc338 100644
--- a/src/android/net/apf/ApfV4GeneratorBase.java
+++ b/src/android/net/apf/ApfV4GeneratorBase.java
@@ -218,14 +218,14 @@
         return addLeftShift(-val);
     }
 
-    // Argument should be one of Opcodes.{ADD,MUL,DIV,AND,OR,SH}
-    abstract void addArithR1(Opcodes opcode);
+    // R0 op= R1, where op should be one of Opcodes.{ADD,MUL,DIV,AND,OR,SH}
+    abstract void addR0ArithR1(Opcodes opcode);
 
     /**
      * Add an instruction to the end of the program to add register R1 to register R0.
      */
     public final Type addAddR1ToR0() {
-        addArithR1(Opcodes.ADD);
+        addR0ArithR1(Opcodes.ADD);  // R0 += R1
         return self();
     }
 
@@ -233,7 +233,7 @@
      * Add an instruction to the end of the program to multiply register R0 by register R1.
      */
     public final Type addMulR0ByR1() {
-        addArithR1(Opcodes.MUL);
+        addR0ArithR1(Opcodes.MUL);  // R0 *= R1
         return self();
     }
 
@@ -241,7 +241,7 @@
      * Add an instruction to the end of the program to divide register R0 by register R1.
      */
     public final Type addDivR0ByR1() {
-        addArithR1(Opcodes.DIV);
+        addR0ArithR1(Opcodes.DIV);  // R0 /= R1
         return self();
     }
 
@@ -250,7 +250,7 @@
      * and store the result back into register R0.
      */
     public final Type addAndR0WithR1() {
-        addArithR1(Opcodes.AND);
+        addR0ArithR1(Opcodes.AND);  // R0 &= R1
         return self();
     }
 
@@ -259,7 +259,7 @@
      * and store the result back into register R0.
      */
     public final Type addOrR0WithR1() {
-        addArithR1(Opcodes.OR);
+        addR0ArithR1(Opcodes.OR);  // R0 |= R1
         return self();
     }
 
@@ -268,7 +268,7 @@
      * register R1.
      */
     public final Type addLeftShiftR0ByR1() {
-        addArithR1(Opcodes.SH);
+        addR0ArithR1(Opcodes.SH);  // R0 <<= R1
         return self();
     }
 
diff --git a/src/android/net/apf/ApfV6Generator.java b/src/android/net/apf/ApfV6Generator.java
index 141cf8b..d425907 100644
--- a/src/android/net/apf/ApfV6Generator.java
+++ b/src/android/net/apf/ApfV6Generator.java
@@ -33,22 +33,28 @@
     }
 
     /**
-     * Creates an ApfV6Generator instance which is able to emit instructions for APFv6.
+     * Creates an ApfV6Generator instance which emits instructions for APFv6.
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public ApfV6Generator() throws IllegalInstructionException {
-        super();
-        addData(new byte[0]);
+    public ApfV6Generator(int maximumApfProgramSize) throws IllegalInstructionException {
+        this(new byte[0], maximumApfProgramSize);
+    }
+
+    @Override
+    void updateExceptionBufferSize(int programSize) throws IllegalInstructionException {
+        mInstructions.get(1).updateExceptionBufferSize(
+                mMaximumApfProgramSize - ApfCounterTracker.Counter.totalSize() - programSize);
     }
 
     /**
-     * Creates an ApfV6Generator instance which is able to emit instructions APFv6.
+     * Creates an ApfV6Generator instance which emits instructions APFv6.
      * Initializes the data region with {@code bytes}.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public ApfV6Generator(byte[] bytes) throws IllegalInstructionException {
-        super();
+    public ApfV6Generator(byte[] bytes, int maximumApfProgramSize)
+            throws IllegalInstructionException {
+        super(maximumApfProgramSize);
         Objects.requireNonNull(bytes);
         addData(bytes);
+        addExceptionBuffer(0);
     }
 }
diff --git a/src/android/net/apf/ApfV6GeneratorBase.java b/src/android/net/apf/ApfV6GeneratorBase.java
index 9cfbdd2..b8359ec 100644
--- a/src/android/net/apf/ApfV6GeneratorBase.java
+++ b/src/android/net/apf/ApfV6GeneratorBase.java
@@ -17,6 +17,7 @@
 
 import static android.net.apf.BaseApfGenerator.Rbit.Rbit0;
 import static android.net.apf.BaseApfGenerator.Rbit.Rbit1;
+import static android.net.apf.BaseApfGenerator.Register.R0;
 import static android.net.apf.BaseApfGenerator.Register.R1;
 
 import androidx.annotation.NonNull;
@@ -41,14 +42,17 @@
 public abstract class ApfV6GeneratorBase<Type extends ApfV6GeneratorBase<Type>> extends
         ApfV4GeneratorBase<Type> {
 
+    final int mMaximumApfProgramSize;
+
     /**
      * Creates an ApfV6GeneratorBase instance which is able to emit instructions for the specified
      * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
      * the requested version is unsupported.
      *
      */
-    public ApfV6GeneratorBase() throws IllegalInstructionException {
+    public ApfV6GeneratorBase(int maximumApfProgramSize) throws IllegalInstructionException {
         super(APF_VERSION_6, false);
+        this.mMaximumApfProgramSize = maximumApfProgramSize;
     }
 
     /**
@@ -126,6 +130,14 @@
     }
 
     /**
+     * Add an instruction to the end of the program to set the exception buffer size.
+     * @param bufSize the exception buffer size
+     */
+    public final Type addExceptionBuffer(int bufSize) throws IllegalInstructionException {
+        return append(new Instruction(ExtendedOpcodes.EXCEPTIONBUFFER).addU16(bufSize));
+    }
+
+    /**
      * Add an instruction to the end of the program to transmit the allocated buffer without
      * checksum.
      */
@@ -599,8 +611,8 @@
     }
 
     @Override
-    void addArithR1(Opcodes opcode) {
-        append(new Instruction(opcode, R1));
+    void addR0ArithR1(Opcodes opcode) {
+        append(new Instruction(opcode, R0));  // APFv6+: R0 op= R1
     }
 
     /**
diff --git a/src/android/net/apf/BaseApfGenerator.java b/src/android/net/apf/BaseApfGenerator.java
index cbcc125..a53913b 100644
--- a/src/android/net/apf/BaseApfGenerator.java
+++ b/src/android/net/apf/BaseApfGenerator.java
@@ -184,7 +184,12 @@
         //        middle 2 bits - 1..4 length of immediates - 1
         //        bottom 1 bit  - =0 jmp if in set, =1 if not in set
         // imm4(imm3 * 1/2/3/4 bytes): the *UNIQUE* values to compare against
-        JONEOF(47);
+        JONEOF(47),
+        /* Specify length of exception buffer, which is populated on abnormal program termination.
+         * imm1: Extended opcode
+         * imm2(u16): Length of exception buffer (located *immediately* after the program itself)
+         */
+        EXCEPTIONBUFFER(48);
 
         final int value;
 
@@ -505,6 +510,22 @@
         }
 
         /**
+         * Updates exception buffer size.
+         * @param bufSize the new exception buffer size
+         */
+        void updateExceptionBufferSize(int bufSize) throws IllegalInstructionException {
+            if (mOpcode != Opcodes.EXT || mIntImms.get(0).mValue
+                    != ExtendedOpcodes.EXCEPTIONBUFFER.value) {
+                throw new IllegalInstructionException(
+                        "updateExceptionBuffer() is only valid for EXCEPTIONBUFFER opcode");
+            }
+            // Update the buffer size immediate (second imm) value. Due to mValue within
+            // IntImmediate being final, we must remove and re-add the value to apply changes.
+            mIntImms.remove(1);
+            addU16(bufSize);
+        }
+
+        /**
          * @return size of instruction in bytes.
          */
         int size() {
@@ -727,6 +748,11 @@
     }
 
     /**
+     * Updates the exception buffer size.
+     */
+    abstract void updateExceptionBufferSize(int programSize) throws IllegalInstructionException;
+
+    /**
      * Generate the bytecode for the APF program.
      * @return the bytecode.
      * @throws IllegalStateException if a label is referenced but not defined.
@@ -765,6 +791,7 @@
         } while (shrunk);
         // Generate bytecode for instructions.
         byte[] bytecode = new byte[total_size];
+        updateExceptionBufferSize(total_size);
         for (Instruction instruction : mInstructions) {
             instruction.generate(bytecode);
         }
diff --git a/tests/unit/src/android/net/apf/ApfStandaloneTest.kt b/tests/unit/src/android/net/apf/ApfStandaloneTest.kt
index 6012ed1..1a2307d 100644
--- a/tests/unit/src/android/net/apf/ApfStandaloneTest.kt
+++ b/tests/unit/src/android/net/apf/ApfStandaloneTest.kt
@@ -15,15 +15,15 @@
  */
 package android.net.apf
 
-import android.net.apf.ApfConstant.DHCP_SERVER_PORT
-import android.net.apf.ApfConstant.ETH_HEADER_LEN
-import android.net.apf.ApfConstant.ICMP6_TYPE_OFFSET
-import android.net.apf.ApfConstant.IPV4_BROADCAST_ADDRESS
-import android.net.apf.ApfConstant.IPV4_DEST_ADDR_OFFSET
-import android.net.apf.ApfConstant.IPV4_PROTOCOL_OFFSET
-import android.net.apf.ApfConstant.IPV4_SRC_ADDR_OFFSET
-import android.net.apf.ApfConstant.IPV6_NEXT_HEADER_OFFSET
-import android.net.apf.ApfConstant.TCP_UDP_DESTINATION_PORT_OFFSET
+import android.net.apf.ApfConstants.DHCP_SERVER_PORT
+import android.net.apf.ApfConstants.ETH_HEADER_LEN
+import android.net.apf.ApfConstants.ICMP6_TYPE_OFFSET
+import android.net.apf.ApfConstants.IPV4_BROADCAST_ADDRESS
+import android.net.apf.ApfConstants.IPV4_DEST_ADDR_OFFSET
+import android.net.apf.ApfConstants.IPV4_PROTOCOL_OFFSET
+import android.net.apf.ApfConstants.IPV4_SRC_ADDR_OFFSET
+import android.net.apf.ApfConstants.IPV6_NEXT_HEADER_OFFSET
+import android.net.apf.ApfConstants.TCP_UDP_DESTINATION_PORT_OFFSET
 import android.net.apf.BaseApfGenerator.APF_VERSION_4
 import android.net.apf.BaseApfGenerator.MemorySlot
 import android.net.apf.BaseApfGenerator.Register.R0
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java
index df8bad0..871a431 100644
--- a/tests/unit/src/android/net/apf/ApfTest.java
+++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -3531,7 +3531,7 @@
     @Test
     public void testApfGeneratorPropagation() throws IllegalInstructionException {
         ApfV4Generator v4Gen = new ApfV4Generator(APF_VERSION_4);
-        ApfV6Generator v6Gen = new ApfV6Generator();
+        ApfV6Generator v6Gen = new ApfV6Generator(1024);
         assertEquals(4, deriveApfGeneratorVersion(v4Gen));
         assertEquals(6, deriveApfGeneratorVersion(v6Gen));
     }
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt
index 4107198..435dcf7 100644
--- a/tests/unit/src/android/net/apf/ApfV5Test.kt
+++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -92,6 +92,8 @@
     @Mock
     private lateinit var dependencies: Dependencies
 
+    private val defaultMaximumApfProgramSize = 2048
+
     private val testPacket = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8,
                                          9, 10, 11, 12, 13, 14, 15, 16)
     private val hostIpv4Address = byteArrayOf(10, 0, 0, 1)
@@ -114,15 +116,17 @@
 
     @Test
     fun testDataInstructionMustComeFirst() {
-        var gen = ApfV6Generator()
+        var gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addAllocateR0()
         assertFailsWith<IllegalInstructionException> { gen.addData(ByteArray(3) { 0x01 }) }
     }
 
     @Test
     fun testApfInstructionEncodingSizeCheck() {
-        var gen = ApfV6Generator()
-        assertFailsWith<IllegalArgumentException> { ApfV6Generator(ByteArray(65536) { 0x01 }) }
+        var gen = ApfV6Generator(defaultMaximumApfProgramSize)
+        assertFailsWith<IllegalArgumentException> {
+            ApfV6Generator(ByteArray(65536) { 0x01 }, defaultMaximumApfProgramSize)
+        }
         assertFailsWith<IllegalArgumentException> { gen.addAllocate(65536) }
         assertFailsWith<IllegalArgumentException> { gen.addAllocate(-1) }
         assertFailsWith<IllegalArgumentException> { gen.addDataCopy(-1, 1) }
@@ -355,7 +359,7 @@
     fun testValidateDnsNames() {
         // '%' is a valid label character in mDNS subtype
         // byte == 0xff means it is a '*' wildcard, which is a valid encoding.
-        val program = ApfV6Generator().addJumpIfPktAtR0ContainDnsQ(
+        val program = ApfV6Generator(defaultMaximumApfProgramSize).addJumpIfPktAtR0ContainDnsQ(
                 byteArrayOf(1, '%'.code.toByte(), 0, 0),
                 1,
                 DROP_LABEL
@@ -380,9 +384,9 @@
                 ApfJniUtils.disassembleApf(program).map { it.trim() }
         )
 
-        var gen = ApfV6Generator()
+        var gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addDrop()
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         // encoding DROP opcode: opcode=0, imm_len=0, R=1
         assertContentEquals(
                 byteArrayOf(encodeInstruction(opcode = 0, immLength = 0, register = 1)),
@@ -393,9 +397,9 @@
                 ApfJniUtils.disassembleApf(program).map { it.trim() }
         )
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addCountAndPass(129)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         // encoding COUNT(PASS) opcode: opcode=0, imm_len=size_of(imm), R=0, imm=counterNumber
         assertContentEquals(
                 byteArrayOf(
@@ -409,9 +413,9 @@
                 ApfJniUtils.disassembleApf(program).map { it.trim() }
         )
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addCountAndDrop(1000)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         // encoding COUNT(DROP) opcode: opcode=0, imm_len=size_of(imm), R=1, imm=counterNumber
         assertContentEquals(
                 byteArrayOf(
@@ -426,9 +430,9 @@
                 ApfJniUtils.disassembleApf(program).map { it.trim() }
         )
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addCountAndPass(PASSED_ARP)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         // encoding COUNT(PASS) opcode: opcode=0, imm_len=size_of(imm), R=0, imm=counterNumber
         assertContentEquals(
                 byteArrayOf(
@@ -442,9 +446,9 @@
                 ApfJniUtils.disassembleApf(program).map { it.trim() }
         )
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addCountAndDrop(DROPPED_ETHERTYPE_DENYLISTED)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         // encoding COUNT(DROP) opcode: opcode=0, imm_len=size_of(imm), R=1, imm=counterNumber
         assertContentEquals(
                 byteArrayOf(
@@ -458,10 +462,10 @@
                 ApfJniUtils.disassembleApf(program).map { it.trim() }
         )
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addAllocateR0()
         gen.addAllocate(1500)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         // encoding ALLOC opcode: opcode=21(EXT opcode number), imm=36(TRANS opcode number).
         // R=0 means length stored in R0. R=1 means the length stored in imm1.
         assertContentEquals(
@@ -480,10 +484,10 @@
                 "2: allocate    1500"
         ), ApfJniUtils.disassembleApf(program).map { it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addTransmitWithoutChecksum()
         gen.addTransmitL4(30, 40, 50, 256, true)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         // encoding TRANSMIT opcode: opcode=21(EXT opcode number),
         // imm=37(TRANSMIT opcode number),
         assertContentEquals(byteArrayOf(
@@ -497,23 +501,25 @@
         ), ApfJniUtils.disassembleApf(program).map { it.trim() })
 
         val largeByteArray = ByteArray(256) { 0x01 }
-        gen = ApfV6Generator(largeByteArray)
+        gen = ApfV6Generator(largeByteArray, defaultMaximumApfProgramSize)
         program = gen.generate()
-        // encoding DATA opcode: opcode=14(JMP), R=1
         assertContentEquals(
                 byteArrayOf(
-                        encodeInstruction(opcode = 14, immLength = 2, register = 1),
-                        0x01,
-                        0x00
-                ) + largeByteArray,
+                        encodeInstruction(opcode = 14, immLength = 2, register = 1), 1, 0
+                ) + largeByteArray + byteArrayOf(
+                        encodeInstruction(opcode = 21, immLength = 1, register = 0), 48, 6, 49
+                ),
                 program
         )
         assertContentEquals(
-                listOf("0: data        256, " + "01".repeat(256) ),
+                listOf(
+                        "0: data        256, " + "01".repeat(256),
+                        "259: debugbuf    size=1585"
+                ),
                 ApfJniUtils.disassembleApf(program).map { it.trim() }
         )
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addWriteU8(0x01)
         gen.addWriteU16(0x0102)
         gen.addWriteU32(0x01020304)
@@ -525,7 +531,7 @@
         gen.addWriteU32(0x80000000)
         gen.addWrite32(-2)
         gen.addWrite32(byteArrayOf(0xff.toByte(), 0xfe.toByte(), 0xfd.toByte(), 0xfc.toByte()))
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(24, 1, 0), 0x01,
                 encodeInstruction(24, 2, 0), 0x01, 0x02,
@@ -554,14 +560,14 @@
                 "35: write       0xfffefdfc"
         ), ApfJniUtils.disassembleApf(program).map { it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addWriteU8(R0)
         gen.addWriteU16(R0)
         gen.addWriteU32(R0)
         gen.addWriteU8(R1)
         gen.addWriteU16(R1)
         gen.addWriteU32(R1)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(21, 1, 0), 38,
                 encodeInstruction(21, 1, 0), 39,
@@ -579,11 +585,11 @@
                 "10: ewrite4     r1"
         ), ApfJniUtils.disassembleApf(program).map { it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addDataCopy(0, 10)
         gen.addDataCopy(1, 5)
         gen.addPacketCopy(1000, 255)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(25, 0, 1), 10,
                 encodeInstruction(25, 1, 1), 1, 5,
@@ -596,12 +602,12 @@
                 "5: pktcopy     src=1000, len=255"
         ), ApfJniUtils.disassembleApf(program).map { it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addDataCopyFromR0(5)
         gen.addPacketCopyFromR0(5)
         gen.addDataCopyFromR0LenR1()
         gen.addPacketCopyFromR0LenR1()
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(21, 1, 1), 41, 5,
                 encodeInstruction(21, 1, 0), 41, 5,
@@ -615,9 +621,9 @@
                 "8: epktcopy     src=r0, len=r1"
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfBytesAtR0Equal(byteArrayOf('a'.code.toByte()), ApfV4Generator.DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(opcode = 20, immLength = 1, register = 1),
                 1,
@@ -629,10 +635,10 @@
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
 
         val qnames = byteArrayOf(1, 'A'.code.toByte(), 1, 'B'.code.toByte(), 0, 0)
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfPktAtR0DoesNotContainDnsQ(qnames, 0x0c, ApfV4Generator.DROP_LABEL)
         gen.addJumpIfPktAtR0ContainDnsQ(qnames, 0x0c, ApfV4Generator.DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(21, 1, 0), 43, 11, 0x0c.toByte(),
         ) + qnames + byteArrayOf(
@@ -643,10 +649,10 @@
                 "10: jdnsqeq     r0, DROP, 12, (1)A(1)B(0)(0)"
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfPktAtR0DoesNotContainDnsQSafe(qnames, 0x0c, ApfV4Generator.DROP_LABEL)
         gen.addJumpIfPktAtR0ContainDnsQSafe(qnames, 0x0c, ApfV4Generator.DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(21, 1, 0), 45, 11, 0x0c.toByte(),
         ) + qnames + byteArrayOf(
@@ -657,10 +663,10 @@
                 "10: jdnsqeqsafe r0, DROP, 12, (1)A(1)B(0)(0)"
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfPktAtR0DoesNotContainDnsA(qnames, ApfV4Generator.DROP_LABEL)
         gen.addJumpIfPktAtR0ContainDnsA(qnames, ApfV4Generator.DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(21, 1, 0), 44, 10,
         ) + qnames + byteArrayOf(
@@ -671,10 +677,10 @@
                 "9: jdnsaeq     r0, DROP, (1)A(1)B(0)(0)"
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfPktAtR0DoesNotContainDnsASafe(qnames, ApfV4Generator.DROP_LABEL)
         gen.addJumpIfPktAtR0ContainDnsASafe(qnames, ApfV4Generator.DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(21, 1, 0), 46, 10,
         ) + qnames + byteArrayOf(
@@ -685,11 +691,11 @@
                 "9: jdnsaeqsafe r0, DROP, (1)A(1)B(0)(0)"
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfOneOf(R1, List(32) { (it + 1).toLong() }.toSet(), DROP_LABEL)
         gen.addJumpIfOneOf(R0, setOf(0, 257, 65536), DROP_LABEL)
         gen.addJumpIfNoneOf(R0, setOf(1, 2, 3), DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(21, 1, 1), 47, 24, -16, 1, 2, 3, 4, 5, 6,
                 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
@@ -699,20 +705,20 @@
                 encodeInstruction(21, 1, 0), 47, 1, 9, 1, 2, 3
         ), program)
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfOneOf(R0, setOf(0, 128, 256, 65536), DROP_LABEL)
         gen.addJumpIfNoneOf(R1, setOf(0, 128, 256, 65536), DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(listOf(
                 "0: joneof      r0, DROP, { 0, 128, 256, 65536 }",
                 "20: jnoneof     r1, DROP, { 0, 128, 256, 65536 }"
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
 
-        gen = ApfV6Generator()
+        gen = ApfV6Generator(defaultMaximumApfProgramSize)
         gen.addJumpIfBytesAtR0EqualsAnyOf(listOf(byteArrayOf(1, 2), byteArrayOf(3, 4)), DROP_LABEL)
         gen.addJumpIfBytesAtR0EqualNoneOf(listOf(byteArrayOf(1, 2), byteArrayOf(3, 4)), DROP_LABEL)
         gen.addJumpIfBytesAtR0EqualNoneOf(listOf(byteArrayOf(1, 1), byteArrayOf(1, 1)), DROP_LABEL)
-        program = gen.generate().skipEmptyData()
+        program = gen.generate().skipDataAndDebug()
         assertContentEquals(byteArrayOf(
                 encodeInstruction(opcode = 20, immLength = 2, register = 1),
                 0, 15, 8, 2, 1, 2, 3, 4,
@@ -730,7 +736,7 @@
 
     @Test
     fun testWriteToTxBuffer() {
-        var program = ApfV6Generator()
+        var program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addAllocate(14)
                 .addWriteU8(0x01)
                 .addWriteU16(0x0203)
@@ -757,7 +763,7 @@
 
     @Test
     fun testCopyToTxBuffer() {
-        var program = ApfV6Generator(byteArrayOf(33, 34, 35))
+        var program = ApfV6Generator(byteArrayOf(33, 34, 35), defaultMaximumApfProgramSize)
                 .addAllocate(14)
                 .addDataCopy(3, 2) // arg1=src, arg2=len
                 .addDataCopy(5, 1) // arg1=src, arg2=len
@@ -784,7 +790,7 @@
 
     @Test
     fun testCopyContentToTxBuffer() {
-        val program = ApfV6Generator()
+        val program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addAllocate(18)
                 .addDataCopy(HexDump.hexStringToByteArray("112233445566"))
                 .addDataCopy(HexDump.hexStringToByteArray("223344"))
@@ -794,12 +800,13 @@
                 .generate()
         assertContentEquals(listOf(
                 "0: data        9, 112233445566778899",
-                "12: allocate    18",
-                "16: datacopy    src=3, len=6",
-                "19: datacopy    src=4, len=3",
-                "22: datacopy    src=9, len=3",
-                "25: datacopy    src=3, len=6",
-                "28: transmit    ip_ofs=255"
+                "12: debugbuf    size=1812",
+                "16: allocate    18",
+                "20: datacopy    src=3, len=6",
+                "23: datacopy    src=4, len=3",
+                "26: datacopy    src=9, len=3",
+                "29: datacopy    src=3, len=6",
+                "32: transmit    ip_ofs=255"
         ), ApfJniUtils.disassembleApf(program).map{ it.trim() })
         assertPass(APF_VERSION_6, program, testPacket)
         val transmitPkt = HexDump.toHexString(ApfJniUtils.getTransmittedPacket())
@@ -808,14 +815,14 @@
 
     @Test
     fun testPassDrop() {
-        var program = ApfV6Generator()
+        var program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addDrop()
                 .addPass()
                 .generate()
         assertDrop(APF_VERSION_6, program, testPacket)
 
         var dataRegion = ByteArray(Counter.totalSize()) { 0 }
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addCountAndDrop(Counter.DROPPED_ETH_BROADCAST)
                 .generate()
         assertVerdict(APF_VERSION_6, DROP, program, testPacket, dataRegion)
@@ -826,7 +833,7 @@
         ), counterMap)
 
         dataRegion = ByteArray(Counter.totalSize()) { 0 }
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addCountAndPass(Counter.PASSED_ARP)
                 .generate()
         assertVerdict(APF_VERSION_6, PASS, program, testPacket, dataRegion)
@@ -845,7 +852,7 @@
         )
         doTestLoadStoreCounter (
                 { mutableMapOf(TOTAL_PACKETS to 1) },
-                { ApfV6Generator() }
+                { ApfV6Generator(defaultMaximumApfProgramSize) }
         )
     }
 
@@ -873,7 +880,7 @@
         )
         doTestCountAndPassDropCompareR0(
                 { mutableMapOf(Counter.TOTAL_PACKETS to 1) },
-                { ApfV6Generator() }
+                { ApfV6Generator(defaultMaximumApfProgramSize) }
         )
     }
 
@@ -1034,7 +1041,7 @@
 
     @Test
     fun testAllocateFailure() {
-        val program = ApfV6Generator()
+        val program = ApfV6Generator(defaultMaximumApfProgramSize)
                 // allocate size: 65535 > sizeof(apf_test_buffer): 1514, trigger allocate failure.
                 .addAllocate(65535)
                 .addDrop()
@@ -1050,7 +1057,7 @@
 
     @Test
     fun testTransmitFailure() {
-        val program = ApfV6Generator()
+        val program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addAllocate(14)
                 // len: 13 is less than ETH_HLEN, trigger transmit failure.
                 .addLoadImmediate(R0, 13)
@@ -1092,7 +1099,7 @@
                 0x00, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04, 0xc0, 0xa8, 0x01,
                 0x09,
         ).map { it.toByte() }.toByteArray()
-        val program = ApfV6Generator(etherIpv4UdpPacket)
+        val program = ApfV6Generator(etherIpv4UdpPacket, defaultMaximumApfProgramSize)
                 .addAllocate(etherIpv4UdpPacket.size)
                 .addDataCopy(3, etherIpv4UdpPacket.size) // arg1=src, arg2=len
                 .addTransmitL4(
@@ -1138,28 +1145,28 @@
                 0x00, 0x01, 0x00, 0x01 // type = A, class = 0x0001
         ).map { it.toByte() }.toByteArray()
 
-        var program = ApfV6Generator()
+        var program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsQ(needlesMatch, 0x01, DROP_LABEL) // arg2=qtype
                 .addPass()
                 .generate()
         assertDrop(APF_VERSION_6, program, udpPayload)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsQSafe(needlesMatch, 0x01, DROP_LABEL)
                 .addPass()
                 .generate()
         assertDrop(APF_VERSION_6, program, udpPayload)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0DoesNotContainDnsQ(needlesMatch, 0x01, DROP_LABEL) // arg2=qtype
                 .addPass()
                 .generate()
         assertPass(APF_VERSION_6, program, udpPayload)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0DoesNotContainDnsQSafe(needlesMatch, 0x01, DROP_LABEL) // arg2=qtype
                 .addPass()
@@ -1181,7 +1188,7 @@
                 0x00, 0x01, 0x00, 0x01 // type = A, class = 0x0001
         ).map { it.toByte() }.toByteArray()
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsQ(needlesMatch, 0x01, DROP_LABEL) // arg2=qtype
                 .addPass()
@@ -1194,7 +1201,7 @@
                 Counter.CORRUPT_DNS_PACKET to 1
         ), counterMap)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsQSafe(needlesMatch, 0x01, DROP_LABEL) // arg2=qtype
                 .addPass()
@@ -1239,28 +1246,28 @@
                 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09 // rdlengh = 4, rdata = 192.168.1.9
         ).map { it.toByte() }.toByteArray()
 
-        var program = ApfV6Generator()
+        var program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsA(needlesMatch, DROP_LABEL)
                 .addPass()
                 .generate()
         assertDrop(APF_VERSION_6, program, udpPayload)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsASafe(needlesMatch, DROP_LABEL)
                 .addPass()
                 .generate()
         assertDrop(APF_VERSION_6, program, udpPayload)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0DoesNotContainDnsA(needlesMatch, DROP_LABEL)
                 .addPass()
                 .generate()
         assertPass(APF_VERSION_6, program, udpPayload)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0DoesNotContainDnsASafe(needlesMatch, DROP_LABEL)
                 .addPass()
@@ -1286,7 +1293,7 @@
                 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09 // rdlengh = 4, rdata = 192.168.1.9
         ).map { it.toByte() }.toByteArray()
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsA(needlesMatch, DROP_LABEL)
                 .addPass()
@@ -1299,7 +1306,7 @@
                 Counter.CORRUPT_DNS_PACKET to 1
         ), counterMap)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfPktAtR0ContainDnsASafe(needlesMatch, DROP_LABEL)
                 .addPass()
@@ -1322,7 +1329,7 @@
 
     @Test
     fun testJumpMultipleByteSequencesMatch() {
-        var program = ApfV6Generator()
+        var program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfBytesAtR0EqualsAnyOf(
                         listOf(byteArrayOf(1, 2, 3), byteArrayOf(6, 5, 4)),
@@ -1332,7 +1339,7 @@
                 .generate()
         assertDrop(APF_VERSION_6, program, testPacket)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 2)
                 .addJumpIfBytesAtR0EqualsAnyOf(
                         listOf(byteArrayOf(1, 2, 3), byteArrayOf(6, 5, 4)),
@@ -1342,7 +1349,7 @@
                 .generate()
         assertPass(APF_VERSION_6, program, testPacket)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 1)
                 .addJumpIfBytesAtR0EqualNoneOf(
                         listOf(byteArrayOf(1, 2, 3), byteArrayOf(6, 5, 4)),
@@ -1352,7 +1359,7 @@
                 .generate()
         assertDrop(APF_VERSION_6, program, testPacket)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 0)
                 .addJumpIfBytesAtR0EqualNoneOf(
                         listOf(byteArrayOf(1, 2, 3), byteArrayOf(6, 5, 4)),
@@ -1365,28 +1372,28 @@
 
     @Test
     fun testJumpOneOf() {
-        var program = ApfV6Generator()
+        var program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 255)
                 .addJumpIfOneOf(R0, setOf(1, 2, 3, 128, 255), DROP_LABEL)
                 .addPass()
                 .generate()
         assertDrop(APF_VERSION_6, program, testPacket)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 254)
                 .addJumpIfOneOf(R0, setOf(1, 2, 3, 128, 255), DROP_LABEL)
                 .addPass()
                 .generate()
         assertPass(APF_VERSION_6, program, testPacket)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 254)
                 .addJumpIfNoneOf(R0, setOf(1, 2, 3, 128, 255), DROP_LABEL)
                 .addPass()
                 .generate()
         assertDrop(APF_VERSION_6, program, testPacket)
 
-        program = ApfV6Generator()
+        program = ApfV6Generator(defaultMaximumApfProgramSize)
                 .addLoadImmediate(R0, 255)
                 .addJumpIfNoneOf(R0, setOf(1, 2, 3, 128, 255), DROP_LABEL)
                 .addPass()
@@ -1394,6 +1401,18 @@
         assertPass(APF_VERSION_6, program, testPacket)
     }
 
+    @Test
+    fun testDebugBuffer() {
+        val program = ApfV6Generator(defaultMaximumApfProgramSize)
+                .addLoad8(R0, 255)
+                .generate()
+        val dataRegion = ByteArray(defaultMaximumApfProgramSize - program.size) { 0 }
+
+        assertVerdict(APF_VERSION_6, PASS, program, testPacket, dataRegion)
+        // offset 3 in the data region should contain if the interpreter is APFv6 mode or not
+        assertEquals(1, dataRegion[3])
+    }
+
     // The APFv6 code path is only turned on in V+
     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     @Test
@@ -1470,12 +1489,19 @@
         return opcode.shl(3).or(immLengthEncoding.shl(1)).or(register).toByte()
     }
 
-    private fun ByteArray.skipEmptyData(): ByteArray {
+    private fun ByteArray.skipDataAndDebug(): ByteArray {
         assertEquals(
-                listOf(encodeInstruction(14, 2, 1), 0, 0),
-                this.take(3)
+                listOf(
+                        encodeInstruction(14, 2, 1),
+                        0,
+                        0,
+                        encodeInstruction(21, 1, 0),
+                        48
+                        // the actual exception buffer size is not checked here.
+                ),
+                this.take(5)
         )
-        return this.drop(3).toByteArray()
+        return this.drop(7).toByteArray()
     }
 
     private fun getDefaultConfig(): ApfFilter.ApfConfiguration {