From 4fc3ee5be223122792ebc0ee8a05c93d93e26a52 Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Thu, 2 Jun 2016 11:20:27 +0900 Subject: [PATCH] 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 --- api/system-current.txt | 13 ++ .../android/net/metrics/ApfProgramEvent.java | 137 ++++++++++++++++++ .../android/net/metrics/IpManagerEvent.java | 1 + .../net/metrics/ValidationProbeEvent.java | 1 + .../net/java/android/net/apf/ApfFilter.java | 18 ++- .../src/android/net/apf/ApfTest.java | 6 +- 6 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 core/java/android/net/metrics/ApfProgramEvent.java diff --git a/api/system-current.txt b/api/system-current.txt index 3fa518a43e49f..77414fb45cb9b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -26018,6 +26018,19 @@ package android.net.http { 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 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 0000000000000..3cd058cb6dc59 --- /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 CREATOR + = new Parcelable.Creator() { + 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 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 a39061748ac3d..8949fae2545f2 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -29,6 +29,7 @@ import com.android.internal.util.MessageUtils; @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 d5ad0f6c25a92..c2d259fd216bf 100644 --- a/core/java/android/net/metrics/ValidationProbeEvent.java +++ b/core/java/android/net/metrics/ValidationProbeEvent.java @@ -29,6 +29,7 @@ import com.android.internal.util.MessageUtils; @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 ce374266b6a24..66fb9005208c3 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; 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 @@ public class ApfFilter { // 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 @@ public class ApfFilter { 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 @@ public class ApfFilter { // 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 @@ public class ApfFilter { return gen; } + /** + * Generate and install a new filter program. + */ @GuardedBy("this") @VisibleForTesting void installNewProgramLocked() { purgeExpiredRasLocked(); + ArrayList 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 rasToFilter = new ArrayList(); for (Ra ra : mRas) { ra.generateFilterLocked(gen); // Stop if we get too big. @@ -797,6 +804,9 @@ public class ApfFilter { 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 8ac238a9c415d..af78839c82d6f 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 @@ public class ApfTest extends AndroidTestCase { 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 @@ public class ApfTest extends AndroidTestCase { 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 {