Add static IpClient logging
Support keeping IpClient logs around and dumping them
during dumpsys. Previously we got this benefit for
wifi by virtue of WifiStateMachine's long-lived nature.
Now that this is changing we need to be sure we have
logs, and this method gets us Ethernet logs as well.
Bug: 62476366
Bug: 77999594
Test: as follows
- built
- flashed
- booted
- runtest frameworks-net passes
- dumpsys connmetrics [ipclient] works
Change-Id: I1136a83de8097fdb4130debe1eaf689be7132fe5
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9994462..72f9d74 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -132,6 +132,7 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -1992,13 +1993,6 @@
return ret;
}
- private boolean argsContain(String[] args, String target) {
- for (String arg : args) {
- if (target.equals(arg)) return true;
- }
- return false;
- }
-
private void dumpNetworkDiagnostics(IndentingPrintWriter pw) {
final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>();
final long DIAG_TIME_MS = 5000;
@@ -2027,10 +2021,10 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
if (asProto) return;
- if (argsContain(args, DIAG_ARG)) {
+ if (ArrayUtils.contains(args, DIAG_ARG)) {
dumpNetworkDiagnostics(pw);
return;
- } else if (argsContain(args, TETHERING_ARG)) {
+ } else if (ArrayUtils.contains(args, TETHERING_ARG)) {
mTethering.dump(fd, pw, args);
return;
}
@@ -2098,7 +2092,7 @@
pw.println();
mMultipathPolicyTracker.dump(pw);
- if (argsContain(args, SHORT_ARG) == false) {
+ if (ArrayUtils.contains(args, SHORT_ARG) == false) {
pw.println();
synchronized (mValidationLogs) {
pw.println("mValidationLogs (most recent first):");
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index f427819..ac74598 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -20,6 +20,7 @@
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
+import android.net.ip.IpClient;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
@@ -269,10 +270,12 @@
// Dump the rolling buffer of metrics event and pretty print events using a human readable
// format. Also print network dns/connect statistics and default network event time series.
static final String CMD_LIST = "list";
- // By default any other argument will fall into the default case which is remapped to the
- // "list" command. This includes most notably bug reports collected by dumpsys.cpp with
- // the "-a" argument.
- static final String CMD_DEFAULT = CMD_LIST;
+ // Dump all IpClient logs ("ipclient").
+ static final String CMD_IPCLIENT = IpClient.DUMP_ARG;
+ // By default any other argument will fall into the default case which is the equivalent
+ // of calling both the "list" and "ipclient" commands. This includes most notably bug
+ // reports collected by dumpsys.cpp with the "-a" argument.
+ static final String CMD_DEFAULT = "";
@Override
public int logEvent(ConnectivityMetricsEvent event) {
@@ -292,9 +295,20 @@
case CMD_PROTO:
cmdListAsProto(pw);
return;
- case CMD_LIST: // fallthrough
+ case CMD_IPCLIENT: {
+ final String[] ipclientArgs = ((args != null) && (args.length > 1))
+ ? Arrays.copyOfRange(args, 1, args.length)
+ : null;
+ IpClient.dumpAllLogs(pw, ipclientArgs);
+ return;
+ }
+ case CMD_LIST:
+ cmdList(pw);
+ return;
default:
cmdList(pw);
+ pw.println("");
+ IpClient.dumpAllLogs(pw, null);
return;
}
}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index d6999dd..63ae09a 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -54,6 +54,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.IState;
import com.android.internal.util.Preconditions;
@@ -74,6 +75,7 @@
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -100,6 +102,36 @@
private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
private static final SparseArray<String> sWhatToString =
MessageUtils.findMessageNames(sMessageClasses);
+ // Two static concurrent hashmaps of interface name to logging classes.
+ // One holds StateMachine logs and the other connectivity packet logs.
+ private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
+
+ // If |args| is non-empty, assume it's a list of interface names for which
+ // we should print IpClient logs (filter out all others).
+ public static void dumpAllLogs(PrintWriter writer, String[] args) {
+ for (String ifname : sSmLogs.keySet()) {
+ if (!ArrayUtils.isEmpty(args) && !ArrayUtils.contains(args, ifname)) continue;
+
+ writer.println(String.format("--- BEGIN %s ---", ifname));
+
+ final SharedLog smLog = sSmLogs.get(ifname);
+ if (smLog != null) {
+ writer.println("State machine log:");
+ smLog.dump(null, writer, null);
+ }
+
+ writer.println("");
+
+ final LocalLog pktLog = sPktLogs.get(ifname);
+ if (pktLog != null) {
+ writer.println("Connectivity packet log:");
+ pktLog.readOnlyLocalLog().dump(null, writer, null);
+ }
+
+ writer.println(String.format("--- END %s ---", ifname));
+ }
+ }
/**
* Callbacks for handling IpClient events.
@@ -680,8 +712,10 @@
mShutdownLatch = new CountDownLatch(1);
mNwService = deps.getNMS();
- mLog = new SharedLog(MAX_LOG_RECORDS, mTag);
- mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
+ sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
+ mLog = sSmLogs.get(mInterfaceName);
+ sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
+ mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
mMsgStateLogger = new MessageHandlingLogger();
// TODO: Consider creating, constructing, and passing in some kind of