diff options
| -rw-r--r-- | api/system-current.txt | 14 | ||||
| -rw-r--r-- | core/java/android/net/metrics/ApfStats.java | 103 | ||||
| -rw-r--r-- | services/net/java/android/net/apf/ApfFilter.java | 98 |
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; } /** |