diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java index cab5d1901a56c..4862f013366fc 100644 --- a/core/java/android/util/LocalLog.java +++ b/core/java/android/util/LocalLog.java @@ -54,4 +54,18 @@ public final class LocalLog { pw.println(itr.next()); } } + + public static class ReadOnlyLocalLog { + private final LocalLog mLog; + ReadOnlyLocalLog(LocalLog log) { + mLog = log; + } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mLog.dump(fd, pw, args); + } + } + + public ReadOnlyLocalLog readOnlyLocalLog() { + return new ReadOnlyLocalLog(this); + } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 25d4d5ec01e0e..84bcf66267e6c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -92,6 +92,9 @@ import android.security.Credentials; import android.security.KeyStore; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.LocalLog; +import android.util.LocalLog.ReadOnlyLocalLog; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -139,6 +142,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -414,6 +418,20 @@ public class ConnectivityService extends IConnectivityManager.Stub // sequence number of NetworkRequests private int mNextNetworkRequestId = 1; + // Array of tracking network validation and results + private static final int MAX_VALIDATION_LOGS = 10; + private final ArrayDeque> mValidationLogs = + new ArrayDeque>(MAX_VALIDATION_LOGS); + + private void addValidationLogs(ReadOnlyLocalLog log, Network network) { + synchronized(mValidationLogs) { + while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) { + mValidationLogs.removeLast(); + } + mValidationLogs.addFirst(new Pair(network, log)); + } + } + /** * Implements support for the legacy "one network per network type" model. * @@ -1715,11 +1733,9 @@ public class ConnectivityService extends IConnectivityManager.Stub return ret; } - private boolean shouldPerformDiagnostics(String[] args) { + private boolean argsContain(String[] args, String target) { for (String arg : args) { - if (arg.equals("--diag")) { - return true; - } + if (arg.equals(target)) return true; } return false; } @@ -1737,7 +1753,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } final List netDiags = new ArrayList(); - if (shouldPerformDiagnostics(args)) { + if (argsContain(args, "--diag")) { final long DIAG_TIME_MS = 5000; for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { // Start gathering diagnostic information. @@ -1824,6 +1840,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } pw.decreaseIndent(); } + + if (argsContain(args, "--short") == false) { + pw.println(); + synchronized (mValidationLogs) { + pw.println("mValidationLogs (most recent first):"); + for (Pair p : mValidationLogs) { + pw.println(p.first); + pw.increaseIndent(); + p.second.dump(fd, pw, args); + pw.decreaseIndent(); + } + } + } } private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) { @@ -3814,6 +3843,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (this) { nai.networkMonitor.systemReady = mSystemReady; } + addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network); if (DBG) log("registerNetworkAgent " + nai); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); return nai.network.netId; diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index 99a0567ef406c..310e3615b16c8 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -47,6 +47,8 @@ import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellInfoWcdma; import android.telephony.TelephonyManager; +import android.util.LocalLog; +import android.util.LocalLog.ReadOnlyLocalLog; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -232,6 +234,8 @@ public class NetworkMonitor extends StateMachine { private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null; private String mCaptivePortalLoggedInResponseToken = null; + private final LocalLog validationLogs = new LocalLog(20); // 20 lines + public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest) { // Add suffix indicating which NetworkMonitor we're talking about. @@ -272,6 +276,15 @@ public class NetworkMonitor extends StateMachine { Log.d(TAG + "/" + mNetworkAgentInfo.name(), s); } + private void validationLog(String s) { + if (DBG) log(s); + validationLogs.log(s); + } + + public ReadOnlyLocalLog getValidationLogs() { + return validationLogs.readOnlyLocalLog(); + } + // DefaultState is the parent of all States. It exists only to handle CMD_* messages but // does not entail any real state (hence no enter() or exit() routines). private class DefaultState extends State { @@ -649,10 +662,8 @@ public class NetworkMonitor extends StateMachine { fetchPac = true; } } - if (DBG) { - log("Checking " + url.toString() + " on " + - mNetworkAgentInfo.networkInfo.getExtraInfo()); - } + validationLog("Checking " + url.toString() + " on " + + mNetworkAgentInfo.networkInfo.getExtraInfo()); urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url); urlConnection.setInstanceFollowRedirects(fetchPac); urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); @@ -668,10 +679,8 @@ public class NetworkMonitor extends StateMachine { long responseTimestamp = SystemClock.elapsedRealtime(); httpResponseCode = urlConnection.getResponseCode(); - if (DBG) { - log("isCaptivePortal: ret=" + httpResponseCode + - " headers=" + urlConnection.getHeaderFields()); - } + validationLog("isCaptivePortal: ret=" + httpResponseCode + + " headers=" + urlConnection.getHeaderFields()); // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive // portal. The only example of this seen so far was a captive portal. For // the time being go with prior behavior of assuming it's not a captive @@ -684,12 +693,12 @@ public class NetworkMonitor extends StateMachine { // sign-in to an empty page. Probably the result of a broken transparent proxy. // See http://b/9972012. if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) { - if (DBG) log("Empty 200 response interpreted as 204 response."); + validationLog("Empty 200 response interpreted as 204 response."); httpResponseCode = 204; } if (httpResponseCode == 200 && fetchPac) { - if (DBG) log("PAC fetch 200 response interpreted as 204 response."); + validationLog("PAC fetch 200 response interpreted as 204 response."); httpResponseCode = 204; } @@ -697,7 +706,7 @@ public class NetworkMonitor extends StateMachine { httpResponseCode != 204 /* isCaptivePortal */, requestTimestamp, responseTimestamp); } catch (IOException e) { - if (DBG) log("Probably not a portal: exception " + e); + validationLog("Probably not a portal: exception " + e); if (httpResponseCode == 599) { // TODO: Ping gateway and DNS server and log results. }