Merge "Support NetworkStatsEventLogger" into main
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user