summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt14
-rw-r--r--core/java/android/net/metrics/ApfStats.java103
-rw-r--r--services/net/java/android/net/apf/ApfFilter.java98
3 files changed, 200 insertions, 15 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 77414fb45cb9..41ca0e684b29 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26031,6 +26031,20 @@ package android.net.metrics {
field public final int programLength;
}
+ public final class ApfStats 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.ApfStats> CREATOR;
+ field public final int droppedRas;
+ field public final long durationMs;
+ field public final int matchingRas;
+ field public final int maxProgramSize;
+ field public final int parseErrors;
+ field public final int programUpdates;
+ field public final int receivedRas;
+ field public final int zeroLifetimeRas;
+ }
+
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/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
new file mode 100644
index 000000000000..8451e539a7f6
--- /dev/null
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+/**
+ * An event logged for an interface with APF capabilities when its IpManager state machine exits.
+ * {@hide}
+ */
+@SystemApi
+public final class ApfStats implements Parcelable {
+
+ public final long durationMs; // time interval in milliseconds these stastistics covers
+ public final int receivedRas; // number of received RAs
+ public final int matchingRas; // number of received RAs matching a known RA
+ public final int droppedRas; // number of received RAs ignored due to the MAX_RAS limit
+ public final int zeroLifetimeRas; // number of received RAs with a minimum lifetime of 0
+ public final int parseErrors; // number of received RAs that could not be parsed
+ public final int programUpdates; // number of APF program updates
+ public final int maxProgramSize; // maximum APF program size advertised by hardware
+
+ /** {@hide} */
+ public ApfStats(long durationMs, int receivedRas, int matchingRas, int droppedRas,
+ int zeroLifetimeRas, int parseErrors, int programUpdates, int maxProgramSize) {
+ this.durationMs = durationMs;
+ this.receivedRas = receivedRas;
+ this.matchingRas = matchingRas;
+ this.droppedRas = droppedRas;
+ this.zeroLifetimeRas = zeroLifetimeRas;
+ this.parseErrors = parseErrors;
+ this.programUpdates = programUpdates;
+ this.maxProgramSize = maxProgramSize;
+ }
+
+ private ApfStats(Parcel in) {
+ this.durationMs = in.readLong();
+ this.receivedRas = in.readInt();
+ this.matchingRas = in.readInt();
+ this.droppedRas = in.readInt();
+ this.zeroLifetimeRas = in.readInt();
+ this.parseErrors = in.readInt();
+ this.programUpdates = in.readInt();
+ this.maxProgramSize = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(durationMs);
+ out.writeInt(receivedRas);
+ out.writeInt(matchingRas);
+ out.writeInt(droppedRas);
+ out.writeInt(zeroLifetimeRas);
+ out.writeInt(parseErrors);
+ out.writeInt(programUpdates);
+ out.writeInt(maxProgramSize);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("ApfStats(")
+ .append(String.format("%dms ", durationMs))
+ .append(String.format("%dB RA: {", maxProgramSize))
+ .append(String.format("%d received, ", receivedRas))
+ .append(String.format("%d matching, ", matchingRas))
+ .append(String.format("%d dropped, ", droppedRas))
+ .append(String.format("%d zero lifetime, ", zeroLifetimeRas))
+ .append(String.format("%d parse errors, ", parseErrors))
+ .append(String.format("%d program updates})", programUpdates))
+ .toString();
+ }
+
+ public static final Parcelable.Creator<ApfStats> CREATOR = new Parcelable.Creator<ApfStats>() {
+ public ApfStats createFromParcel(Parcel in) {
+ return new ApfStats(in);
+ }
+
+ public ApfStats[] newArray(int size) {
+ return new ApfStats[size];
+ }
+ };
+}
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 66fb9005208c..6d1a4eb40021 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -18,6 +18,7 @@ package android.net.apf;
import static android.system.OsConstants.*;
+import android.os.SystemClock;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.apf.ApfGenerator;
@@ -25,6 +26,7 @@ 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.ApfStats;
import android.net.metrics.IpConnectivityLog;
import android.system.ErrnoException;
import android.system.Os;
@@ -72,6 +74,17 @@ import libcore.io.IoBridge;
* @hide
*/
public class ApfFilter {
+
+ // Enums describing the outcome of receiving an RA packet.
+ private static enum ProcessRaResult {
+ MATCH, // Received RA matched a known RA
+ DROPPED, // Received RA ignored due to MAX_RAS
+ PARSE_ERROR, // Received RA could not be parsed
+ ZERO_LIFETIME, // Received RA had 0 lifetime
+ UPDATE_NEW_RA, // APF program updated for new RA
+ UPDATE_EXPIRY // APF program updated for expiry
+ }
+
// Thread to listen for RAs.
@VisibleForTesting
class ReceiveThread extends Thread {
@@ -79,6 +92,16 @@ public class ApfFilter {
private final FileDescriptor mSocket;
private volatile boolean mStopped;
+ // Starting time of the RA receiver thread.
+ private final long mStart = SystemClock.elapsedRealtime();
+
+ private int mReceivedRas; // Number of received RAs
+ private int mMatchingRas; // Number of received RAs matching a known RA
+ private int mDroppedRas; // Number of received RAs ignored due to the MAX_RAS limit
+ private int mParseErrors; // Number of received RAs that could not be parsed
+ private int mZeroLifetimeRas; // Number of received RAs with a 0 lifetime
+ private int mProgramUpdates; // Number of APF program updates triggered by receiving a RA
+
public ReceiveThread(FileDescriptor socket) {
mSocket = socket;
}
@@ -97,13 +120,46 @@ public class ApfFilter {
while (!mStopped) {
try {
int length = Os.read(mSocket, mPacket, 0, mPacket.length);
- processRa(mPacket, length);
+ updateStats(processRa(mPacket, length));
} catch (IOException|ErrnoException e) {
if (!mStopped) {
Log.e(TAG, "Read error", e);
}
}
}
+ logStats();
+ }
+
+ private void updateStats(ProcessRaResult result) {
+ mReceivedRas++;
+ switch(result) {
+ case MATCH:
+ mMatchingRas++;
+ return;
+ case DROPPED:
+ mDroppedRas++;
+ return;
+ case PARSE_ERROR:
+ mParseErrors++;
+ return;
+ case ZERO_LIFETIME:
+ mZeroLifetimeRas++;
+ return;
+ case UPDATE_EXPIRY:
+ mMatchingRas++;
+ mProgramUpdates++;
+ return;
+ case UPDATE_NEW_RA:
+ mProgramUpdates++;
+ return;
+ }
+ }
+
+ private void logStats() {
+ long durationMs = SystemClock.elapsedRealtime() - mStart;
+ int maxSize = mApfCapabilities.maximumApfProgramSize;
+ mMetricsLog.log(new ApfStats(durationMs, mReceivedRas, mMatchingRas, mDroppedRas,
+ mZeroLifetimeRas, mParseErrors, mProgramUpdates, maxSize));
}
}
@@ -216,6 +272,7 @@ public class ApfFilter {
}
// Returns seconds since Unix Epoch.
+ // TODO: use SystemClock.elapsedRealtime() instead
private static long curTime() {
return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS;
}
@@ -809,15 +866,12 @@ public class ApfFilter {
programMinLifetime, rasToFilter.size(), mRas.size(), program.length, flags));
}
- // Install a new filter program if the last installed one will die soon.
- @GuardedBy("this")
- private void maybeInstallNewProgramLocked() {
- if (mRas.size() == 0) return;
- // If the current program doesn't expire for a while, don't bother updating.
+ /**
+ * Returns {@code true} if a new program should be installed because the current one dies soon.
+ */
+ private boolean shouldInstallnewProgram() {
long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
- if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) {
- installNewProgramLocked();
- }
+ return expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
}
private void hexDump(String msg, byte[] packet, int length) {
@@ -836,7 +890,12 @@ public class ApfFilter {
}
}
- private synchronized void processRa(byte[] packet, int length) {
+ /**
+ * Process an RA packet, updating the list of known RAs and installing a new APF program
+ * if the current APF program should be updated.
+ * @return a ProcessRaResult enum describing what action was performed.
+ */
+ private synchronized ProcessRaResult processRa(byte[] packet, int length) {
if (VDBG) hexDump("Read packet = ", packet, length);
// Have we seen this RA before?
@@ -858,25 +917,34 @@ public class ApfFilter {
// Swap to front of array.
mRas.add(0, mRas.remove(i));
- maybeInstallNewProgramLocked();
- return;
+ // If the current program doesn't expire for a while, don't update.
+ if (shouldInstallnewProgram()) {
+ installNewProgramLocked();
+ return ProcessRaResult.UPDATE_EXPIRY;
+ }
+ return ProcessRaResult.MATCH;
}
}
purgeExpiredRasLocked();
// TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
- if (mRas.size() >= MAX_RAS) return;
+ if (mRas.size() >= MAX_RAS) {
+ return ProcessRaResult.DROPPED;
+ }
final Ra ra;
try {
ra = new Ra(packet, length);
} catch (Exception e) {
Log.e(TAG, "Error parsing RA: " + e);
- return;
+ return ProcessRaResult.PARSE_ERROR;
}
// Ignore 0 lifetime RAs.
- if (ra.isExpired()) return;
+ if (ra.isExpired()) {
+ return ProcessRaResult.ZERO_LIFETIME;
+ }
log("Adding " + ra);
mRas.add(ra);
installNewProgramLocked();
+ return ProcessRaResult.UPDATE_NEW_RA;
}
/**