summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <treehugger-gerrit@google.com> 2018-05-21 15:33:28 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2018-05-21 15:33:28 +0000
commitb5dda0e316a26936a7834373215fbf08fb27a8c3 (patch)
tree43372aecae8ceb4058e87a68264f5e2bc859ee8c
parentb1b864cee20ebe34c2ec7638b51da1318f4f5984 (diff)
parentab30db7072968016c1be0bb384385fd7235ae815 (diff)
Merge changes from topic "apf-read-ram"
* changes: apf: Add counters for dropped / passed packets Add support for reading a snapshot of the APF data
-rw-r--r--services/net/java/android/net/apf/ApfCapabilities.java10
-rw-r--r--services/net/java/android/net/apf/ApfFilter.java276
-rw-r--r--services/net/java/android/net/apf/ApfGenerator.java5
-rw-r--r--services/net/java/android/net/ip/IpClient.java43
4 files changed, 301 insertions, 33 deletions
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
index 703b41560290..dec8ca207343 100644
--- a/services/net/java/android/net/apf/ApfCapabilities.java
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -49,4 +49,14 @@ public class ApfCapabilities {
return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(),
apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
}
+
+ /**
+ * Returns true if the APF interpreter advertises support for the data buffer access opcodes
+ * LDDW and STDW.
+ *
+ * Full LDDW and STDW support is present from APFv4 on.
+ */
+ public boolean hasDataAccess() {
+ return apfVersionSupported >= 4;
+ }
}
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 92d37096d5a6..8ac6ede03529 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -24,6 +24,7 @@ import static com.android.internal.util.BitUtils.getUint32;
import static com.android.internal.util.BitUtils.getUint8;
import static com.android.internal.util.BitUtils.uint32;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -102,6 +103,70 @@ public class ApfFilter {
UPDATE_EXPIRY // APF program updated for expiry
}
+ /**
+ * APF packet counters.
+ *
+ * Packet counters are 32bit big-endian values, and allocated near the end of the APF data
+ * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4,
+ * the last writable 32bit word.
+ */
+ @VisibleForTesting
+ private static enum Counter {
+ RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds)
+ TOTAL_PACKETS,
+ PASSED_ARP,
+ PASSED_DHCP,
+ PASSED_IPV4,
+ PASSED_IPV6_NON_ICMP,
+ PASSED_IPV4_UNICAST,
+ PASSED_IPV6_ICMP,
+ PASSED_IPV6_UNICAST_NON_ICMP,
+ PASSED_ARP_NON_IPV4,
+ PASSED_ARP_UNKNOWN,
+ PASSED_ARP_UNICAST_REPLY,
+ PASSED_NON_IP_UNICAST,
+ DROPPED_ETH_BROADCAST,
+ DROPPED_RA,
+ DROPPED_GARP_REPLY,
+ DROPPED_ARP_OTHER_HOST,
+ DROPPED_IPV4_L2_BROADCAST,
+ DROPPED_IPV4_BROADCAST_ADDR,
+ DROPPED_IPV4_BROADCAST_NET,
+ DROPPED_IPV4_MULTICAST,
+ DROPPED_IPV6_ROUTER_SOLICITATION,
+ DROPPED_IPV6_MULTICAST_NA,
+ DROPPED_IPV6_MULTICAST,
+ DROPPED_IPV6_MULTICAST_PING,
+ DROPPED_IPV6_NON_ICMP_MULTICAST,
+ DROPPED_802_3_FRAME,
+ DROPPED_ETHERTYPE_BLACKLISTED;
+
+ // Returns the negative byte offset from the end of the APF data segment for
+ // a given counter.
+ public int offset() {
+ return - this.ordinal() * 4; // Currently, all counters are 32bit long.
+ }
+
+ // Returns the total size of the data segment in bytes.
+ public static int totalSize() {
+ return (Counter.class.getEnumConstants().length - 1) * 4;
+ }
+ }
+
+ /**
+ * When APFv4 is supported, loads R1 with the offset of the specified counter.
+ */
+ private void maybeSetCounter(ApfGenerator gen, Counter c) {
+ if (mApfCapabilities.hasDataAccess()) {
+ gen.addLoadImmediate(Register.R1, c.offset());
+ }
+ }
+
+ // When APFv4 is supported, these point to the trampolines generated by emitEpilogue().
+ // Otherwise, they're just aliases for PASS_LABEL and DROP_LABEL.
+ private final String mCountAndPassLabel;
+ private final String mCountAndDropLabel;
+
// Thread to listen for RAs.
@VisibleForTesting
class ReceiveThread extends Thread {
@@ -289,6 +354,16 @@ public class ApfFilter {
mDrop802_3Frames = config.ieee802_3Filter;
mContext = context;
+ if (mApfCapabilities.hasDataAccess()) {
+ mCountAndPassLabel = "countAndPass";
+ mCountAndDropLabel = "countAndDrop";
+ } else {
+ // APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP,
+ // preserving the original pre-APFv4 behavior.
+ mCountAndPassLabel = ApfGenerator.PASS_LABEL;
+ mCountAndDropLabel = ApfGenerator.DROP_LABEL;
+ }
+
// Now fill the black list from the passed array
mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
@@ -302,6 +377,10 @@ public class ApfFilter {
new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
}
+ public synchronized void setDataSnapshot(byte[] data) {
+ mDataSnapshot = data;
+ }
+
private void log(String s) {
Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
}
@@ -350,6 +429,10 @@ public class ApfFilter {
try {
mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
synchronized(this) {
+ // Clear APF memory.
+ byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize];
+ mIpClientCallback.installPacketFilter(zeroes);
+
// Install basic filters
installNewProgramLocked();
}
@@ -729,7 +812,8 @@ public class ApfFilter {
gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
}
}
- gen.addJump(gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.DROPPED_RA);
+ gen.addJump(mCountAndDropLabel);
gen.defineLabel(nextFilterLabel);
return filterLifetime;
}
@@ -764,6 +848,16 @@ public class ApfFilter {
@GuardedBy("this")
private byte[] mLastInstalledProgram;
+ /**
+ * For debugging only. Contains the latest APF buffer snapshot captured from the firmware.
+ *
+ * A typical size for this buffer is 4KB. It is present only if the WiFi HAL supports
+ * IWifiStaIface#readApfPacketFilterData(), and the APF interpreter advertised support for
+ * the opcodes to access the data buffer (LDDW and STDW).
+ */
+ @GuardedBy("this") @Nullable
+ private byte[] mDataSnapshot;
+
// How many times the program was updated since we started.
@GuardedBy("this")
private int mNumProgramUpdates = 0;
@@ -799,31 +893,37 @@ public class ApfFilter {
// Pass if not ARP IPv4.
gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_ARP_NON_IPV4);
+ gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel);
// Pass if unknown ARP opcode.
gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
- gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_ARP_UNKNOWN);
+ gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel);
// Pass if unicast reply.
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
+ gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
// Either a unicast request, a unicast reply, or a broadcast reply.
gen.defineLabel(checkTargetIPv4);
if (mIPv4Address == null) {
// When there is no IPv4 address, drop GARP replies (b/29404209).
gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.DROPPED_GARP_REPLY);
+ gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
} else {
// When there is an IPv4 address, drop unicast/broadcast requests
// and broadcast replies with a different target IPv4 address.
gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
+ gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel);
}
- gen.addJump(gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_ARP);
+ gen.addJump(mCountAndPassLabel);
}
/**
@@ -866,7 +966,8 @@ public class ApfFilter {
// NOTE: Relies on R1 containing IPv4 header offset.
gen.addAddR1();
gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
- gen.addJump(gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_DHCP);
+ gen.addJump(mCountAndPassLabel);
// Drop all multicasts/broadcasts.
gen.defineLabel(skipDhcpv4Filter);
@@ -874,24 +975,31 @@ public class ApfFilter {
// If IPv4 destination address is in multicast range, drop.
gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
gen.addAnd(0xf0);
- gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
+ gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel);
// If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
+ maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel);
if (mIPv4Address != null && mIPv4PrefixLength < 31) {
+ maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
- gen.addJumpIfR0Equals(broadcastAddr, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
}
// If L2 broadcast packet, drop.
+ // TODO: can we invert this condition to fall through to the common pass case below?
+ maybeSetCounter(gen, Counter.PASSED_IPV4_UNICAST);
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
- gen.addJump(gen.DROP_LABEL);
+ gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
+ maybeSetCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
+ gen.addJump(mCountAndDropLabel);
}
// Otherwise, pass
- gen.addJump(gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_IPV4);
+ gen.addJump(mCountAndPassLabel);
}
@@ -938,14 +1046,17 @@ public class ApfFilter {
// Drop all other packets sent to ff00::/8 (multicast prefix).
gen.defineLabel(dropAllIPv6MulticastsLabel);
+ maybeSetCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(0xff, mCountAndDropLabel);
// Not multicast. Pass.
- gen.addJump(gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
+ gen.addJump(mCountAndPassLabel);
gen.defineLabel(skipIPv6MulticastFilterLabel);
} else {
// If not ICMPv6, pass.
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
+ gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel);
}
// If we got this far, the packet is ICMPv6. Drop some specific types.
@@ -954,7 +1065,8 @@ public class ApfFilter {
String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
// Drop all router solicitations (b/32833400)
- gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
+ gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel);
// If not neighbor announcements, skip filter.
gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
// If to ff02::1, drop.
@@ -962,7 +1074,8 @@ public class ApfFilter {
gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
skipUnsolicitedMulticastNALabel);
- gen.addJump(gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
+ gen.addJump(mCountAndDropLabel);
gen.defineLabel(skipUnsolicitedMulticastNALabel);
}
@@ -985,10 +1098,18 @@ public class ApfFilter {
* </ul>
*/
@GuardedBy("this")
- private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
+ private ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
// This is guaranteed to succeed because of the check in maybeCreate.
ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
+ if (mApfCapabilities.hasDataAccess()) {
+ // Increment TOTAL_PACKETS
+ maybeSetCounter(gen, Counter.TOTAL_PACKETS);
+ gen.addLoadData(Register.R0, 0); // load counter
+ gen.addAdd(1);
+ gen.addStoreData(Register.R0, 0); // write-back counter
+ }
+
// Here's a basic summary of what the initial program does:
//
// if it's a 802.3 Frame (ethtype < 0x0600):
@@ -1009,12 +1130,14 @@ public class ApfFilter {
if (mDrop802_3Frames) {
// drop 802.3 frames (ethtype < 0x0600)
- gen.addJumpIfR0LessThan(ETH_TYPE_MIN, gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.DROPPED_802_3_FRAME);
+ gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel);
}
// Handle ether-type black list
+ maybeSetCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
for (int p : mEthTypeBlackList) {
- gen.addJumpIfR0Equals(p, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(p, mCountAndDropLabel);
}
// Add ARP filters:
@@ -1041,8 +1164,10 @@ public class ApfFilter {
// Drop non-IP non-ARP broadcasts, pass the rest
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
- gen.addJump(gen.DROP_LABEL);
+ maybeSetCounter(gen, Counter.PASSED_NON_IP_UNICAST);
+ gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
+ maybeSetCounter(gen, Counter.DROPPED_ETH_BROADCAST);
+ gen.addJump(mCountAndDropLabel);
// Add IPv6 filters:
gen.defineLabel(ipv6FilterLabel);
@@ -1051,6 +1176,39 @@ public class ApfFilter {
}
/**
+ * Append packet counting epilogue to the APF program.
+ *
+ * Currently, the epilogue consists of two trampolines which count passed and dropped packets
+ * before jumping to the actual PASS and DROP labels.
+ */
+ @GuardedBy("this")
+ private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException {
+ // If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it
+ // will just fall-through to the PASS label.
+ if (!mApfCapabilities.hasDataAccess()) return;
+
+ // Execution will reach the bottom of the program if none of the filters match,
+ // which will pass the packet to the application processor.
+ maybeSetCounter(gen, Counter.PASSED_IPV6_ICMP);
+
+ // Append the count & pass trampoline, which increments the counter at the data address
+ // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting
+ // the entire sequence inline for every counter.
+ gen.defineLabel(mCountAndPassLabel);
+ gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
+ gen.addAdd(1); // R0++
+ gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
+ gen.addJump(gen.PASS_LABEL);
+
+ // Same as above for the count & drop trampoline.
+ gen.defineLabel(mCountAndDropLabel);
+ gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
+ gen.addAdd(1); // R0++
+ gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
+ gen.addJump(gen.DROP_LABEL);
+ }
+
+ /**
* Generate and install a new filter program.
*/
@GuardedBy("this")
@@ -1060,22 +1218,39 @@ public class ApfFilter {
ArrayList<Ra> rasToFilter = new ArrayList<>();
final byte[] program;
long programMinLifetime = Long.MAX_VALUE;
+ long maximumApfProgramSize = mApfCapabilities.maximumApfProgramSize;
+ if (mApfCapabilities.hasDataAccess()) {
+ // Reserve space for the counters.
+ maximumApfProgramSize -= Counter.totalSize();
+ }
+
try {
// Step 1: Determine how many RA filters we can fit in the program.
- ApfGenerator gen = beginProgramLocked();
+ ApfGenerator gen = emitPrologueLocked();
+
+ // The epilogue normally goes after the RA filters, but add it early to include its
+ // length when estimating the total.
+ emitEpilogue(gen);
+
+ // Can't fit the program even without any RA filters?
+ if (gen.programLengthOverEstimate() > maximumApfProgramSize) {
+ Log.e(TAG, "Program exceeds maximum size " + maximumApfProgramSize);
+ return;
+ }
+
for (Ra ra : mRas) {
ra.generateFilterLocked(gen);
// Stop if we get too big.
- if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break;
+ if (gen.programLengthOverEstimate() > maximumApfProgramSize) break;
rasToFilter.add(ra);
}
+
// Step 2: Actually generate the program
- gen = beginProgramLocked();
+ gen = emitPrologueLocked();
for (Ra ra : rasToFilter) {
programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
}
- // Execution will reach the end of the program if no filters match, which will pass the
- // packet to the AP.
+ emitEpilogue(gen);
program = gen.generate();
} catch (IllegalInstructionException|IllegalStateException e) {
Log.e(TAG, "Failed to generate APF program.", e);
@@ -1277,6 +1452,23 @@ public class ApfFilter {
installNewProgramLocked();
}
+ static public long counterValue(byte[] data, Counter counter)
+ throws ArrayIndexOutOfBoundsException {
+ // Follow the same wrap-around addressing scheme of the interpreter.
+ int offset = counter.offset();
+ if (offset < 0) {
+ offset = data.length + offset;
+ }
+
+ // Decode 32bit big-endian integer into a long so we can count up beyond 2^31.
+ long value = 0;
+ for (int i = 0; i < 4; i++) {
+ value = value << 8 | (data[offset] & 0xFF);
+ offset++;
+ }
+ return value;
+ }
+
public synchronized void dump(IndentingPrintWriter pw) {
pw.println("Capabilities: " + mApfCapabilities);
pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
@@ -1318,6 +1510,32 @@ public class ApfFilter {
pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
pw.decreaseIndent();
}
+
+ pw.println("APF packet counters: ");
+ pw.increaseIndent();
+ if (!mApfCapabilities.hasDataAccess()) {
+ pw.println("APF counters not supported");
+ } else if (mDataSnapshot == null) {
+ pw.println("No last snapshot.");
+ } else {
+ try {
+ Counter[] counters = Counter.class.getEnumConstants();
+ for (Counter c : Arrays.asList(counters).subList(1, counters.length)) {
+ long value = counterValue(mDataSnapshot, c);
+ // Only print non-zero counters
+ if (value != 0) {
+ pw.println(c.toString() + ": " + value);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ pw.println("Uh-oh: " + e);
+ }
+ if (VDBG) {
+ pw.println("Raw data dump: ");
+ pw.println(HexDump.dumpHexString(mDataSnapshot));
+ }
+ }
+ pw.decreaseIndent();
}
// TODO: move to android.net.NetworkUtils
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index 99b2fc6db472..87a1b5ea8b4d 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -378,8 +378,7 @@ public class ApfGenerator {
}
/**
- * Returns true if the specified {@code version} is supported by the ApfGenerator, otherwise
- * false.
+ * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false.
*/
public static boolean supportsVersion(int version) {
return version >= MIN_APF_VERSION;
@@ -753,7 +752,7 @@ public class ApfGenerator {
/**
* Add an instruction to the end of the program to jump to {@code target} if the bytes of the
- * packet at, an offset specified by {@code register}, match {@code bytes}.
+ * packet at an offset specified by {@code register} match {@code bytes}.
*/
public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
throws IllegalInstructionException {
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 37bb2ad8f6c1..63ae09a79379 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -16,6 +16,7 @@
package android.net.ip;
+import com.android.internal.util.HexDump;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.WakeupMessage;
@@ -174,6 +175,12 @@ public class IpClient extends StateMachine {
// Install an APF program to filter incoming packets.
public void installPacketFilter(byte[] filter) {}
+ // Asynchronously read back the APF program & data buffer from the wifi driver.
+ // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+ // buffer. In response to this request, the driver returns the data buffer asynchronously
+ // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+ public void startReadPacketFilter() {}
+
// If multicast filtering cannot be accomplished with APF, this function will be called to
// actuate multicast filtering using another means.
public void setFallbackMulticastFilter(boolean enabled) {}
@@ -280,6 +287,11 @@ public class IpClient extends StateMachine {
log("installPacketFilter(byte[" + filter.length + "])");
}
@Override
+ public void startReadPacketFilter() {
+ mCallback.startReadPacketFilter();
+ log("startReadPacketFilter()");
+ }
+ @Override
public void setFallbackMulticastFilter(boolean enabled) {
mCallback.setFallbackMulticastFilter(enabled);
log("setFallbackMulticastFilter(" + enabled + ")");
@@ -591,6 +603,7 @@ public class IpClient extends StateMachine {
private static final int CMD_SET_MULTICAST_FILTER = 9;
private static final int EVENT_PROVISIONING_TIMEOUT = 10;
private static final int EVENT_DHCPACTION_TIMEOUT = 11;
+ private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
private static final int MAX_LOG_RECORDS = 500;
private static final int MAX_PACKET_RECORDS = 100;
@@ -644,6 +657,14 @@ public class IpClient extends StateMachine {
private boolean mMulticastFiltering;
private long mStartTimeMillis;
+ /**
+ * Reading the snapshot is an asynchronous operation initiated by invoking
+ * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
+ * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
+ * signals when a new snapshot is ready.
+ */
+ private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
+
public static class Dependencies {
public INetworkManagementService getNMS() {
return INetworkManagementService.Stub.asInterface(
@@ -857,6 +878,10 @@ public class IpClient extends StateMachine {
sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
}
+ public void readPacketFilterComplete(byte[] data) {
+ sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
+ }
+
/**
* Set the TCP buffer sizes to use.
*
@@ -902,7 +927,16 @@ public class IpClient extends StateMachine {
pw.println(mTag + " APF dump:");
pw.increaseIndent();
if (apfFilter != null) {
+ if (apfCapabilities.hasDataAccess()) {
+ // Request a new snapshot, then wait for it.
+ mApfDataSnapshotComplete.close();
+ mCallback.startReadPacketFilter();
+ if (!mApfDataSnapshotComplete.block(1000)) {
+ pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
+ }
+ }
apfFilter.dump(pw);
+
} else {
pw.print("No active ApfFilter; ");
if (provisioningConfig == null) {
@@ -914,7 +948,6 @@ public class IpClient extends StateMachine {
}
}
pw.decreaseIndent();
-
pw.println();
pw.println(mTag + " current ProvisioningConfiguration:");
pw.increaseIndent();
@@ -1710,6 +1743,14 @@ public class IpClient extends StateMachine {
break;
}
+ case EVENT_READ_PACKET_FILTER_COMPLETE: {
+ if (mApfFilter != null) {
+ mApfFilter.setDataSnapshot((byte[]) msg.obj);
+ }
+ mApfDataSnapshotComplete.open();
+ break;
+ }
+
case EVENT_DHCPACTION_TIMEOUT:
stopDhcpAction();
break;