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.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
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.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_SUCCESSES_COUNTER_NAME = "import.successes";
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 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
* the caller of open session and it is only for debugging.
*/
// TODO: Move to NetworkStatsEventLogger to centralize event logging.
@GuardedBy("mOpenSessionCallsLock")
private final HashMap<OpenSessionKey, Integer> mOpenSessionCallsPerCaller = new HashMap<>();
@@ -513,20 +527,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PERFORM_POLL: {
performPoll(FLAG_PERSIST_ALL);
performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent((int) msg.obj));
break;
}
case MSG_NOTIFY_NETWORK_STATUS: {
synchronized (mStatsLock) {
// If no cached states, ignore.
if (mLastNetworkStateSnapshots == null) break;
handleNotifyNetworkStatus(
mDefaultNetworks, mLastNetworkStateSnapshots, mActiveIface);
handleNotifyNetworkStatus(mDefaultNetworks, mLastNetworkStateSnapshots,
mActiveIface, maybeCreatePollEvent((int) msg.obj));
}
break;
}
case MSG_PERFORM_POLL_REGISTER_ALERT: {
performPoll(FLAG_PERSIST_NETWORK);
performPoll(FLAG_PERSIST_NETWORK,
maybeCreatePollEvent(POLL_REASON_GLOBAL_ALERT));
registerGlobalAlert();
break;
}
@@ -612,6 +627,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mStatsMapB = mDeps.getStatsMapB();
mAppUidStatsMap = mDeps.getAppUidStatsMap();
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
// 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) {
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) {
final long ident = Binder.clearCallingIdentity();
try {
performPoll(FLAG_PERSIST_ALL);
performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_OPEN_SESSION));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1828,7 +1858,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final long token = Binder.clearCallingIdentity();
try {
handleNotifyNetworkStatus(defaultNetworks, networkStates, activeIface);
handleNotifyNetworkStatus(defaultNetworks, networkStates, activeIface,
maybeCreatePollEvent(POLL_REASON_NETWORK_STATUS_CHANGED));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1845,7 +1876,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final long token = Binder.clearCallingIdentity();
try {
performPoll(FLAG_PERSIST_ALL);
// TODO: Log callstack for system server callers.
performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_FORCE_UPDATE));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1902,7 +1934,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
// Create baseline stats
mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL));
mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL,
POLL_REASON_REG_CALLBACK));
return normalizedRequest;
}
@@ -1999,7 +2032,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
new TetheringManager.TetheringEventCallback() {
@Override
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) {
// on background handler thread, and verified UPDATE_DEVICE_STATS
// permission above.
performPoll(FLAG_PERSIST_ALL);
performPoll(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_PERIODIC));
// verify that we're watching global alert
registerGlobalAlert();
@@ -2072,19 +2106,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void handleOnCollapsedRatTypeChanged() {
// Protect service from frequently updating. Remove pending messages if any.
mHandler.removeMessages(MSG_NOTIFY_NETWORK_STATUS);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_NOTIFY_NETWORK_STATUS), mSettings.getPollDelay());
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_NOTIFY_NETWORK_STATUS,
POLL_REASON_RAT_CHANGED), mSettings.getPollDelay());
}
private void handleNotifyNetworkStatus(
Network[] defaultNetworks,
NetworkStateSnapshot[] snapshots,
String activeIface) {
String activeIface,
@Nullable PollEvent event) {
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
mActiveIface = activeIface;
handleNotifyNetworkStatusLocked(defaultNetworks, snapshots);
handleNotifyNetworkStatusLocked(defaultNetworks, snapshots, event);
} finally {
mWakeLock.release();
}
@@ -2098,7 +2133,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
*/
@GuardedBy("mStatsLock")
private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
@NonNull NetworkStateSnapshot[] snapshots) {
@NonNull NetworkStateSnapshot[] snapshots, @Nullable PollEvent event) {
if (!mSystemReady) return;
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
// will be persisted during next alarm poll event.
performPollLocked(FLAG_PERSIST_NETWORK);
performPollLocked(FLAG_PERSIST_NETWORK, event);
// Rebuild active interfaces based on connected networks
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) {
mWakeLock.acquire();
try {
performPollLocked(flags);
performPollLocked(flags, event);
} finally {
mWakeLock.release();
}
@@ -2342,11 +2377,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* {@link NetworkStatsHistory}.
*/
@GuardedBy("mStatsLock")
private void performPollLocked(int flags) {
private void performPollLocked(int flags, @Nullable PollEvent event) {
if (!mSystemReady) return;
if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
if (mSupportEventLogger) {
mEventLogger.logPollEvent(flags, event);
}
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
final boolean persistUid = (flags & FLAG_PERSIST_UID) != 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));
// Perform one last poll before removing
performPollLocked(FLAG_PERSIST_ALL);
performPollLocked(FLAG_PERSIST_ALL, maybeCreatePollEvent(POLL_REASON_REMOVE_UIDS));
mUidRecorder.removeUidsLocked(uids);
mUidTagRecorder.removeUidsLocked(uids);
@@ -2629,7 +2668,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
if (poll) {
performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE,
maybeCreatePollEvent(POLL_REASON_DUMPSYS));
pw.println("Forced poll");
return;
}
@@ -2689,6 +2729,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
pw.println("(failed to dump platform legacy stats import counters)");
}
}
pw.println(CONFIG_ENABLE_NETWORK_STATS_EVENT_LOGGER + ": " + mSupportEventLogger);
pw.decreaseIndent();
@@ -2746,6 +2787,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
pw.decreaseIndent();
pw.println();
if (mSupportEventLogger) {
mEventLogger.dump(pw);
}
pw.println("Stats Providers:");
pw.increaseIndent();
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> {
@Override
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.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.NETSTATS_IMPORT_ATTEMPTS_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) {
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 */);
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));
}
}