Log events at APF program generation
Example:
ConnectivityMetricsEvent(15:24:52.018, 0, 0): ApfProgramEvent(0/0 RAs 121B forever FLAG_MULTICAST_FILTER_ON)
ConnectivityMetricsEvent(15:24:53.036, 0, 0): ApfProgramEvent(1/1 RAs 334B 600s)
ConnectivityMetricsEvent(15:24:53.590, 0, 0): ApfProgramEvent(1/1 RAs 360B 600s FLAG_MULTICAST_FILTER_ON, FLAG_HAS_IPV4_ADDRESS)
ConnectivityMetricsEvent(15:24:58.157, 0, 0): ApfProgramEvent(1/1 RAs 294B 599s FLAG_HAS_IPV4_ADDRESS)
Bug: 28204408
Change-Id: I9c4c82861cf42eb2c7e7bf5471f05e8ff2fc560c
diff --git a/api/system-current.txt b/api/system-current.txt
index 3fa518a..77414fb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26018,6 +26018,19 @@
package android.net.metrics {
+ public final class ApfProgramEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.metrics.ApfProgramEvent> CREATOR;
+ field public static final int FLAG_HAS_IPV4_ADDRESS = 1; // 0x1
+ field public static final int FLAG_MULTICAST_FILTER_ON = 0; // 0x0
+ field public final int currentRas;
+ field public final int filteredRas;
+ field public final int flags;
+ field public final long lifetime;
+ field public final int programLength;
+ }
+
public final class DefaultNetworkEvent implements android.os.Parcelable {
method public int describeContents();
method public static void logEvent(int, int[], int, boolean, boolean);
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
new file mode 100644
index 0000000..3cd058c
--- /dev/null
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 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.metrics;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import com.android.internal.util.MessageUtils;
+
+/**
+ * An event logged when there is a change or event that requires updating the
+ * the APF program in place with a new APF program.
+ * {@hide}
+ */
+@SystemApi
+public final class ApfProgramEvent implements Parcelable {
+
+ // Bitflag constants describing what an Apf program filters.
+ // Bits are indexeds from LSB to MSB, starting at index 0.
+ // TODO: use @IntDef
+ public static final int FLAG_MULTICAST_FILTER_ON = 0;
+ public static final int FLAG_HAS_IPV4_ADDRESS = 1;
+
+ public final long lifetime; // Lifetime of the program in seconds
+ public final int filteredRas; // Number of RAs filtered by the APF program
+ public final int currentRas; // Total number of current RAs at generation time
+ public final int programLength; // Length of the APF program in bytes
+ public final int flags; // Bitfield compound of FLAG_* constants
+
+ /** {@hide} */
+ public ApfProgramEvent(
+ long lifetime, int filteredRas, int currentRas, int programLength, int flags) {
+ this.lifetime = lifetime;
+ this.filteredRas = filteredRas;
+ this.currentRas = currentRas;
+ this.programLength = programLength;
+ this.flags = flags;
+ }
+
+ private ApfProgramEvent(Parcel in) {
+ this.lifetime = in.readLong();
+ this.filteredRas = in.readInt();
+ this.currentRas = in.readInt();
+ this.programLength = in.readInt();
+ this.flags = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(lifetime);
+ out.writeInt(filteredRas);
+ out.writeInt(currentRas);
+ out.writeInt(programLength);
+ out.writeInt(flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ String lifetimeString = (lifetime < Long.MAX_VALUE) ? lifetime + "s" : "forever";
+ return String.format("ApfProgramEvent(%d/%d RAs %dB %s %s)",
+ filteredRas, currentRas, programLength, lifetimeString, namesOf(flags));
+ }
+
+ public static final Parcelable.Creator<ApfProgramEvent> CREATOR
+ = new Parcelable.Creator<ApfProgramEvent>() {
+ public ApfProgramEvent createFromParcel(Parcel in) {
+ return new ApfProgramEvent(in);
+ }
+
+ public ApfProgramEvent[] newArray(int size) {
+ return new ApfProgramEvent[size];
+ }
+ };
+
+ /** {@hide} */
+ public static int flagsFor(boolean hasIPv4, boolean multicastFilterOn) {
+ int bitfield = 0;
+ if (hasIPv4) {
+ bitfield |= (1 << FLAG_HAS_IPV4_ADDRESS);
+ }
+ if (multicastFilterOn) {
+ bitfield |= (1 << FLAG_MULTICAST_FILTER_ON);
+ }
+ return bitfield;
+ }
+
+ // TODO: consider using java.util.BitSet
+ private static int[] bitflagsOf(int bitfield) {
+ int[] flags = new int[Integer.bitCount(bitfield)];
+ int i = 0;
+ int bitflag = 0;
+ while (bitfield != 0) {
+ if ((bitfield & 1) != 0) {
+ flags[i++] = bitflag;
+ }
+ bitflag++;
+ bitfield = bitfield >>> 1;
+ }
+ return flags;
+ }
+
+ private static String namesOf(int bitfields) {
+ return Arrays.stream(bitflagsOf(bitfields))
+ .mapToObj(i -> Decoder.constants.get(i))
+ .collect(Collectors.joining(", "));
+ }
+
+ final static class Decoder {
+ static final SparseArray<String> constants =
+ MessageUtils.findMessageNames(
+ new Class[]{ApfProgramEvent.class}, new String[]{"FLAG_"});
+ }
+}
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index a390617..8949fae 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -29,6 +29,7 @@
@SystemApi
public final class IpManagerEvent implements Parcelable {
+ // TODO: use @IntDef
public static final int PROVISIONING_OK = 1;
public static final int PROVISIONING_FAIL = 2;
public static final int COMPLETE_LIFECYCLE = 3;
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index d5ad0f6..c2d259f 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -29,6 +29,7 @@
@SystemApi
public final class ValidationProbeEvent implements Parcelable {
+ // TODO: use @IntDef
public static final int PROBE_DNS = 0;
public static final int PROBE_HTTP = 1;
public static final int PROBE_HTTPS = 2;
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index ce37426..66fb900 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -24,9 +24,12 @@
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpManager;
+import android.net.metrics.ApfProgramEvent;
+import android.net.metrics.IpConnectivityLog;
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
+import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
@@ -140,7 +143,7 @@
// NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
- private static int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
+ private static final 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)
@@ -148,11 +151,12 @@
4, // Protocol size: 4
0, 1 // Opcode: request (1)
};
- private static int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
private final ApfCapabilities mApfCapabilities;
private final IpManager.Callback mIpManagerCallback;
private final NetworkInterface mNetworkInterface;
+ private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
@VisibleForTesting
byte[] mHardwareAddress;
@VisibleForTesting
@@ -213,7 +217,7 @@
// Returns seconds since Unix Epoch.
private static long curTime() {
- return System.currentTimeMillis() / 1000L;
+ return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS;
}
// A class to hold information about an RA.
@@ -760,16 +764,19 @@
return gen;
}
+ /**
+ * Generate and install a new filter program.
+ */
@GuardedBy("this")
@VisibleForTesting
void installNewProgramLocked() {
purgeExpiredRasLocked();
+ ArrayList<Ra> rasToFilter = new ArrayList<>();
final byte[] program;
long programMinLifetime = Long.MAX_VALUE;
try {
// Step 1: Determine how many RA filters we can fit in the program.
ApfGenerator gen = beginProgramLocked();
- ArrayList<Ra> rasToFilter = new ArrayList<Ra>();
for (Ra ra : mRas) {
ra.generateFilterLocked(gen);
// Stop if we get too big.
@@ -797,6 +804,9 @@
hexDump("Installing filter: ", program, program.length);
}
mIpManagerCallback.installPacketFilter(program);
+ int flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
+ mMetricsLog.log(new ApfProgramEvent(
+ programMinLifetime, rasToFilter.size(), mRas.size(), program.length, flags));
}
// Install a new filter program if the last installed one will die soon.
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index 8ac238a..af78839 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -652,7 +652,7 @@
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 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)
@@ -660,9 +660,9 @@
4, // Protocol size: 4
0, 1 // Opcode: request (1)
};
- private static int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
- private static byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1};
+ private static final byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1};
@LargeTest
public void testApfFilterIPv4() throws Exception {