Merge "Support NetworkStatsEventLogger" into main

This commit is contained in:
Treehugger Robot
2023-10-26 14:17:21 +00:00
committed by Gerrit Code Review
4 changed files with 424 additions and 21 deletions

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2023 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 com.android.server.net;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Helper class for NetworkStatsService to log events.
*
* @hide
*/
public class NetworkStatsEventLogger {
static final int POLL_REASON_DUMPSYS = 0;
static final int POLL_REASON_FORCE_UPDATE = 1;
static final int POLL_REASON_GLOBAL_ALERT = 2;
static final int POLL_REASON_NETWORK_STATUS_CHANGED = 3;
static final int POLL_REASON_OPEN_SESSION = 4;
static final int POLL_REASON_PERIODIC = 5;
static final int POLL_REASON_RAT_CHANGED = 6;
static final int POLL_REASON_REG_CALLBACK = 7;
static final int POLL_REASON_REMOVE_UIDS = 8;
static final int POLL_REASON_UPSTREAM_CHANGED = 9;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "POLL_REASON_" }, value = {
POLL_REASON_DUMPSYS,
POLL_REASON_FORCE_UPDATE,
POLL_REASON_GLOBAL_ALERT,
POLL_REASON_NETWORK_STATUS_CHANGED,
POLL_REASON_OPEN_SESSION,
POLL_REASON_PERIODIC,
POLL_REASON_RAT_CHANGED,
POLL_REASON_REMOVE_UIDS,
POLL_REASON_REG_CALLBACK,
POLL_REASON_UPSTREAM_CHANGED
})
public @interface PollReason {
}
static final int MAX_POLL_REASON = POLL_REASON_UPSTREAM_CHANGED;
@VisibleForTesting(visibility = PRIVATE)
public static final int MAX_EVENTS_LOGS = 50;
private final LocalLog mEventChanges = new LocalLog(MAX_EVENTS_LOGS);
private final int[] mPollEventCounts = new int[MAX_POLL_REASON + 1];
/**
* Log a poll event.
*
* @param flags Flags used when polling. See NetworkStatsService#FLAG_PERSIST_*.
* @param event The event of polling to be logged.
*/
public void logPollEvent(int flags, @NonNull PollEvent event) {
mEventChanges.log("Poll(flags=" + flags + ", " + event + ")");
mPollEventCounts[event.reason]++;
}
/**
* Print poll counts per reason into the given stream.
*/
@VisibleForTesting(visibility = PRIVATE)
public void dumpPollCountsPerReason(@NonNull IndentingPrintWriter pw) {
pw.println("Poll counts per reason:");
pw.increaseIndent();
for (int i = 0; i <= MAX_POLL_REASON; i++) {
pw.println(PollEvent.pollReasonNameOf(i) + ": " + mPollEventCounts[i]);
}
pw.decreaseIndent();
pw.println();
}
/**
* Print recent poll events into the given stream.
*/
@VisibleForTesting(visibility = PRIVATE)
public void dumpRecentPollEvents(@NonNull IndentingPrintWriter pw) {
pw.println("Recent poll events:");
pw.increaseIndent();
mEventChanges.reverseDump(pw);
pw.decreaseIndent();
pw.println();
}
/**
* Print the object's state into the given stream.
*/
public void dump(@NonNull IndentingPrintWriter pw) {
dumpPollCountsPerReason(pw);
dumpRecentPollEvents(pw);
}
public static class PollEvent {
public final int reason;
public PollEvent(@PollReason int reason) {
if (reason < 0 || reason > MAX_POLL_REASON) {
throw new IllegalArgumentException("Unsupported poll reason: " + reason);
}
this.reason = reason;
}
@Override
public String toString() {
return "PollEvent{" + "reason=" + pollReasonNameOf(reason) + "}";
}
/**
* Get the name of the given reason.
*
* If the reason does not have a String representation, returns its integer representation.
*/
@NonNull
public static String pollReasonNameOf(@PollReason int reason) {
switch (reason) {
case POLL_REASON_DUMPSYS: return "DUMPSYS";
case POLL_REASON_FORCE_UPDATE: return "FORCE_UPDATE";
case POLL_REASON_GLOBAL_ALERT: return "GLOBAL_ALERT";
case POLL_REASON_NETWORK_STATUS_CHANGED: return "NETWORK_STATUS_CHANGED";
case POLL_REASON_OPEN_SESSION: return "OPEN_SESSION";
case POLL_REASON_PERIODIC: return "PERIODIC";
case POLL_REASON_RAT_CHANGED: return "RAT_CHANGED";
case POLL_REASON_REMOVE_UIDS: return "REMOVE_UIDS";
case POLL_REASON_REG_CALLBACK: return "REG_CALLBACK";
case POLL_REASON_UPSTREAM_CHANGED: return "UPSTREAM_CHANGED";
default: return Integer.toString(reason);
}
}
}
}

View File

@@ -66,6 +66,17 @@ import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport; import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT; import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_PERIODIC;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_DUMPSYS;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_FORCE_UPDATE;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_GLOBAL_ALERT;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_NETWORK_STATUS_CHANGED;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_OPEN_SESSION;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_RAT_CHANGED;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_REG_CALLBACK;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_REMOVE_UIDS;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_UPSTREAM_CHANGED;
import static com.android.server.net.NetworkStatsEventLogger.PollEvent;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
@@ -281,6 +292,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
static final String NETSTATS_IMPORT_ATTEMPTS_COUNTER_NAME = "import.attempts"; static final String NETSTATS_IMPORT_ATTEMPTS_COUNTER_NAME = "import.attempts";
static final String NETSTATS_IMPORT_SUCCESSES_COUNTER_NAME = "import.successes"; static final String NETSTATS_IMPORT_SUCCESSES_COUNTER_NAME = "import.successes";
static final String NETSTATS_IMPORT_FALLBACKS_COUNTER_NAME = "import.fallbacks"; static final String NETSTATS_IMPORT_FALLBACKS_COUNTER_NAME = "import.fallbacks";
static final String CONFIG_ENABLE_NETWORK_STATS_EVENT_LOGGER =
"enable_network_stats_event_logger";
private final Context mContext; private final Context mContext;
private final NetworkStatsFactory mStatsFactory; private final NetworkStatsFactory mStatsFactory;
@@ -441,6 +454,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* Map from key {@code OpenSessionKey} to count of opened sessions. This is for recording * Map from key {@code OpenSessionKey} to count of opened sessions. This is for recording
* the caller of open session and it is only for debugging. * the caller of open session and it is only for debugging.
*/ */
// TODO: Move to NetworkStatsEventLogger to centralize event logging.
@GuardedBy("mOpenSessionCallsLock") @GuardedBy("mOpenSessionCallsLock")
private final HashMap<OpenSessionKey, Integer> mOpenSessionCallsPerCaller = new HashMap<>(); private final HashMap<OpenSessionKey, Integer> mOpenSessionCallsPerCaller = new HashMap<>();
@@ -513,20 +527,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
case MSG_PERFORM_POLL: { case MSG_PERFORM_POLL: {
performPoll(FLAG_PERSIST_ALL); performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent((int) msg.obj));
break; break;
} }
case MSG_NOTIFY_NETWORK_STATUS: { case MSG_NOTIFY_NETWORK_STATUS: {
synchronized (mStatsLock) { synchronized (mStatsLock) {
// If no cached states, ignore. // If no cached states, ignore.
if (mLastNetworkStateSnapshots == null) break; if (mLastNetworkStateSnapshots == null) break;
handleNotifyNetworkStatus( handleNotifyNetworkStatus(mDefaultNetworks, mLastNetworkStateSnapshots,
mDefaultNetworks, mLastNetworkStateSnapshots, mActiveIface); mActiveIface, maybeCreatePollEvent((int) msg.obj));
} }
break; break;
} }
case MSG_PERFORM_POLL_REGISTER_ALERT: { case MSG_PERFORM_POLL_REGISTER_ALERT: {
performPoll(FLAG_PERSIST_NETWORK); performPoll(FLAG_PERSIST_NETWORK,
maybeCreatePollEvent(POLL_REASON_GLOBAL_ALERT));
registerGlobalAlert(); registerGlobalAlert();
break; break;
} }
@@ -612,6 +627,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mStatsMapB = mDeps.getStatsMapB(); mStatsMapB = mDeps.getStatsMapB();
mAppUidStatsMap = mDeps.getAppUidStatsMap(); mAppUidStatsMap = mDeps.getAppUidStatsMap();
mIfaceStatsMap = mDeps.getIfaceStatsMap(); mIfaceStatsMap = mDeps.getIfaceStatsMap();
// To prevent any possible races, the flag is not allowed to change until rebooting.
mSupportEventLogger = mDeps.supportEventLogger(mContext);
if (mSupportEventLogger) {
mEventLogger = new NetworkStatsEventLogger();
} else {
mEventLogger = null;
}
// TODO: Remove bpfNetMaps creation and always start SkDestroyListener // TODO: Remove bpfNetMaps creation and always start SkDestroyListener
// Following code is for the experiment to verify the SkDestroyListener refactoring. Based // Following code is for the experiment to verify the SkDestroyListener refactoring. Based
@@ -840,6 +862,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) { IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) {
return new SkDestroyListener(cookieTagMap, handler, new SharedLog(TAG)); return new SkDestroyListener(cookieTagMap, handler, new SharedLog(TAG));
} }
/**
* Get whether event logger feature is supported.
*/
public boolean supportEventLogger(Context ctx) {
return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
ctx, CONFIG_ENABLE_NETWORK_STATS_EVENT_LOGGER);
}
} }
/** /**
@@ -1432,7 +1462,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
| NetworkStatsManager.FLAG_POLL_FORCE)) != 0) { | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
performPoll(FLAG_PERSIST_ALL); performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_OPEN_SESSION));
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
@@ -1828,7 +1858,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
handleNotifyNetworkStatus(defaultNetworks, networkStates, activeIface); handleNotifyNetworkStatus(defaultNetworks, networkStates, activeIface,
maybeCreatePollEvent(POLL_REASON_NETWORK_STATUS_CHANGED));
} finally { } finally {
Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token);
} }
@@ -1845,7 +1876,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
performPoll(FLAG_PERSIST_ALL); // TODO: Log callstack for system server callers.
performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_FORCE_UPDATE));
} finally { } finally {
Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token);
} }
@@ -1902,7 +1934,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
// Create baseline stats // Create baseline stats
mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL)); mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL,
POLL_REASON_REG_CALLBACK));
return normalizedRequest; return normalizedRequest;
} }
@@ -1999,7 +2032,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
new TetheringManager.TetheringEventCallback() { new TetheringManager.TetheringEventCallback() {
@Override @Override
public void onUpstreamChanged(@Nullable Network network) { public void onUpstreamChanged(@Nullable Network network) {
performPoll(FLAG_PERSIST_NETWORK); performPoll(FLAG_PERSIST_NETWORK,
maybeCreatePollEvent(POLL_REASON_UPSTREAM_CHANGED));
} }
}; };
@@ -2008,7 +2042,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified UPDATE_DEVICE_STATS // on background handler thread, and verified UPDATE_DEVICE_STATS
// permission above. // permission above.
performPoll(FLAG_PERSIST_ALL); performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_PERIODIC));
// verify that we're watching global alert // verify that we're watching global alert
registerGlobalAlert(); registerGlobalAlert();
@@ -2072,19 +2106,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void handleOnCollapsedRatTypeChanged() { public void handleOnCollapsedRatTypeChanged() {
// Protect service from frequently updating. Remove pending messages if any. // Protect service from frequently updating. Remove pending messages if any.
mHandler.removeMessages(MSG_NOTIFY_NETWORK_STATUS); mHandler.removeMessages(MSG_NOTIFY_NETWORK_STATUS);
mHandler.sendMessageDelayed( mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_NOTIFY_NETWORK_STATUS,
mHandler.obtainMessage(MSG_NOTIFY_NETWORK_STATUS), mSettings.getPollDelay()); POLL_REASON_RAT_CHANGED), mSettings.getPollDelay());
} }
private void handleNotifyNetworkStatus( private void handleNotifyNetworkStatus(
Network[] defaultNetworks, Network[] defaultNetworks,
NetworkStateSnapshot[] snapshots, NetworkStateSnapshot[] snapshots,
String activeIface) { String activeIface,
@Nullable PollEvent event) {
synchronized (mStatsLock) { synchronized (mStatsLock) {
mWakeLock.acquire(); mWakeLock.acquire();
try { try {
mActiveIface = activeIface; mActiveIface = activeIface;
handleNotifyNetworkStatusLocked(defaultNetworks, snapshots); handleNotifyNetworkStatusLocked(defaultNetworks, snapshots, event);
} finally { } finally {
mWakeLock.release(); mWakeLock.release();
} }
@@ -2098,7 +2133,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
*/ */
@GuardedBy("mStatsLock") @GuardedBy("mStatsLock")
private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks, private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
@NonNull NetworkStateSnapshot[] snapshots) { @NonNull NetworkStateSnapshot[] snapshots, @Nullable PollEvent event) {
if (!mSystemReady) return; if (!mSystemReady) return;
if (LOGV) Log.v(TAG, "handleNotifyNetworkStatusLocked()"); if (LOGV) Log.v(TAG, "handleNotifyNetworkStatusLocked()");
@@ -2108,7 +2143,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// poll, but only persist network stats to keep codepath fast. UID stats // poll, but only persist network stats to keep codepath fast. UID stats
// will be persisted during next alarm poll event. // will be persisted during next alarm poll event.
performPollLocked(FLAG_PERSIST_NETWORK); performPollLocked(FLAG_PERSIST_NETWORK, event);
// Rebuild active interfaces based on connected networks // Rebuild active interfaces based on connected networks
mActiveIfaces.clear(); mActiveIfaces.clear();
@@ -2325,12 +2360,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
} }
private void performPoll(int flags) { private void performPoll(int flags, @Nullable PollEvent event) {
synchronized (mStatsLock) { synchronized (mStatsLock) {
mWakeLock.acquire(); mWakeLock.acquire();
try { try {
performPollLocked(flags); performPollLocked(flags, event);
} finally { } finally {
mWakeLock.release(); mWakeLock.release();
} }
@@ -2342,11 +2377,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* {@link NetworkStatsHistory}. * {@link NetworkStatsHistory}.
*/ */
@GuardedBy("mStatsLock") @GuardedBy("mStatsLock")
private void performPollLocked(int flags) { private void performPollLocked(int flags, @Nullable PollEvent event) {
if (!mSystemReady) return; if (!mSystemReady) return;
if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")"); if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked"); Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
if (mSupportEventLogger) {
mEventLogger.logPollEvent(flags, event);
}
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0; final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0; final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0; final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
@@ -2546,7 +2585,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
if (LOGV) Log.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids)); if (LOGV) Log.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
// Perform one last poll before removing // Perform one last poll before removing
performPollLocked(FLAG_PERSIST_ALL); performPollLocked(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_REMOVE_UIDS));
mUidRecorder.removeUidsLocked(uids); mUidRecorder.removeUidsLocked(uids);
mUidTagRecorder.removeUidsLocked(uids); mUidTagRecorder.removeUidsLocked(uids);
@@ -2629,7 +2668,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
if (poll) { if (poll) {
performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE); performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE,
maybeCreatePollEvent(POLL_REASON_DUMPSYS));
pw.println("Forced poll"); pw.println("Forced poll");
return; return;
} }
@@ -2689,6 +2729,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
pw.println("(failed to dump platform legacy stats import counters)"); pw.println("(failed to dump platform legacy stats import counters)");
} }
} }
pw.println(CONFIG_ENABLE_NETWORK_STATS_EVENT_LOGGER + ": " + mSupportEventLogger);
pw.decreaseIndent(); pw.decreaseIndent();
@@ -2746,6 +2787,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
pw.decreaseIndent(); pw.decreaseIndent();
pw.println(); pw.println();
if (mSupportEventLogger) {
mEventLogger.dump(pw);
}
pw.println("Stats Providers:"); pw.println("Stats Providers:");
pw.increaseIndent(); pw.increaseIndent();
invokeForAllStatsProviderCallbacks((cb) -> { invokeForAllStatsProviderCallbacks((cb) -> {
@@ -3215,6 +3260,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
} }
private final boolean mSupportEventLogger;
@GuardedBy("mStatsLock")
@Nullable
private final NetworkStatsEventLogger mEventLogger;
/**
* Create a PollEvent instance if the feature is enabled.
*/
@Nullable
public PollEvent maybeCreatePollEvent(@NetworkStatsEventLogger.PollReason int reason) {
if (mSupportEventLogger) {
return new PollEvent(reason);
}
return null;
}
private class DropBoxNonMonotonicObserver implements NonMonotonicObserver<String> { private class DropBoxNonMonotonicObserver implements NonMonotonicObserver<String> {
@Override @Override
public void foundNonMonotonic(NetworkStats left, int leftIndex, NetworkStats right, public void foundNonMonotonic(NetworkStats left, int leftIndex, NetworkStats right,

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2023 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 com.android.server.net
import android.util.IndentingPrintWriter
import com.android.server.net.NetworkStatsEventLogger.MAX_POLL_REASON
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_DUMPSYS
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_FORCE_UPDATE
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_GLOBAL_ALERT
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_NETWORK_STATUS_CHANGED
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_OPEN_SESSION
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_PERIODIC
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_RAT_CHANGED
import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_REG_CALLBACK
import com.android.server.net.NetworkStatsEventLogger.PollEvent
import com.android.server.net.NetworkStatsEventLogger.PollEvent.pollReasonNameOf
import com.android.testutils.DevSdkIgnoreRunner
import java.io.StringWriter
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
const val TEST_PERSIST_FLAG = 0x101
@RunWith(DevSdkIgnoreRunner::class)
class NetworkStatsEventLoggerTest {
val logger = NetworkStatsEventLogger()
val stringWriter = TestStringWriter()
val pw = IndentingPrintWriter(stringWriter)
@Test
fun testDump_invalid() {
// Verify it won't crash.
logger.dump(pw)
// Clear output buffer.
stringWriter.getOutputAndClear()
// Verify log invalid event throws. And nothing output in the dump.
val invalidReasons = listOf(-1, MAX_POLL_REASON + 1)
invalidReasons.forEach {
assertFailsWith<IllegalArgumentException> {
logger.logPollEvent(TEST_PERSIST_FLAG, PollEvent(it))
}
logger.dumpRecentPollEvents(pw)
val output = stringWriter.getOutputAndClear()
assertStringNotContains(output, pollReasonNameOf(it))
}
}
@Test
fun testDump_valid() {
// Choose arbitrary set of reasons for testing.
val loggedReasons = listOf(
POLL_REASON_GLOBAL_ALERT,
POLL_REASON_FORCE_UPDATE,
POLL_REASON_DUMPSYS,
POLL_REASON_PERIODIC,
POLL_REASON_RAT_CHANGED
)
val nonLoggedReasons = listOf(
POLL_REASON_NETWORK_STATUS_CHANGED,
POLL_REASON_OPEN_SESSION,
POLL_REASON_REG_CALLBACK)
// Add some valid records.
loggedReasons.forEach {
logger.logPollEvent(TEST_PERSIST_FLAG, PollEvent(it))
}
// Collect dumps.
logger.dumpRecentPollEvents(pw)
val outputRecentEvents = stringWriter.getOutputAndClear()
logger.dumpPollCountsPerReason(pw)
val outputCountsPerReason = stringWriter.getOutputAndClear()
// Verify the output contains at least necessary information.
loggedReasons.forEach {
// Verify all events are shown in the recent event dump.
val eventString = PollEvent(it).toString()
assertStringContains(outputRecentEvents, TEST_PERSIST_FLAG.toString())
assertStringContains(eventString, pollReasonNameOf(it))
assertStringContains(outputRecentEvents, eventString)
// Verify counts are 1 for each reason.
assertCountForReason(outputCountsPerReason, it, 1)
}
// Verify the output remains untouched for other reasons.
nonLoggedReasons.forEach {
assertStringNotContains(outputRecentEvents, PollEvent(it).toString())
assertCountForReason(outputCountsPerReason, it, 0)
}
}
@Test
fun testDump_maxEventLogs() {
// Choose arbitrary reason.
val reasonToBeTested = POLL_REASON_PERIODIC
val repeatCount = NetworkStatsEventLogger.MAX_EVENTS_LOGS * 2
// Collect baseline.
logger.dumpRecentPollEvents(pw)
val lineCountBaseLine = getLineCount(stringWriter.getOutputAndClear())
repeat(repeatCount) {
logger.logPollEvent(TEST_PERSIST_FLAG, PollEvent(reasonToBeTested))
}
// Collect dump.
logger.dumpRecentPollEvents(pw)
val lineCountAfterTest = getLineCount(stringWriter.getOutputAndClear())
// Verify line count increment is limited.
assertEquals(
NetworkStatsEventLogger.MAX_EVENTS_LOGS,
lineCountAfterTest - lineCountBaseLine
)
// Verify count per reason increased for the testing reason.
logger.dumpPollCountsPerReason(pw)
val outputCountsPerReason = stringWriter.getOutputAndClear()
for (reason in 0..MAX_POLL_REASON) {
assertCountForReason(
outputCountsPerReason,
reason,
if (reason == reasonToBeTested) repeatCount else 0
)
}
}
private fun getLineCount(multilineString: String) = multilineString.lines().size
private fun assertStringContains(got: String, want: String) {
assertTrue(got.contains(want), "Wanted: $want, but got: $got")
}
private fun assertStringNotContains(got: String, unwant: String) {
assertFalse(got.contains(unwant), "Unwanted: $unwant, but got: $got")
}
/**
* Assert the reason and the expected count are at the same line.
*/
private fun assertCountForReason(dump: String, reason: Int, expectedCount: Int) {
// Matches strings like "GLOBAL_ALERT: 50" but not too strict since the format might change.
val regex = Regex(pollReasonNameOf(reason) + "[^0-9]+" + expectedCount)
assertEquals(
1,
regex.findAll(dump).count(),
"Unexpected output: $dump " + " for reason: " + pollReasonNameOf(reason)
)
}
class TestStringWriter : StringWriter() {
fun getOutputAndClear() = toString().also { buffer.setLength(0) }
}
}

View File

@@ -64,6 +64,8 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_RAT_CHANGED;
import static com.android.server.net.NetworkStatsEventLogger.PollEvent.pollReasonNameOf;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static com.android.server.net.NetworkStatsService.NETSTATS_IMPORT_ATTEMPTS_COUNTER_NAME; import static com.android.server.net.NetworkStatsService.NETSTATS_IMPORT_ATTEMPTS_COUNTER_NAME;
import static com.android.server.net.NetworkStatsService.NETSTATS_IMPORT_FALLBACKS_COUNTER_NAME; import static com.android.server.net.NetworkStatsService.NETSTATS_IMPORT_FALLBACKS_COUNTER_NAME;
@@ -525,6 +527,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) { IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) {
return mSkDestroyListener; return mSkDestroyListener;
} }
@Override
public boolean supportEventLogger(@NonNull Context cts) {
return true;
}
}; };
} }
@@ -2674,4 +2681,14 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
doReturn(null).when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */); doReturn(null).when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
doTestDumpIfaceStatsMap("unknown"); doTestDumpIfaceStatsMap("unknown");
} }
// Basic test to ensure event logger dump is called.
// Note that tests to ensure detailed correctness is done in the dedicated tests.
// See NetworkStatsEventLoggerTest.
@Test
public void testDumpEventLogger() {
setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
final String dump = getDump();
assertDumpContains(dump, pollReasonNameOf(POLL_REASON_RAT_CHANGED));
}
} }