Merge "PIP: Fix NPE when onboarding is shown" into nyc-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 43238ec..cfffe34 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4183,12 +4183,10 @@
                         // window is being added.
                         r.mPendingRemoveWindow = r.window;
                         r.mPendingRemoveWindowManager = wm;
-                        if (r.mPreserveWindow) {
-                            // We can only keep the part of the view hierarchy that we control,
-                            // everything else must be removed, because it might not be able to
-                            // behave properly when activity is relaunching.
-                            r.window.clearContentView();
-                        }
+                        // We can only keep the part of the view hierarchy that we control,
+                        // everything else must be removed, because it might not be able to
+                        // behave properly when activity is relaunching.
+                        r.window.clearContentView();
                     } else {
                         wm.removeViewImmediate(v);
                     }
@@ -4196,6 +4194,13 @@
                 if (wtoken != null && r.mPendingRemoveWindow == null) {
                     WindowManagerGlobal.getInstance().closeAll(wtoken,
                             r.activity.getClass().getName(), "Activity");
+                } else if (r.mPendingRemoveWindow != null) {
+                    // We're preserving only one window, others should be closed so app views
+                    // will be detached before the final tear down. It should be done now because
+                    // some components (e.g. WebView) rely on detach callbacks to perform receiver
+                    // unregister and other cleanup.
+                    WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
+                            r.activity.getClass().getName(), "Activity");
                 }
                 r.activity.mDecor = null;
             }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 887cc3a..11734d3 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -391,17 +391,34 @@
         }
     }
 
+    /**
+     * Remove all roots with specified token.
+     *
+     * @param token app or window token.
+     * @param who name of caller, used in logs.
+     * @param what type of caller, used in logs.
+     */
     public void closeAll(IBinder token, String who, String what) {
+        closeAllExceptView(token, null /* view */, who, what);
+    }
+
+    /**
+     * Remove all roots with specified token, except maybe one view.
+     *
+     * @param token app or window token.
+     * @param view view that should be should be preserved along with it's root.
+     *             Pass null if everything should be removed.
+     * @param who name of caller, used in logs.
+     * @param what type of caller, used in logs.
+     */
+    public void closeAllExceptView(IBinder token, View view, String who, String what) {
         synchronized (mLock) {
             int count = mViews.size();
-            //Log.i("foo", "Closing all windows of " + token);
             for (int i = 0; i < count; i++) {
-                //Log.i("foo", "@ " + i + " token " + mParams[i].token
-                //        + " view " + mRoots[i].getView());
-                if (token == null || mParams.get(i).token == token) {
+                if ((view == null || mViews.get(i) != view)
+                        && (token == null || mParams.get(i).token == token)) {
                     ViewRootImpl root = mRoots.get(i);
 
-                    //Log.i("foo", "Force closing " + root);
                     if (who != null) {
                         WindowLeaked leak = new WindowLeaked(
                                 what + " " + who + " has leaked window "
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index be53cfc..be021ea 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -73,6 +73,7 @@
         "/system/bin/surfaceflinger",
         "media.codec",     // system/bin/mediacodec
         "media.extractor", // system/bin/mediaextractor
+        "com.android.bluetooth",  // Bluetooth service
     };
 
     static Watchdog sWatchdog;
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
index 0ec50c4..b0e0230 100644
--- a/services/net/java/android/net/apf/ApfCapabilities.java
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -38,7 +38,8 @@
      */
     public final int apfPacketFormat;
 
-    ApfCapabilities(int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) {
+    public ApfCapabilities(int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat)
+    {
         this.apfVersionSupported = apfVersionSupported;
         this.maximumApfProgramSize = maximumApfProgramSize;
         this.apfPacketFormat = apfPacketFormat;
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 5a10275..4cbfd9c 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -31,6 +31,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -69,7 +70,8 @@
  */
 public class ApfFilter {
     // Thread to listen for RAs.
-    private class ReceiveThread extends Thread {
+    @VisibleForTesting
+    class ReceiveThread extends Thread {
         private final byte[] mPacket = new byte[1514];
         private final FileDescriptor mSocket;
         private volatile boolean mStopped;
@@ -151,8 +153,10 @@
     private final ApfCapabilities mApfCapabilities;
     private final IpManager.Callback mIpManagerCallback;
     private final NetworkInterface mNetworkInterface;
-    private byte[] mHardwareAddress;
-    private ReceiveThread mReceiveThread;
+    @VisibleForTesting
+    byte[] mHardwareAddress;
+    @VisibleForTesting
+    ReceiveThread mReceiveThread;
     @GuardedBy("this")
     private long mUniqueCounter;
     @GuardedBy("this")
@@ -161,7 +165,8 @@
     @GuardedBy("this")
     private byte[] mIPv4Address;
 
-    private ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
+    @VisibleForTesting
+    ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
             IpManager.Callback ipManagerCallback, boolean multicastFilter) {
         mApfCapabilities = apfCapabilities;
         mIpManagerCallback = ipManagerCallback;
@@ -184,7 +189,8 @@
      * Attempt to start listening for RAs and, if RAs are received, generating and installing
      * filters to ignore useless RAs.
      */
-    private void maybeStartFilter() {
+    @VisibleForTesting
+    void maybeStartFilter() {
         FileDescriptor socket;
         try {
             mHardwareAddress = mNetworkInterface.getHardwareAddress();
@@ -724,7 +730,8 @@
     }
 
     @GuardedBy("this")
-    private void installNewProgramLocked() {
+    @VisibleForTesting
+    void installNewProgramLocked() {
         purgeExpiredRasLocked();
         final byte[] program;
         long programMinLifetime = Long.MAX_VALUE;
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 34152cf..d8eab35 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -276,6 +276,16 @@
         public static class Builder {
             private ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
 
+            public Builder withoutIPv4() {
+                mConfig.mEnableIPv4 = false;
+                return this;
+            }
+
+            public Builder withoutIPv6() {
+                mConfig.mEnableIPv6 = false;
+                return this;
+            }
+
             public Builder withoutIpReachabilityMonitor() {
                 mConfig.mUsingIpReachabilityMonitor = false;
                 return this;
@@ -311,6 +321,8 @@
             }
         }
 
+        /* package */ boolean mEnableIPv4 = true;
+        /* package */ boolean mEnableIPv6 = true;
         /* package */ boolean mUsingIpReachabilityMonitor = true;
         /* package */ int mRequestedPreDhcpActionMs;
         /* package */ StaticIpConfiguration mStaticIpConfig;
@@ -320,6 +332,8 @@
         public ProvisioningConfiguration() {}
 
         public ProvisioningConfiguration(ProvisioningConfiguration other) {
+            mEnableIPv4 = other.mEnableIPv4;
+            mEnableIPv6 = other.mEnableIPv6;
             mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
             mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
             mStaticIpConfig = other.mStaticIpConfig;
@@ -330,6 +344,8 @@
         @Override
         public String toString() {
             return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
+                    .add("mEnableIPv4: " + mEnableIPv4)
+                    .add("mEnableIPv6: " + mEnableIPv6)
                     .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
                     .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
                     .add("mStaticIpConfig: " + mStaticIpConfig)
@@ -883,6 +899,51 @@
         }
     }
 
+    private boolean startIPv4() {
+        // If we have a StaticIpConfiguration attempt to apply it and
+        // handle the result accordingly.
+        if (mConfiguration.mStaticIpConfig != null) {
+            if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
+                handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
+            } else {
+                if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                recordMetric(IpManagerEvent.PROVISIONING_FAIL);
+                mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
+                return false;
+            }
+        } else {
+            // Start DHCPv4.
+            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
+            mDhcpClient.registerForPreDhcpNotification();
+            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
+
+            if (mConfiguration.mProvisioningTimeoutMs > 0) {
+                final long alarmTime = SystemClock.elapsedRealtime() +
+                        mConfiguration.mProvisioningTimeoutMs;
+                mProvisioningTimeoutAlarm.schedule(alarmTime);
+            }
+        }
+
+        return true;
+    }
+
+    private boolean startIPv6() {
+        // Set privacy extensions.
+        try {
+            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
+            mNwService.enableIpv6(mInterfaceName);
+        } catch (RemoteException re) {
+            Log.e(mTag, "Unable to change interface settings: " + re);
+            return false;
+        } catch (IllegalStateException ie) {
+            Log.e(mTag, "Unable to change interface settings: " + ie);
+            return false;
+        }
+
+        return true;
+    }
+
+
     class StoppedState extends State {
         @Override
         public void enter() {
@@ -978,15 +1039,9 @@
                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
             }
 
-            // Set privacy extensions.
-            try {
-                mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
-                mNwService.enableIpv6(mInterfaceName);
-                // TODO: Perhaps clearIPv4Address() as well.
-            } catch (RemoteException re) {
-                Log.e(mTag, "Unable to change interface settings: " + re);
-            } catch (IllegalStateException ie) {
-                Log.e(mTag, "Unable to change interface settings: " + ie);
+            if (mConfiguration.mEnableIPv6) {
+                // TODO: Consider transitionTo(mStoppingState) if this fails.
+                startIPv6();
             }
 
             if (mConfiguration.mUsingIpReachabilityMonitor) {
@@ -1001,31 +1056,10 @@
                         });
             }
 
-            // If we have a StaticIpConfiguration attempt to apply it and
-            // handle the result accordingly.
-            if (mConfiguration.mStaticIpConfig != null) {
-                if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
-                    handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
-                } else {
-                    if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
-                    recordMetric(IpManagerEvent.PROVISIONING_FAIL);
-                    mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
+            if (mConfiguration.mEnableIPv4) {
+                if (!startIPv4()) {
                     transitionTo(mStoppingState);
                 }
-            } else {
-                // Start DHCPv4.
-                mDhcpClient = DhcpClient.makeDhcpClient(
-                        mContext,
-                        IpManager.this,
-                        mInterfaceName);
-                mDhcpClient.registerForPreDhcpNotification();
-                mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
-
-                if (mConfiguration.mProvisioningTimeoutMs > 0) {
-                    final long alarmTime = SystemClock.elapsedRealtime() +
-                            mConfiguration.mProvisioningTimeoutMs;
-                    mProvisioningTimeoutAlarm.schedule(alarmTime);
-                }
             }
         }
 
diff --git a/services/tests/servicestests/jni/apf_jni.cpp b/services/tests/servicestests/jni/apf_jni.cpp
index 7d142eb..ee43dd4 100644
--- a/services/tests/servicestests/jni/apf_jni.cpp
+++ b/services/tests/servicestests/jni/apf_jni.cpp
@@ -175,7 +175,7 @@
                     (void*)com_android_server_ApfTest_compareBpfApf },
     };
 
-    jniRegisterNativeMethods(env, "com/android/server/ApfTest",
+    jniRegisterNativeMethods(env, "android/net/apf/ApfTest",
             gMethods, ARRAY_SIZE(gMethods));
 
     return JNI_VERSION_1_6;
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
new file mode 100644
index 0000000..e9c5bdd
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -0,0 +1,952 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.apf;
+
+import static android.system.OsConstants.*;
+
+import com.android.frameworks.servicestests.R;
+
+import android.net.apf.ApfCapabilities;
+import android.net.apf.ApfFilter;
+import android.net.apf.ApfGenerator;
+import android.net.apf.ApfGenerator.IllegalInstructionException;
+import android.net.apf.ApfGenerator.Register;
+import android.net.ip.IpManager;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.os.ConditionVariable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.nio.ByteBuffer;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+/**
+ * Tests for APF program generator and interpreter.
+ *
+ * Build, install and run with:
+ *  runtest frameworks-services -c com.android.server.ApfTest
+ */
+public class ApfTest extends AndroidTestCase {
+    private static final int TIMEOUT_MS = 500;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        // Load up native shared library containing APF interpreter exposed via JNI.
+        System.loadLibrary("servicestestsjni");
+    }
+
+    // Expected return codes from APF interpreter.
+    private final static int PASS = 1;
+    private final static int DROP = 0;
+    // Interpreter will just accept packets without link layer headers, so pad fake packet to at
+    // least the minimum packet size.
+    private final static int MIN_PKT_SIZE = 15;
+
+    private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
+        assertEquals(expected, apfSimulate(program, packet, filterAge));
+    }
+
+    private void assertPass(byte[] program, byte[] packet, int filterAge) {
+        assertVerdict(PASS, program, packet, filterAge);
+    }
+
+    private void assertDrop(byte[] program, byte[] packet, int filterAge) {
+        assertVerdict(DROP, program, packet, filterAge);
+    }
+
+    private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge)
+            throws IllegalInstructionException {
+        assertEquals(expected, apfSimulate(gen.generate(), packet, filterAge));
+    }
+
+    private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
+            throws IllegalInstructionException {
+        assertVerdict(PASS, gen, packet, filterAge);
+    }
+
+    private void assertDrop(ApfGenerator gen, byte[] packet, int filterAge)
+            throws IllegalInstructionException {
+        assertVerdict(DROP, gen, packet, filterAge);
+    }
+
+    private void assertPass(ApfGenerator gen)
+            throws IllegalInstructionException {
+        assertVerdict(PASS, gen, new byte[MIN_PKT_SIZE], 0);
+    }
+
+    private void assertDrop(ApfGenerator gen)
+            throws IllegalInstructionException {
+        assertVerdict(DROP, gen, new byte[MIN_PKT_SIZE], 0);
+    }
+
+    /**
+     * Test each instruction by generating a program containing the instruction,
+     * generating bytecode for that program and running it through the
+     * interpreter to verify it functions correctly.
+     */
+    @LargeTest
+    public void testApfInstructions() throws IllegalInstructionException {
+        // Empty program should pass because having the program counter reach the
+        // location immediately after the program indicates the packet should be
+        // passed to the AP.
+        ApfGenerator gen = new ApfGenerator();
+        assertPass(gen);
+
+        // Test jumping to pass label.
+        gen = new ApfGenerator();
+        gen.addJump(gen.PASS_LABEL);
+        byte[] program = gen.generate();
+        assertEquals(1, program.length);
+        assertEquals((14 << 3) | (0 << 1) | 0, program[0]);
+        assertPass(program, new byte[MIN_PKT_SIZE], 0);
+
+        // Test jumping to drop label.
+        gen = new ApfGenerator();
+        gen.addJump(gen.DROP_LABEL);
+        program = gen.generate();
+        assertEquals(2, program.length);
+        assertEquals((14 << 3) | (1 << 1) | 0, program[0]);
+        assertEquals(1, program[1]);
+        assertDrop(program, new byte[15], 15);
+
+        // Test jumping if equal to 0.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if not equal to 0.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if registers equal.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0EqualsR1(gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if registers not equal.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test load immediate.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test add.
+        gen = new ApfGenerator();
+        gen.addAdd(1234567890);
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test subtract.
+        gen = new ApfGenerator();
+        gen.addAdd(-1234567890);
+        gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test or.
+        gen = new ApfGenerator();
+        gen.addOr(1234567890);
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test and.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addAnd(123456789);
+        gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test left shift.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLeftShift(1);
+        gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test right shift.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addRightShift(1);
+        gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test multiply.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addMul(2);
+        gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test divide.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addDiv(2);
+        gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test divide by zero.
+        gen = new ApfGenerator();
+        gen.addDiv(0);
+        gen.addJump(gen.DROP_LABEL);
+        assertPass(gen);
+
+        // Test add.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1234567890);
+        gen.addAddR1();
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test subtract.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, -1234567890);
+        gen.addAddR1();
+        gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test or.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1234567890);
+        gen.addOrR1();
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test and.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLoadImmediate(Register.R1, 123456789);
+        gen.addAndR1();
+        gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test left shift.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLoadImmediate(Register.R1, 1);
+        gen.addLeftShiftR1();
+        gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test right shift.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLoadImmediate(Register.R1, -1);
+        gen.addLeftShiftR1();
+        gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test multiply.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLoadImmediate(Register.R1, 2);
+        gen.addMulR1();
+        gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test divide.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLoadImmediate(Register.R1, 2);
+        gen.addDivR1();
+        gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test divide by zero.
+        gen = new ApfGenerator();
+        gen.addDivR1();
+        gen.addJump(gen.DROP_LABEL);
+        assertPass(gen);
+
+        // Test byte load.
+        gen = new ApfGenerator();
+        gen.addLoad8(Register.R0, 1);
+        gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
+        assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test out of bounds load.
+        gen = new ApfGenerator();
+        gen.addLoad8(Register.R0, 16);
+        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+        assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test half-word load.
+        gen = new ApfGenerator();
+        gen.addLoad16(Register.R0, 1);
+        gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
+        assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test word load.
+        gen = new ApfGenerator();
+        gen.addLoad32(Register.R0, 1);
+        gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
+        assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test byte indexed load.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1);
+        gen.addLoad8Indexed(Register.R0, 0);
+        gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
+        assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test out of bounds indexed load.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 8);
+        gen.addLoad8Indexed(Register.R0, 8);
+        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+        assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test half-word indexed load.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1);
+        gen.addLoad16Indexed(Register.R0, 0);
+        gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
+        assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test word indexed load.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1);
+        gen.addLoad32Indexed(Register.R0, 0);
+        gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
+        assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
+
+        // Test jumping if greater than.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if less than.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0LessThan(0, gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addJumpIfR0LessThan(1, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if any bits set.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
+        assertDrop(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 3);
+        gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if register greater than.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 2);
+        gen.addLoadImmediate(Register.R1, 1);
+        gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if register less than.
+        gen = new ApfGenerator();
+        gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1);
+        gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jumping if any bits set in register.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 3);
+        gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
+        assertPass(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 3);
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
+        assertDrop(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 3);
+        gen.addLoadImmediate(Register.R0, 3);
+        gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test load from memory.
+        gen = new ApfGenerator();
+        gen.addLoadFromMemory(Register.R0, 0);
+        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test store to memory.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1234567890);
+        gen.addStoreToMemory(Register.R1, 12);
+        gen.addLoadFromMemory(Register.R0, 12);
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test filter age pre-filled memory.
+        gen = new ApfGenerator();
+        gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890);
+
+        // Test packet size pre-filled memory.
+        gen = new ApfGenerator();
+        gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
+        gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test IPv4 header size pre-filled memory.
+        gen = new ApfGenerator();
+        gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+        gen.addJumpIfR0Equals(20, gen.DROP_LABEL);
+        assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0);
+
+        // Test not.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addNot(Register.R0);
+        gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test negate.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addNeg(Register.R0);
+        gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test move.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1234567890);
+        gen.addMove(Register.R0);
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addMove(Register.R1);
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test swap.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R1, 1234567890);
+        gen.addSwap();
+        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
+        assertDrop(gen);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addSwap();
+        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
+        assertDrop(gen);
+
+        // Test jump if bytes not equal.
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
+        program = gen.generate();
+        assertEquals(6, program.length);
+        assertEquals((13 << 3) | (1 << 1) | 0, program[0]);
+        assertEquals(1, program[1]);
+        assertEquals(((20 << 3) | (1 << 1) | 0) - 256, program[2]);
+        assertEquals(1, program[3]);
+        assertEquals(1, program[4]);
+        assertEquals(123, program[5]);
+        assertDrop(program, new byte[MIN_PKT_SIZE], 0);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
+        byte[] packet123 = new byte[]{0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
+        assertPass(gen, packet123, 0);
+        gen = new ApfGenerator();
+        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
+        assertDrop(gen, packet123, 0);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL);
+        byte[] packet12345 = new byte[]{0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
+        assertDrop(gen, packet12345, 0);
+        gen = new ApfGenerator();
+        gen.addLoadImmediate(Register.R0, 1);
+        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL);
+        assertPass(gen, packet12345, 0);
+    }
+
+    /**
+     * Generate some BPF programs, translate them to APF, then run APF and BPF programs
+     * over packet traces and verify both programs filter out the same packets.
+     */
+    @LargeTest
+    public void testApfAgainstBpf() throws Exception {
+        String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53",
+                "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24",
+                "arp or icmp6 or portrange 53-54", "portrange 53-54 or portrange 100-50000",
+                "tcp[tcpflags] & (tcp-ack|tcp-fin) != 0 and (ip[2:2] > 57 or icmp)" };
+        String pcap_filename = stageFile(R.raw.apf);
+        for (String tcpdump_filter : tcpdump_filters) {
+            byte[] apf_program = Bpf2Apf.convert(compileToBpf(tcpdump_filter));
+            assertTrue("Failed to match for filter: " + tcpdump_filter,
+                    compareBpfApf(tcpdump_filter, pcap_filename, apf_program));
+        }
+    }
+
+    private class MockIpManagerCallback extends IpManager.Callback {
+        private final ConditionVariable mGotApfProgram = new ConditionVariable();
+        private byte[] mLastApfProgram;
+
+        @Override
+        public void installPacketFilter(byte[] filter) {
+            mLastApfProgram = filter;
+            mGotApfProgram.open();
+        }
+
+        public void resetApfProgramWait() {
+            mGotApfProgram.close();
+        }
+
+        public byte[] getApfProgram() {
+            assertTrue(mGotApfProgram.block(TIMEOUT_MS));
+            return mLastApfProgram;
+        }
+    }
+
+    private static class TestApfFilter extends ApfFilter {
+        public final static byte[] MOCK_MAC_ADDR = new byte[]{1,2,3,4,5,6};
+        private FileDescriptor mWriteSocket;
+
+        public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter) throws
+                Exception {
+            super(new ApfCapabilities(2, 1000, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
+                    ipManagerCallback, multicastFilter);
+        }
+
+        // Pretend an RA packet has been received and show it to ApfFilter.
+        public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException {
+            // ApfFilter's ReceiveThread will be waiting to read this.
+            Os.write(mWriteSocket, packet, 0, packet.length);
+        }
+
+        @Override
+        void maybeStartFilter() {
+            mHardwareAddress = MOCK_MAC_ADDR;
+            installNewProgramLocked();
+
+            // Create two sockets, "readSocket" and "mWriteSocket" and connect them together.
+            FileDescriptor readSocket = new FileDescriptor();
+            mWriteSocket = new FileDescriptor();
+            try {
+                Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket);
+            } catch (ErrnoException e) {
+                fail();
+                return;
+            }
+            // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs.
+            // This allows us to pretend RA packets have been recieved via pretendPacketReceived().
+            mReceiveThread = new ReceiveThread(readSocket);
+            mReceiveThread.start();
+        }
+
+        @Override
+        public void shutdown() {
+            super.shutdown();
+            IoUtils.closeQuietly(mWriteSocket);
+        }
+    }
+
+    private static final int ETH_HEADER_LEN = 14;
+    private static final int ETH_ETHERTYPE_OFFSET = 12;
+    private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+
+    private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
+    private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+    private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+
+    private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
+    private static final int IPV6_HEADER_LEN = 40;
+    private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+    // The IPv6 all nodes address ff02::1
+    private static final byte[] IPV6_ALL_NODES_ADDRESS =
+            new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+
+    private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+    private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
+    private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
+
+    private static final int ICMP6_RA_HEADER_LEN = 16;
+    private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
+            ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
+    private static final int ICMP6_RA_CHECKSUM_OFFSET =
+            ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
+    private static final int ICMP6_RA_OPTION_OFFSET =
+            ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
+
+    private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
+    private static final int ICMP6_PREFIX_OPTION_LEN = 32;
+    private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
+    private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
+
+    // From RFC6106: Recursive DNS Server option
+    private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
+    // From RFC6106: DNS Search List option
+    private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
+
+    // From RFC4191: Route Information option
+    private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
+    // Above three options all have the same format:
+    private static final int ICMP6_4_BYTE_OPTION_LEN = 8;
+    private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
+    private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
+
+    private static final int UDP_HEADER_LEN = 8;
+    private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 22;
+
+    private static final int DHCP_CLIENT_PORT = 68;
+    private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
+
+    private static int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
+    private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{
+            0, 1, // Hardware type: Ethernet (1)
+            8, 0, // Protocol type: IP (0x0800)
+            6,    // Hardware size: 6
+            4,    // Protocol size: 4
+            0, 1  // Opcode: request (1)
+    };
+    private static int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+
+    private static byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1};
+
+    @LargeTest
+    public void testApfFilterIPv4() throws Exception {
+        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+        byte[] program = ipManagerCallback.getApfProgram();
+
+        // Verify empty packet of 100 zero bytes is passed
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        assertPass(program, packet.array(), 0);
+
+        // Verify unicast IPv4 packet is passed
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+        assertPass(program, packet.array(), 0);
+
+        // Verify broadcast IPv4, not DHCP to us, is dropped
+        packet.put(ETH_BROADCAST_MAC_ADDRESS);
+        assertDrop(program, packet.array(), 0);
+        packet.put(IPV4_VERSION_IHL_OFFSET, (byte)0x45);
+        assertDrop(program, packet.array(), 0);
+        packet.put(IPV4_PROTOCOL_OFFSET, (byte)IPPROTO_UDP);
+        assertDrop(program, packet.array(), 0);
+        packet.putShort(UDP_DESTINATION_PORT_OFFSET, (short)DHCP_CLIENT_PORT);
+        assertDrop(program, packet.array(), 0);
+
+        // Verify broadcast IPv4 DHCP to us is passed
+        packet.position(DHCP_CLIENT_MAC_OFFSET);
+        packet.put(TestApfFilter.MOCK_MAC_ADDR);
+        assertPass(program, packet.array(), 0);
+
+        apfFilter.shutdown();
+    }
+
+    @LargeTest
+    public void testApfFilterIPv6() throws Exception {
+        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+        byte[] program = ipManagerCallback.getApfProgram();
+
+        // Verify empty IPv6 packet is passed
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+        assertPass(program, packet.array(), 0);
+
+        // Verify empty ICMPv6 packet is passed
+        packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+        assertPass(program, packet.array(), 0);
+
+        // Verify empty ICMPv6 NA packet is passed
+        packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_NEIGHBOR_ANNOUNCEMENT);
+        assertPass(program, packet.array(), 0);
+
+        // Verify ICMPv6 NA to ff02::1 is dropped
+        packet.position(IPV6_DEST_ADDR_OFFSET);
+        packet.put(IPV6_ALL_NODES_ADDRESS);
+        assertDrop(program, packet.array(), 0);
+
+        apfFilter.shutdown();
+    }
+
+    @LargeTest
+    public void testApfFilterIPv4Multicast() throws Exception {
+        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+        byte[] program = ipManagerCallback.getApfProgram();
+
+        // Verify initially disabled multicast filter is off
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+        packet.position(IPV4_DEST_ADDR_OFFSET);
+        packet.put(new byte[]{(byte)224,0,0,1});
+        assertPass(program, packet.array(), 0);
+
+        // Turn on multicast filter and verify it works
+        ipManagerCallback.resetApfProgramWait();
+        apfFilter.setMulticastFilter(true);
+        program = ipManagerCallback.getApfProgram();
+        assertDrop(program, packet.array(), 0);
+
+        // Turn off multicast filter and verify it's off
+        ipManagerCallback.resetApfProgramWait();
+        apfFilter.setMulticastFilter(false);
+        program = ipManagerCallback.getApfProgram();
+        assertPass(program, packet.array(), 0);
+
+        // Verify it can be initialized to on
+        ipManagerCallback.resetApfProgramWait();
+        apfFilter.shutdown();
+        apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
+        program = ipManagerCallback.getApfProgram();
+        assertDrop(program, packet.array(), 0);
+
+        apfFilter.shutdown();
+    }
+
+    private void verifyArpFilter(MockIpManagerCallback ipManagerCallback, ApfFilter apfFilter,
+            LinkProperties linkProperties, int filterResult) {
+        ipManagerCallback.resetApfProgramWait();
+        apfFilter.setLinkProperties(linkProperties);
+        byte[] program = ipManagerCallback.getApfProgram();
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
+        assertPass(program, packet.array(), 0);
+        packet.position(ARP_HEADER_OFFSET);
+        packet.put(ARP_IPV4_REQUEST_HEADER);
+        assertVerdict(filterResult, program, packet.array(), 0);
+        packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
+        packet.put(MOCK_IPV4_ADDR);
+        assertPass(program, packet.array(), 0);
+    }
+
+    @LargeTest
+    public void testApfFilterArp() throws Exception {
+        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+        byte[] program = ipManagerCallback.getApfProgram();
+
+        // Verify initially ARP filter is off
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
+        assertPass(program, packet.array(), 0);
+        packet.position(ARP_HEADER_OFFSET);
+        packet.put(ARP_IPV4_REQUEST_HEADER);
+        assertPass(program, packet.array(), 0);
+        packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
+        packet.put(MOCK_IPV4_ADDR);
+        assertPass(program, packet.array(), 0);
+
+        // Inform ApfFilter of our address and verify ARP filtering is on
+        LinkProperties lp = new LinkProperties();
+        assertTrue(lp.addLinkAddress(
+                new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24)));
+        verifyArpFilter(ipManagerCallback, apfFilter, lp, DROP);
+
+        // Inform ApfFilter of loss of IP and verify ARP filtering is off
+        verifyArpFilter(ipManagerCallback, apfFilter, new LinkProperties(), PASS);
+
+        apfFilter.shutdown();
+    }
+
+    // Verify that the last program pushed to the IpManager.Callback properly filters the
+    // given packet for the given lifetime.
+    private void verifyRaLifetime(MockIpManagerCallback ipManagerCallback, ByteBuffer packet,
+            int lifetime) {
+        byte[] program = ipManagerCallback.getApfProgram();
+
+        // Verify new program should drop RA for 1/6th its lifetime
+        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array(), lifetime/6);
+        assertPass(program, packet.array(), lifetime/6 + 1);
+        assertPass(program, packet.array(), lifetime);
+
+        // Verify RA checksum is ignored
+        packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345);
+        assertDrop(program, packet.array(), 0);
+        packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345);
+        assertDrop(program, packet.array(), 0);
+
+        // Verify other changes to RA make it not match filter
+        packet.put(0, (byte)-1);
+        assertPass(program, packet.array(), 0);
+        packet.put(0, (byte)0);
+        assertDrop(program, packet.array(), 0);
+    }
+
+    // Test that when ApfFilter is shown the given packet, it generates a program to filter it
+    // for the given lifetime.
+    private void testRaLifetime(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+            ByteBuffer packet, int lifetime) throws IOException, ErrnoException {
+        // Verify new program generated if ApfFilter witnesses RA
+        ipManagerCallback.resetApfProgramWait();
+        apfFilter.pretendPacketReceived(packet.array());
+        ipManagerCallback.getApfProgram();
+
+        verifyRaLifetime(ipManagerCallback, packet, lifetime);
+    }
+
+    @LargeTest
+    public void testApfFilterRa() throws Exception {
+        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+        byte[] program = ipManagerCallback.getApfProgram();
+
+        // Verify RA is passed the first time
+        ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
+        basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+        basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+        basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
+        basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000);
+        assertPass(program, basePacket.array(), 0);
+
+        testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
+
+        // Generate several RAs with different options and lifetimes, and verify when
+        // ApfFilter is shown these packets, it generates programs to filter them for the
+        // appropriate lifetime.
+        ByteBuffer prefixOptionPacket = ByteBuffer.wrap(
+                new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_LEN]);
+        basePacket.clear();
+        prefixOptionPacket.put(basePacket);
+        prefixOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
+        prefixOptionPacket.put((byte)(ICMP6_PREFIX_OPTION_LEN / 8));
+        prefixOptionPacket.putInt(
+                ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, 100);
+        prefixOptionPacket.putInt(
+                ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, 200);
+        testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, 100);
+
+        ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
+                new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
+        basePacket.clear();
+        rdnssOptionPacket.put(basePacket);
+        rdnssOptionPacket.put((byte)ICMP6_RDNSS_OPTION_TYPE);
+        rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
+        rdnssOptionPacket.putInt(
+                ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 300);
+        testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, 300);
+
+        ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
+                new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
+        basePacket.clear();
+        routeInfoOptionPacket.put(basePacket);
+        routeInfoOptionPacket.put((byte)ICMP6_ROUTE_INFO_OPTION_TYPE);
+        routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
+        routeInfoOptionPacket.putInt(
+                ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 400);
+        testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, 400);
+
+        ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
+                new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
+        basePacket.clear();
+        dnsslOptionPacket.put(basePacket);
+        dnsslOptionPacket.put((byte)ICMP6_DNSSL_OPTION_TYPE);
+        dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
+        dnsslOptionPacket.putInt(
+                ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 2000);
+        // Note that lifetime of 2000 will be ignored in favor of shorter
+        // route lifetime of 1000.
+        testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, 1000);
+
+        // Verify that current program filters all five RAs:
+        verifyRaLifetime(ipManagerCallback, basePacket, 1000);
+        verifyRaLifetime(ipManagerCallback, prefixOptionPacket, 100);
+        verifyRaLifetime(ipManagerCallback, rdnssOptionPacket, 300);
+        verifyRaLifetime(ipManagerCallback, routeInfoOptionPacket, 400);
+        verifyRaLifetime(ipManagerCallback, dnsslOptionPacket, 1000);
+
+        apfFilter.shutdown();
+    }
+
+    /**
+     * Stage a file for testing, i.e. make it native accessible. Given a resource ID,
+     * copy that resource into the app's data directory and return the path to it.
+     */
+    private String stageFile(int rawId) throws Exception {
+        File file = new File(getContext().getFilesDir(), "staged_file");
+        new File(file.getParent()).mkdirs();
+        InputStream in = null;
+        OutputStream out = null;
+        try {
+            in = getContext().getResources().openRawResource(rawId);
+            out = new FileOutputStream(file);
+            Streams.copy(in, out);
+        } finally {
+            if (in != null) in.close();
+            if (out != null) out.close();
+        }
+        return file.getAbsolutePath();
+    }
+
+    /**
+     * Call the APF interpreter the run {@code program} on {@code packet} pretending the
+     * filter was installed {@code filter_age} seconds ago.
+     */
+    private native static int apfSimulate(byte[] program, byte[] packet, int filter_age);
+
+    /**
+     * Compile a tcpdump human-readable filter (e.g. "icmp" or "tcp port 54") into a BPF
+     * prorgam and return a human-readable dump of the BPF program identical to "tcpdump -d".
+     */
+    private native static String compileToBpf(String filter);
+
+    /**
+     * Open packet capture file {@code pcap_filename} and filter the packets using tcpdump
+     * human-readable filter (e.g. "icmp" or "tcp port 54") compiled to a BPF program and
+     * at the same time using APF program {@code apf_program}.  Return {@code true} if
+     * both APF and BPF programs filter out exactly the same packets.
+     */
+    private native static boolean compareBpfApf(String filter, String pcap_filename,
+            byte[] apf_program);
+}
diff --git a/services/tests/servicestests/src/com/android/server/Bpf2Apf.java b/services/tests/servicestests/src/android/net/apf/Bpf2Apf.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/Bpf2Apf.java
rename to services/tests/servicestests/src/android/net/apf/Bpf2Apf.java
index 29594a8..220e54d 100644
--- a/services/tests/servicestests/src/com/android/server/Bpf2Apf.java
+++ b/services/tests/servicestests/src/android/net/apf/Bpf2Apf.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package android.net.apf;
 
 import android.net.apf.ApfGenerator;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
@@ -31,9 +31,9 @@
  *
  * Example usage:
  *   javac net/java/android/net/apf/ApfGenerator.java \
- *         tests/servicestests/src/com/android/server/Bpf2Apf.java
+ *         tests/servicestests/src/android/net/apf/Bpf2Apf.java
  *   sudo tcpdump -i em1 -d icmp | java -classpath tests/servicestests/src:net/java \
- *                                      com.android.server.Bpf2Apf
+ *                                      android.net.apf.Bpf2Apf
  */
 public class Bpf2Apf {
     private static int parseImm(String line, String arg) {
diff --git a/services/tests/servicestests/src/com/android/server/ApfTest.java b/services/tests/servicestests/src/com/android/server/ApfTest.java
deleted file mode 100644
index 9ba27cb..0000000
--- a/services/tests/servicestests/src/com/android/server/ApfTest.java
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import com.android.frameworks.servicestests.R;
-import android.net.apf.ApfGenerator;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-/**
- * Tests for APF program generator and interpreter.
- *
- * Build, install and run with:
- *  runtest frameworks-services -c com.android.server.ApfTest
- */
-public class ApfTest extends AndroidTestCase {
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        // Load up native shared library containing APF interpreter exposed via JNI.
-        System.loadLibrary("servicestestsjni");
-    }
-
-    // Expected return codes from APF interpreter.
-    private final static int PASS = 1;
-    private final static int DROP = 0;
-    // Interpreter will just accept packets without link layer headers, so pad fake packet to at
-    // least the minimum packet size.
-    private final static int MIN_PKT_SIZE = 15;
-
-    private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
-        assertEquals(expected, apfSimulate(program, packet, filterAge));
-    }
-
-    private void assertPass(byte[] program, byte[] packet, int filterAge) {
-        assertVerdict(PASS, program, packet, filterAge);
-    }
-
-    private void assertDrop(byte[] program, byte[] packet, int filterAge) {
-        assertVerdict(DROP, program, packet, filterAge);
-    }
-
-    private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge)
-            throws IllegalInstructionException {
-        assertEquals(expected, apfSimulate(gen.generate(), packet, filterAge));
-    }
-
-    private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
-            throws IllegalInstructionException {
-        assertVerdict(PASS, gen, packet, filterAge);
-    }
-
-    private void assertDrop(ApfGenerator gen, byte[] packet, int filterAge)
-            throws IllegalInstructionException {
-        assertVerdict(DROP, gen, packet, filterAge);
-    }
-
-    private void assertPass(ApfGenerator gen)
-            throws IllegalInstructionException {
-        assertVerdict(PASS, gen, new byte[MIN_PKT_SIZE], 0);
-    }
-
-    private void assertDrop(ApfGenerator gen)
-            throws IllegalInstructionException {
-        assertVerdict(DROP, gen, new byte[MIN_PKT_SIZE], 0);
-    }
-
-    /**
-     * Test each instruction by generating a program containing the instruction,
-     * generating bytecode for that program and running it through the
-     * interpreter to verify it functions correctly.
-     */
-    @LargeTest
-    public void testApfInstructions() throws IllegalInstructionException {
-        // Empty program should pass because having the program counter reach the
-        // location immediately after the program indicates the packet should be
-        // passed to the AP.
-        ApfGenerator gen = new ApfGenerator();
-        assertPass(gen);
-
-        // Test jumping to pass label.
-        gen = new ApfGenerator();
-        gen.addJump(gen.PASS_LABEL);
-        byte[] program = gen.generate();
-        assertEquals(1, program.length);
-        assertEquals((14 << 3) | (0 << 1) | 0, program[0]);
-        assertPass(program, new byte[MIN_PKT_SIZE], 0);
-
-        // Test jumping to drop label.
-        gen = new ApfGenerator();
-        gen.addJump(gen.DROP_LABEL);
-        program = gen.generate();
-        assertEquals(2, program.length);
-        assertEquals((14 << 3) | (1 << 1) | 0, program[0]);
-        assertEquals(1, program[1]);
-        assertDrop(program, new byte[15], 15);
-
-        // Test jumping if equal to 0.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if not equal to 0.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if registers equal.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0EqualsR1(gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if registers not equal.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test load immediate.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test add.
-        gen = new ApfGenerator();
-        gen.addAdd(1234567890);
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test subtract.
-        gen = new ApfGenerator();
-        gen.addAdd(-1234567890);
-        gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test or.
-        gen = new ApfGenerator();
-        gen.addOr(1234567890);
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test and.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addAnd(123456789);
-        gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test left shift.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addLeftShift(1);
-        gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test right shift.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addRightShift(1);
-        gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test multiply.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addMul(2);
-        gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test divide.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addDiv(2);
-        gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test divide by zero.
-        gen = new ApfGenerator();
-        gen.addDiv(0);
-        gen.addJump(gen.DROP_LABEL);
-        assertPass(gen);
-
-        // Test add.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1234567890);
-        gen.addAddR1();
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test subtract.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, -1234567890);
-        gen.addAddR1();
-        gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test or.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1234567890);
-        gen.addOrR1();
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test and.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addLoadImmediate(Register.R1, 123456789);
-        gen.addAndR1();
-        gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test left shift.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addLoadImmediate(Register.R1, 1);
-        gen.addLeftShiftR1();
-        gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test right shift.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addLoadImmediate(Register.R1, -1);
-        gen.addLeftShiftR1();
-        gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test multiply.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addLoadImmediate(Register.R1, 2);
-        gen.addMulR1();
-        gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test divide.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addLoadImmediate(Register.R1, 2);
-        gen.addDivR1();
-        gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test divide by zero.
-        gen = new ApfGenerator();
-        gen.addDivR1();
-        gen.addJump(gen.DROP_LABEL);
-        assertPass(gen);
-
-        // Test byte load.
-        gen = new ApfGenerator();
-        gen.addLoad8(Register.R0, 1);
-        gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
-        assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test out of bounds load.
-        gen = new ApfGenerator();
-        gen.addLoad8(Register.R0, 16);
-        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
-        assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test half-word load.
-        gen = new ApfGenerator();
-        gen.addLoad16(Register.R0, 1);
-        gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
-        assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test word load.
-        gen = new ApfGenerator();
-        gen.addLoad32(Register.R0, 1);
-        gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
-        assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test byte indexed load.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1);
-        gen.addLoad8Indexed(Register.R0, 0);
-        gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
-        assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test out of bounds indexed load.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 8);
-        gen.addLoad8Indexed(Register.R0, 8);
-        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
-        assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test half-word indexed load.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1);
-        gen.addLoad16Indexed(Register.R0, 0);
-        gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
-        assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test word indexed load.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1);
-        gen.addLoad32Indexed(Register.R0, 0);
-        gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
-        assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
-
-        // Test jumping if greater than.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if less than.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0LessThan(0, gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addJumpIfR0LessThan(1, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if any bits set.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
-        assertDrop(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 3);
-        gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if register greater than.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 2);
-        gen.addLoadImmediate(Register.R1, 1);
-        gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if register less than.
-        gen = new ApfGenerator();
-        gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1);
-        gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jumping if any bits set in register.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 3);
-        gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
-        assertPass(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 3);
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
-        assertDrop(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 3);
-        gen.addLoadImmediate(Register.R0, 3);
-        gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test load from memory.
-        gen = new ApfGenerator();
-        gen.addLoadFromMemory(Register.R0, 0);
-        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test store to memory.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1234567890);
-        gen.addStoreToMemory(Register.R1, 12);
-        gen.addLoadFromMemory(Register.R0, 12);
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test filter age pre-filled memory.
-        gen = new ApfGenerator();
-        gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890);
-
-        // Test packet size pre-filled memory.
-        gen = new ApfGenerator();
-        gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
-        gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test IPv4 header size pre-filled memory.
-        gen = new ApfGenerator();
-        gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
-        gen.addJumpIfR0Equals(20, gen.DROP_LABEL);
-        assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0);
-
-        // Test not.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addNot(Register.R0);
-        gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test negate.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addNeg(Register.R0);
-        gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test move.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1234567890);
-        gen.addMove(Register.R0);
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addMove(Register.R1);
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test swap.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R1, 1234567890);
-        gen.addSwap();
-        gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
-        assertDrop(gen);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1234567890);
-        gen.addSwap();
-        gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
-        assertDrop(gen);
-
-        // Test jump if bytes not equal.
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
-        program = gen.generate();
-        assertEquals(6, program.length);
-        assertEquals((13 << 3) | (1 << 1) | 0, program[0]);
-        assertEquals(1, program[1]);
-        assertEquals(((20 << 3) | (1 << 1) | 0) - 256, program[2]);
-        assertEquals(1, program[3]);
-        assertEquals(1, program[4]);
-        assertEquals(123, program[5]);
-        assertDrop(program, new byte[MIN_PKT_SIZE], 0);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
-        byte[] packet123 = new byte[]{0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
-        assertPass(gen, packet123, 0);
-        gen = new ApfGenerator();
-        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
-        assertDrop(gen, packet123, 0);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL);
-        byte[] packet12345 = new byte[]{0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
-        assertDrop(gen, packet12345, 0);
-        gen = new ApfGenerator();
-        gen.addLoadImmediate(Register.R0, 1);
-        gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL);
-        assertPass(gen, packet12345, 0);
-    }
-
-    /**
-     * Generate some BPF programs, translate them to APF, then run APF and BPF programs
-     * over packet traces and verify both programs filter out the same packets.
-     */
-    @LargeTest
-    public void testApfAgainstBpf() throws Exception {
-        String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53",
-                "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24",
-                "arp or icmp6 or portrange 53-54", "portrange 53-54 or portrange 100-50000",
-                "tcp[tcpflags] & (tcp-ack|tcp-fin) != 0 and (ip[2:2] > 57 or icmp)" };
-        String pcap_filename = stageFile(R.raw.apf);
-        for (String tcpdump_filter : tcpdump_filters) {
-            byte[] apf_program = Bpf2Apf.convert(compileToBpf(tcpdump_filter));
-            assertTrue("Failed to match for filter: " + tcpdump_filter,
-                    compareBpfApf(tcpdump_filter, pcap_filename, apf_program));
-        }
-    }
-
-    /**
-     * Stage a file for testing, i.e. make it native accessible. Given a resource ID,
-     * copy that resource into the app's data directory and return the path to it.
-     */
-    private String stageFile(int rawId) throws Exception {
-        File file = new File(getContext().getFilesDir(), "staged_file");
-        new File(file.getParent()).mkdirs();
-        InputStream in = null;
-        OutputStream out = null;
-        try {
-            in = getContext().getResources().openRawResource(rawId);
-            out = new FileOutputStream(file);
-            Streams.copy(in, out);
-        } finally {
-            if (in != null) in.close();
-            if (out != null) out.close();
-        }
-        return file.getAbsolutePath();
-    }
-
-    /**
-     * Call the APF interpreter the run {@code program} on {@code packet} pretending the
-     * filter was installed {@code filter_age} seconds ago.
-     */
-    private native static int apfSimulate(byte[] program, byte[] packet, int filter_age);
-
-    /**
-     * Compile a tcpdump human-readable filter (e.g. "icmp" or "tcp port 54") into a BPF
-     * prorgam and return a human-readable dump of the BPF program identical to "tcpdump -d".
-     */
-    private native static String compileToBpf(String filter);
-
-    /**
-     * Open packet capture file {@code pcap_filename} and filter the packets using tcpdump
-     * human-readable filter (e.g. "icmp" or "tcp port 54") compiled to a BPF program and
-     * at the same time using APF program {@code apf_program}.  Return {@code true} if
-     * both APF and BPF programs filter out exactly the same packets.
-     */
-    private native static boolean compareBpfApf(String filter, String pcap_filename,
-            byte[] apf_program);
-}