1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.net.apf;
17 
18 import static android.net.apf.ApfJniUtils.apfSimulate;
19 import static android.system.OsConstants.AF_UNIX;
20 import static android.system.OsConstants.SOCK_STREAM;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 import static org.mockito.Mockito.mock;
27 
28 import android.content.Context;
29 import android.net.LinkAddress;
30 import android.net.LinkProperties;
31 import android.net.apf.BaseApfGenerator.IllegalInstructionException;
32 import android.net.ip.IIpClientCallbacks;
33 import android.net.ip.IpClient;
34 import android.net.metrics.IpConnectivityLog;
35 import android.os.ConditionVariable;
36 import android.os.SystemClock;
37 import android.system.ErrnoException;
38 import android.system.Os;
39 import android.text.format.DateUtils;
40 
41 import com.android.internal.annotations.GuardedBy;
42 import com.android.internal.util.HexDump;
43 import com.android.net.module.util.InterfaceParams;
44 import com.android.net.module.util.SharedLog;
45 import com.android.networkstack.apishim.NetworkInformationShimImpl;
46 import com.android.networkstack.metrics.NetworkQuirkMetrics;
47 
48 import libcore.io.IoUtils;
49 
50 import java.io.FileDescriptor;
51 import java.io.IOException;
52 import java.net.InetAddress;
53 import java.util.Arrays;
54 
55 /**
56  * The util class for calling the APF interpreter and check the return value
57  */
58 public class ApfTestUtils {
59     public static final int TIMEOUT_MS = 500;
60     public static final int PASS = 1;
61     public static final int DROP = 0;
62     // Interpreter will just accept packets without link layer headers, so pad fake packet to at
63     // least the minimum packet size.
64     public static final int MIN_PKT_SIZE = 15;
65 
ApfTestUtils()66     private ApfTestUtils() {
67     }
68 
label(int code)69     private static String label(int code) {
70         switch (code) {
71             case PASS:
72                 return "PASS";
73             case DROP:
74                 return "DROP";
75             default:
76                 return "UNKNOWN";
77         }
78     }
79 
assertReturnCodesEqual(String msg, int expected, int got)80     private static void assertReturnCodesEqual(String msg, int expected, int got) {
81         assertEquals(msg, label(expected), label(got));
82     }
83 
assertReturnCodesEqual(int expected, int got)84     private static void assertReturnCodesEqual(int expected, int got) {
85         assertEquals(label(expected), label(got));
86     }
87 
assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet, int filterAge)88     private static void assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet,
89             int filterAge) {
90         final String msg = "Unexpected APF verdict. To debug:\n"
91                 + "  apf_run --program " + HexDump.toHexString(program)
92                 + " --packet " + HexDump.toHexString(packet)
93                 + " --age " + filterAge
94                 + (apfVersion > 4 ? " --v6" : "")
95                 + " --trace "  + " | less\n  ";
96         assertReturnCodesEqual(msg, expected,
97                 apfSimulate(apfVersion, program, packet, null, filterAge));
98     }
99 
100     /**
101      * Runs the APF program and checks the return code is equals to expected value. If not, the
102      * customized message is printed.
103      */
assertVerdict(int apfVersion, String msg, int expected, byte[] program, byte[] packet, int filterAge)104     public static void assertVerdict(int apfVersion, String msg, int expected, byte[] program,
105             byte[] packet, int filterAge) {
106         assertReturnCodesEqual(msg, expected,
107                 apfSimulate(apfVersion, program, packet, null, filterAge));
108     }
109 
110     /**
111      * Runs the APF program and checks the return code is equals to expected value.
112      */
assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet)113     public static void assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet) {
114         assertVerdict(apfVersion, expected, program, packet, 0);
115     }
116 
117     /**
118      * Runs the APF program and checks the return code is PASS.
119      */
assertPass(int apfVersion, byte[] program, byte[] packet, int filterAge)120     public static void assertPass(int apfVersion, byte[] program, byte[] packet, int filterAge) {
121         assertVerdict(apfVersion, PASS, program, packet, filterAge);
122     }
123 
124     /**
125      * Runs the APF program and checks the return code is PASS.
126      */
assertPass(int apfVersion, byte[] program, byte[] packet)127     public static void assertPass(int apfVersion, byte[] program, byte[] packet) {
128         assertVerdict(apfVersion, PASS, program, packet);
129     }
130 
131     /**
132      * Runs the APF program and checks the return code is DROP.
133      */
assertDrop(int apfVersion, byte[] program, byte[] packet, int filterAge)134     public static void assertDrop(int apfVersion, byte[] program, byte[] packet, int filterAge) {
135         assertVerdict(apfVersion, DROP, program, packet, filterAge);
136     }
137 
138     /**
139      * Runs the APF program and checks the return code is DROP.
140      */
assertDrop(int apfVersion, byte[] program, byte[] packet)141     public static void assertDrop(int apfVersion, byte[] program, byte[] packet) {
142         assertVerdict(apfVersion, DROP, program, packet);
143     }
144 
145     /**
146      * Checks the generated APF program equals to the expected value.
147      */
assertProgramEquals(byte[] expected, byte[] program)148     public static void assertProgramEquals(byte[] expected, byte[] program) throws AssertionError {
149         // assertArrayEquals() would only print one byte, making debugging difficult.
150         if (!Arrays.equals(expected, program)) {
151             throw new AssertionError("\nexpected: " + HexDump.toHexString(expected) + "\nactual:   "
152                     + HexDump.toHexString(program));
153         }
154     }
155 
156     /**
157      * Runs the APF program and checks the return code and data regions equals to expected value.
158      */
assertDataMemoryContents(int apfVersion, int expected, byte[] program, byte[] packet, byte[] data, byte[] expectedData, boolean ignoreInterpreterVersion)159     public static void assertDataMemoryContents(int apfVersion, int expected, byte[] program,
160             byte[] packet, byte[] data, byte[] expectedData, boolean ignoreInterpreterVersion)
161             throws ApfV4Generator.IllegalInstructionException, Exception {
162         assertReturnCodesEqual(expected,
163                 apfSimulate(apfVersion, program, packet, data, 0 /* filterAge */));
164 
165         if (ignoreInterpreterVersion) {
166             final int apfVersionIdx = ApfCounterTracker.Counter.totalSize()
167                     + ApfCounterTracker.Counter.APF_VERSION.offset();
168             final int apfProgramIdIdx = ApfCounterTracker.Counter.totalSize()
169                     + ApfCounterTracker.Counter.APF_PROGRAM_ID.offset();
170             for (int i = 0; i < 4; ++i) {
171                 data[apfVersionIdx + i] = 0;
172                 data[apfProgramIdIdx + i] = 0;
173             }
174         }
175         // assertArrayEquals() would only print one byte, making debugging difficult.
176         if (!Arrays.equals(expectedData, data)) {
177             throw new Exception("\nprogram:     " + HexDump.toHexString(program) + "\ndata memory: "
178                     + HexDump.toHexString(data) + "\nexpected:    " + HexDump.toHexString(
179                     expectedData));
180         }
181     }
182 
183     /**
184      * Runs the APF program with customized data region and checks the return code.
185      */
assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet, byte[] data)186     public static void assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet,
187             byte[] data) {
188         assertVerdict(apfVersion, expected, program, packet, data, 0 /* filterAge */);
189     }
190 
assertVerdict(int apfVersion, int expected, ApfV4Generator gen, byte[] packet, int filterAge)191     private static void assertVerdict(int apfVersion, int expected, ApfV4Generator gen,
192             byte[] packet, int filterAge) throws ApfV4Generator.IllegalInstructionException {
193         assertVerdict(apfVersion, expected, gen.generate(), packet, null, filterAge);
194     }
195 
assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet, byte[] data, int filterAge)196     private static void assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet,
197             byte[] data, int filterAge) {
198         final String msg = "Unexpected APF verdict. To debug:\n"
199                 + "  apf_run --program " + HexDump.toHexString(program)
200                 + " --packet " + HexDump.toHexString(packet)
201                 + (data != null ? " --data " + HexDump.toHexString(data) : "")
202                 + " --age " + filterAge
203                 + (apfVersion > 4 ? " --v6" : "")
204                 + " --trace "  + " | less\n  ";
205         assertReturnCodesEqual(msg, expected,
206                 apfSimulate(apfVersion, program, packet, data, filterAge));
207     }
208 
209     /**
210      * Runs the APF program and checks the return code is PASS.
211      */
assertPass(int apfVersion, ApfV4Generator gen, byte[] packet, int filterAge)212     public static void assertPass(int apfVersion, ApfV4Generator gen, byte[] packet, int filterAge)
213             throws ApfV4Generator.IllegalInstructionException {
214         assertVerdict(apfVersion, PASS, gen, packet, filterAge);
215     }
216 
217     /**
218      * Runs the APF program and checks the return code is DROP.
219      */
assertDrop(int apfVersion, ApfV4Generator gen, byte[] packet, int filterAge)220     public static void assertDrop(int apfVersion, ApfV4Generator gen, byte[] packet, int filterAge)
221             throws ApfV4Generator.IllegalInstructionException {
222         assertVerdict(apfVersion, DROP, gen, packet, filterAge);
223     }
224 
225     /**
226      * Runs the APF program and checks the return code is PASS.
227      */
assertPass(int apfVersion, ApfV4Generator gen)228     public static void assertPass(int apfVersion, ApfV4Generator gen)
229             throws ApfV4Generator.IllegalInstructionException {
230         assertVerdict(apfVersion, PASS, gen, new byte[MIN_PKT_SIZE], 0);
231     }
232 
233     /**
234      * Runs the APF program and checks the return code is DROP.
235      */
assertDrop(int apfVersion, ApfV4Generator gen)236     public static void assertDrop(int apfVersion, ApfV4Generator gen)
237             throws ApfV4Generator.IllegalInstructionException {
238         assertVerdict(apfVersion, DROP, gen, new byte[MIN_PKT_SIZE], 0);
239     }
240 
241     /**
242      * The Mock ip client callback class.
243      */
244     public static class MockIpClientCallback extends IpClient.IpClientCallbacksWrapper {
245         private final ConditionVariable mGotApfProgram = new ConditionVariable();
246         private byte[] mLastApfProgram;
247         private boolean mInstallPacketFilterReturn = true;
248 
MockIpClientCallback()249         MockIpClientCallback() {
250             super(mock(IIpClientCallbacks.class), mock(SharedLog.class), mock(SharedLog.class),
251                     NetworkInformationShimImpl.newInstance(), false);
252         }
253 
MockIpClientCallback(boolean installPacketFilterReturn)254         MockIpClientCallback(boolean installPacketFilterReturn) {
255             super(mock(IIpClientCallbacks.class), mock(SharedLog.class), mock(SharedLog.class),
256                     NetworkInformationShimImpl.newInstance(), false);
257             mInstallPacketFilterReturn = installPacketFilterReturn;
258         }
259 
260         @Override
installPacketFilter(byte[] filter)261         public boolean installPacketFilter(byte[] filter) {
262             mLastApfProgram = filter;
263             mGotApfProgram.open();
264             return mInstallPacketFilterReturn;
265         }
266 
267         /**
268          * Reset the apf program and wait for the next update.
269          */
resetApfProgramWait()270         public void resetApfProgramWait() {
271             mGotApfProgram.close();
272         }
273 
274         /**
275          * Assert the program is update within TIMEOUT_MS and return the program.
276          */
assertProgramUpdateAndGet()277         public byte[] assertProgramUpdateAndGet() {
278             assertTrue(mGotApfProgram.block(TIMEOUT_MS));
279             return mLastApfProgram;
280         }
281 
282         /**
283          * Assert the program is not update within TIMEOUT_MS.
284          */
assertNoProgramUpdate()285         public void assertNoProgramUpdate() {
286             assertFalse(mGotApfProgram.block(TIMEOUT_MS));
287         }
288     }
289 
290     /**
291      * The test apf filter class.
292      */
293     public static class TestApfFilter extends ApfFilter implements TestAndroidPacketFilter {
294         public static final byte[] MOCK_MAC_ADDR = {2, 3, 4, 5, 6, 7};
295         private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1};
296 
297         private FileDescriptor mWriteSocket;
298         private long mCurrentTimeMs = SystemClock.elapsedRealtime();
299         private final MockIpClientCallback mMockIpClientCb;
300         private final boolean mThrowsExceptionWhenGeneratesProgram;
301 
TestApfFilter(Context context, ApfConfiguration config, MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics, Dependencies dependencies)302         public TestApfFilter(Context context, ApfConfiguration config,
303                 MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics,
304                 Dependencies dependencies) throws Exception {
305             this(context, config, ipClientCallback, networkQuirkMetrics, dependencies,
306                     false /* throwsExceptionWhenGeneratesProgram */, new ApfFilter.Clock());
307         }
308 
TestApfFilter(Context context, ApfConfiguration config, MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics, Dependencies dependencies, boolean throwsExceptionWhenGeneratesProgram)309         public TestApfFilter(Context context, ApfConfiguration config,
310                 MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics,
311                 Dependencies dependencies, boolean throwsExceptionWhenGeneratesProgram)
312                 throws Exception {
313             this(context, config, ipClientCallback, networkQuirkMetrics, dependencies,
314                     throwsExceptionWhenGeneratesProgram, new ApfFilter.Clock());
315         }
316 
TestApfFilter(Context context, ApfConfiguration config, MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics, Dependencies dependencies, ApfFilter.Clock clock)317         public TestApfFilter(Context context, ApfConfiguration config,
318                 MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics,
319                 Dependencies dependencies, ApfFilter.Clock clock) throws Exception {
320             this(context, config, ipClientCallback, networkQuirkMetrics, dependencies,
321                     false /* throwsExceptionWhenGeneratesProgram */, clock);
322         }
323 
TestApfFilter(Context context, ApfConfiguration config, MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics, Dependencies dependencies, boolean throwsExceptionWhenGeneratesProgram, ApfFilter.Clock clock)324         public TestApfFilter(Context context, ApfConfiguration config,
325                 MockIpClientCallback ipClientCallback, NetworkQuirkMetrics networkQuirkMetrics,
326                 Dependencies dependencies, boolean throwsExceptionWhenGeneratesProgram,
327                 ApfFilter.Clock clock) throws Exception {
328             super(context, config, InterfaceParams.getByName("lo"), ipClientCallback,
329                     networkQuirkMetrics, dependencies, clock);
330             mMockIpClientCb = ipClientCallback;
331             mThrowsExceptionWhenGeneratesProgram = throwsExceptionWhenGeneratesProgram;
332         }
333 
334         /**
335          * Create a new test ApfFiler.
336          */
createTestApfFilter(Context context, MockIpClientCallback ipClientCallback, ApfConfiguration config, NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies)337         public static ApfFilter createTestApfFilter(Context context,
338                 MockIpClientCallback ipClientCallback, ApfConfiguration config,
339                 NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies)
340                 throws Exception {
341             LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
342             LinkProperties lp = new LinkProperties();
343             lp.addLinkAddress(link);
344             TestApfFilter apfFilter = new TestApfFilter(context, config, ipClientCallback,
345                     networkQuirkMetrics, dependencies);
346             apfFilter.setLinkProperties(lp);
347             return apfFilter;
348         }
349 
350         /**
351          * Pretend an RA packet has been received and show it to ApfFilter.
352          */
pretendPacketReceived(byte[] packet)353         public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException {
354             mMockIpClientCb.resetApfProgramWait();
355             // ApfFilter's ReceiveThread will be waiting to read this.
356             Os.write(mWriteSocket, packet, 0, packet.length);
357         }
358 
359         /**
360          * Simulate current time changes.
361          */
increaseCurrentTimeSeconds(int delta)362         public void increaseCurrentTimeSeconds(int delta) {
363             mCurrentTimeMs += delta * DateUtils.SECOND_IN_MILLIS;
364         }
365 
366         @Override
secondsSinceBoot()367         protected int secondsSinceBoot() {
368             return (int) (mCurrentTimeMs / DateUtils.SECOND_IN_MILLIS);
369         }
370 
371         @Override
maybeStartFilter()372         public synchronized void maybeStartFilter() {
373             mHardwareAddress = MOCK_MAC_ADDR;
374             installNewProgramLocked();
375 
376             // Create two sockets, "readSocket" and "mWriteSocket" and connect them together.
377             FileDescriptor readSocket = new FileDescriptor();
378             mWriteSocket = new FileDescriptor();
379             try {
380                 Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket);
381             } catch (ErrnoException e) {
382                 fail();
383                 return;
384             }
385             // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs.
386             // This allows us to pretend RA packets have been received via pretendPacketReceived().
387             mReceiveThread = new ReceiveThread(readSocket);
388             mReceiveThread.start();
389         }
390 
391         @Override
shutdown()392         public synchronized void shutdown() {
393             super.shutdown();
394             if (mReceiveThread != null) {
395                 mReceiveThread.halt();
396                 mReceiveThread = null;
397             }
398             IoUtils.closeQuietly(mWriteSocket);
399         }
400 
401         @Override
402         @GuardedBy("this")
emitPrologueLocked()403         protected ApfV4GeneratorBase<?> emitPrologueLocked() throws IllegalInstructionException {
404             if (mThrowsExceptionWhenGeneratesProgram) {
405                 throw new IllegalStateException();
406             }
407             return super.emitPrologueLocked();
408         }
409     }
410 
411     /**
412      * The test legacy apf filter class.
413      */
414     public static class TestLegacyApfFilter extends LegacyApfFilter
415             implements TestAndroidPacketFilter {
416         public static final byte[] MOCK_MAC_ADDR = {1, 2, 3, 4, 5, 6};
417         private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1};
418 
419         private FileDescriptor mWriteSocket;
420         private final MockIpClientCallback mMockIpClientCb;
421         private final boolean mThrowsExceptionWhenGeneratesProgram;
422 
TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config, MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog, NetworkQuirkMetrics networkQuirkMetrics)423         public TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config,
424                 MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog,
425                 NetworkQuirkMetrics networkQuirkMetrics) throws Exception {
426             this(context, config, ipClientCallback, ipConnectivityLog, networkQuirkMetrics,
427                     new ApfFilter.Dependencies(context),
428                     false /* throwsExceptionWhenGeneratesProgram */, new ApfFilter.Clock());
429         }
430 
TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config, MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog, NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies, boolean throwsExceptionWhenGeneratesProgram)431         public TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config,
432                 MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog,
433                 NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies,
434                 boolean throwsExceptionWhenGeneratesProgram) throws Exception {
435             this(context, config, ipClientCallback, ipConnectivityLog, networkQuirkMetrics,
436                     dependencies, throwsExceptionWhenGeneratesProgram, new ApfFilter.Clock());
437         }
438 
TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config, MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog, NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies, ApfFilter.Clock clock)439         public TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config,
440                 MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog,
441                 NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies,
442                 ApfFilter.Clock clock) throws Exception {
443             this(context, config, ipClientCallback, ipConnectivityLog, networkQuirkMetrics,
444                     dependencies, false /* throwsExceptionWhenGeneratesProgram */, clock);
445         }
446 
TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config, MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog, NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies, boolean throwsExceptionWhenGeneratesProgram, ApfFilter.Clock clock)447         public TestLegacyApfFilter(Context context, ApfFilter.ApfConfiguration config,
448                 MockIpClientCallback ipClientCallback, IpConnectivityLog ipConnectivityLog,
449                 NetworkQuirkMetrics networkQuirkMetrics, ApfFilter.Dependencies dependencies,
450                 boolean throwsExceptionWhenGeneratesProgram, ApfFilter.Clock clock)
451                 throws Exception {
452             super(context, config, InterfaceParams.getByName("lo"), ipClientCallback,
453                     ipConnectivityLog, networkQuirkMetrics, dependencies, clock);
454             mMockIpClientCb = ipClientCallback;
455             mThrowsExceptionWhenGeneratesProgram = throwsExceptionWhenGeneratesProgram;
456         }
457 
458         /**
459          * Pretend an RA packet has been received and show it to LegacyApfFilter.
460          */
pretendPacketReceived(byte[] packet)461         public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException {
462             mMockIpClientCb.resetApfProgramWait();
463             // ApfFilter's ReceiveThread will be waiting to read this.
464             Os.write(mWriteSocket, packet, 0, packet.length);
465         }
466 
467         @Override
maybeStartFilter()468         public synchronized void maybeStartFilter() {
469             mHardwareAddress = MOCK_MAC_ADDR;
470             installNewProgramLocked();
471 
472             // Create two sockets, "readSocket" and "mWriteSocket" and connect them together.
473             FileDescriptor readSocket = new FileDescriptor();
474             mWriteSocket = new FileDescriptor();
475             try {
476                 Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket);
477             } catch (ErrnoException e) {
478                 fail();
479                 return;
480             }
481             // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs.
482             // This allows us to pretend RA packets have been received via pretendPacketReceived().
483             mReceiveThread = new ReceiveThread(readSocket);
484             mReceiveThread.start();
485         }
486 
487         @Override
shutdown()488         public synchronized void shutdown() {
489             super.shutdown();
490             if (mReceiveThread != null) {
491                 mReceiveThread.halt();
492                 mReceiveThread = null;
493             }
494             IoUtils.closeQuietly(mWriteSocket);
495         }
496 
497         @Override
498         @GuardedBy("this")
emitPrologueLocked()499         protected ApfV4Generator emitPrologueLocked() throws IllegalInstructionException {
500             if (mThrowsExceptionWhenGeneratesProgram) {
501                 throw new IllegalStateException();
502             }
503             return super.emitPrologueLocked();
504         }
505     }
506 }
507