diff options
author | Scott Lobdell <slobdell@google.com> | 2019-05-31 07:00:01 -0700 |
---|---|---|
committer | Justin DeMartino <jjdemartino@google.com> | 2019-05-31 10:34:42 -0700 |
commit | a97ca47ee3713a075711c311c49e3951264007ef (patch) | |
tree | cdcc9c9489c1d64cff9e4eaab93399da47869b84 /packages/NetworkStack | |
parent | 597b6c904375033bfbccbe7929f8bf005d2b1aed (diff) | |
parent | 4b44886aa462b328d655d41cde02728c1eeee459 (diff) |
Merge QP1A.190530.001
Change-Id: I802b7f465b775163726de0627cf9cb5e6773a190
Diffstat (limited to 'packages/NetworkStack')
-rw-r--r-- | packages/NetworkStack/Android.bp | 4 | ||||
-rw-r--r-- | packages/NetworkStack/AndroidManifest.xml | 3 | ||||
-rw-r--r-- | packages/NetworkStack/AndroidManifestBase.xml | 4 | ||||
-rw-r--r-- | packages/NetworkStack/AndroidManifest_InProcess.xml | 4 | ||||
-rw-r--r-- | packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java | 166 | ||||
-rw-r--r-- | packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java | 52 | ||||
-rw-r--r-- | packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java | 33 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/Android.bp (renamed from packages/NetworkStack/tests/Android.bp) | 4 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/AndroidManifest.xml (renamed from packages/NetworkStack/tests/AndroidManifest.xml) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/AndroidTest.xml (renamed from packages/NetworkStack/tests/AndroidTest.xml) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/jni/apf_jni.cpp (renamed from packages/NetworkStack/tests/jni/apf_jni.cpp) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/res/raw/apf.pcap (renamed from packages/NetworkStack/tests/res/raw/apf.pcap) | bin | 5702 -> 5702 bytes | |||
-rw-r--r-- | packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap (renamed from packages/NetworkStack/tests/res/raw/apfPcap.pcap) | bin | 101547 -> 101547 bytes | |||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java (renamed from packages/NetworkStack/tests/src/android/net/apf/ApfTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java (renamed from packages/NetworkStack/tests/src/android/net/apf/Bpf2Apf.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java (renamed from packages/NetworkStack/tests/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java (renamed from packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java (renamed from packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java (renamed from packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java (renamed from packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java (renamed from packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java (renamed from packages/NetworkStack/tests/src/android/net/ip/IpReachabilityMonitorTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java (renamed from packages/NetworkStack/tests/src/android/net/util/ConnectivityPacketSummaryTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java (renamed from packages/NetworkStack/tests/src/android/net/util/PacketReaderTest.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java (renamed from packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java) | 228 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java (renamed from packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java) | 149 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java (renamed from packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java) | 0 | ||||
-rw-r--r-- | packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java (renamed from packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java) | 0 |
28 files changed, 524 insertions, 123 deletions
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp index aefa882f0ce8..c013b8c09ed6 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -42,8 +42,8 @@ android_library { static_libs: [ "androidx.annotation_annotation", "ipmemorystore-client", - "netd_aidl_interface-java", - "networkstack-aidl-interfaces-java", + "netd_aidl_interface-V2-java", + "networkstack-aidl-interfaces-V3-java", "datastallprotosnano", "networkstackprotosnano", "captiveportal-lib", diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index 4c4448482e03..6166c66e0f42 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -49,5 +49,8 @@ <action android:name="android.net.INetworkStackConnector"/> </intent-filter> </service> + <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService" + android:permission="android.permission.BIND_JOB_SERVICE" > + </service> </application> </manifest> diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml index d00a55143605..69a4da46e088 100644 --- a/packages/NetworkStack/AndroidManifestBase.xml +++ b/packages/NetworkStack/AndroidManifestBase.xml @@ -25,9 +25,5 @@ android:defaultToDeviceProtectedStorage="true" android:directBootAware="true" android:usesCleartextTraffic="true"> - - <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService" - android:permission="android.permission.BIND_JOB_SERVICE" > - </service> </application> </manifest> diff --git a/packages/NetworkStack/AndroidManifest_InProcess.xml b/packages/NetworkStack/AndroidManifest_InProcess.xml index 275cd02a6cc0..2778a2a5900b 100644 --- a/packages/NetworkStack/AndroidManifest_InProcess.xml +++ b/packages/NetworkStack/AndroidManifest_InProcess.xml @@ -27,5 +27,9 @@ <action android:name="android.net.INetworkStackConnector.InProcess"/> </intent-filter> </service> + <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService" + android:process="system" + android:permission="android.permission.BIND_JOB_SERVICE" > + </service> </application> </manifest> diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 8e9350d8cbbc..4e40ba4e2d46 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -24,8 +24,13 @@ import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.DnsResolver.FLAG_EMPTY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; @@ -69,7 +74,6 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.DnsResolver; -import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.LinkProperties; import android.net.Network; @@ -277,7 +281,7 @@ public class NetworkMonitor extends StateMachine { private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5; // Delay between reevaluations once a captive portal has been found. private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000; - + private static final int NETWORK_VALIDATION_RESULT_INVALID = 0; private String mPrivateDnsProviderHostname = ""; private final Context mContext; @@ -348,7 +352,8 @@ public class NetworkMonitor extends StateMachine { private long mLastProbeTime; // Set to true if data stall is suspected and reset to false after metrics are sent to statsd. private boolean mCollectDataStallMetrics; - private boolean mAcceptPartialConnectivity; + private boolean mAcceptPartialConnectivity = false; + private final EvaluationState mEvaluationState = new EvaluationState(); public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, SharedLog validationLog) { @@ -601,7 +606,8 @@ public class NetworkMonitor extends StateMachine { case APP_RETURN_UNWANTED: mDontDisplaySigninNotification = true; mUserDoesNotWant = true; - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null); + mEvaluationState.reportEvaluationResult( + NETWORK_VALIDATION_RESULT_INVALID, null); // TODO: Should teardown network. mUidResponsibleForReeval = 0; transitionTo(mEvaluatingState); @@ -653,7 +659,7 @@ public class NetworkMonitor extends StateMachine { // re-evaluating and get the result of partial connectivity, ProbingState will // disable HTTPS probe and transition to EvaluatingPrivateDnsState. case EVENT_ACCEPT_PARTIAL_CONNECTIVITY: - mAcceptPartialConnectivity = true; + maybeDisableHttpsProbing(true /* acceptPartial */); break; case EVENT_LINK_PROPERTIES_CHANGED: mLinkProperties = (LinkProperties) message.obj; @@ -677,7 +683,14 @@ public class NetworkMonitor extends StateMachine { public void enter() { maybeLogEvaluationResult( networkEventType(validationStage(), EvaluationResult.VALIDATED)); - notifyNetworkTested(INetworkMonitor.NETWORK_TEST_RESULT_VALID, null); + // If the user has accepted partial connectivity and HTTPS probing is disabled, then + // mark the network as validated and partial so that settings can keep informing the + // user that the connection is limited. + int result = NETWORK_VALIDATION_RESULT_VALID; + if (!mUseHttps && mAcceptPartialConnectivity) { + result |= NETWORK_VALIDATION_RESULT_PARTIAL; + } + mEvaluationState.reportEvaluationResult(result, null /* redirectUrl */); mValidations++; } @@ -820,6 +833,9 @@ public class NetworkMonitor extends StateMachine { } mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS; mEvaluateAttempts = 0; + // Reset all current probe results to zero, but retain current validation state until + // validation succeeds or fails. + mEvaluationState.clearProbeResults(); } @Override @@ -875,8 +891,7 @@ public class NetworkMonitor extends StateMachine { // 1. Network is connected and finish the network validation. // 2. NetworkMonitor detects network is partial connectivity and user accepts it. case EVENT_ACCEPT_PARTIAL_CONNECTIVITY: - mAcceptPartialConnectivity = true; - mUseHttps = false; + maybeDisableHttpsProbing(true /* acceptPartial */); transitionTo(mEvaluatingPrivateDnsState); return HANDLED; default: @@ -1019,6 +1034,8 @@ public class NetworkMonitor extends StateMachine { mPrivateDnsConfig = null; validationLog("Strict mode hostname resolution failed: " + uhe.getMessage()); } + mEvaluationState.reportProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS, + (mPrivateDnsConfig != null) /* succeeded */); } private void notifyPrivateDnsConfigResolved() { @@ -1030,13 +1047,18 @@ public class NetworkMonitor extends StateMachine { } private void handlePrivateDnsEvaluationFailure() { - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null); - + mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID, + null /* redirectUrl */); // Queue up a re-evaluation with backoff. // // TODO: Consider abandoning this state after a few attempts and // transitioning back to EvaluatingState, to perhaps give ourselves // the opportunity to (re)detect a captive portal or something. + // + // TODO: distinguish between CMD_EVALUATE_PRIVATE_DNS messages that are caused by server + // lookup failures (which should continue to do exponential backoff) and + // CMD_EVALUATE_PRIVATE_DNS messages that are caused by user reconfiguration (which + // should be processed immediately. sendMessageDelayed(CMD_EVALUATE_PRIVATE_DNS, mPrivateDnsReevalDelayMs); mPrivateDnsReevalDelayMs *= 2; if (mPrivateDnsReevalDelayMs > MAX_REEVALUATE_DELAY_MS) { @@ -1050,21 +1072,22 @@ public class NetworkMonitor extends StateMachine { final String host = UUID.randomUUID().toString().substring(0, 8) + oneTimeHostnameSuffix; final Stopwatch watch = new Stopwatch().start(); + boolean success = false; + long time; try { final InetAddress[] ips = mNetwork.getAllByName(host); - final long time = watch.stop(); + time = watch.stop(); final String strIps = Arrays.toString(ips); - final boolean success = (ips != null && ips.length > 0); + success = (ips != null && ips.length > 0); validationLog(PROBE_PRIVDNS, host, String.format("%dms: %s", time, strIps)); - logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE); - return success; } catch (UnknownHostException uhe) { - final long time = watch.stop(); + time = watch.stop(); validationLog(PROBE_PRIVDNS, host, String.format("%dms - Error: %s", time, uhe.getMessage())); - logValidationProbe(time, PROBE_PRIVDNS, DNS_FAILURE); } - return false; + logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE); + mEvaluationState.reportProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS, success); + return success; } } @@ -1106,22 +1129,24 @@ public class NetworkMonitor extends StateMachine { // state (even if no Private DNS validation required). transitionTo(mEvaluatingPrivateDnsState); } else if (probeResult.isPortal()) { - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl); + mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID, + probeResult.redirectUrl); mLastPortalProbeResult = probeResult; transitionTo(mCaptivePortalState); } else if (probeResult.isPartialConnectivity()) { - logNetworkEvent(NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY); - notifyNetworkTested(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY, - probeResult.redirectUrl); + mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_PARTIAL, + null /* redirectUrl */); + // Check if disable https probing needed. + maybeDisableHttpsProbing(mAcceptPartialConnectivity); if (mAcceptPartialConnectivity) { - mUseHttps = false; transitionTo(mEvaluatingPrivateDnsState); } else { transitionTo(mWaitingForNextProbeState); } } else { logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED); - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl); + mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID, + null /* redirectUrl */); transitionTo(mWaitingForNextProbeState); } return HANDLED; @@ -1469,10 +1494,13 @@ public class NetworkMonitor extends StateMachine { final CaptivePortalProbeResult result; if (pacUrl != null) { result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC); + reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result); } else if (mUseHttps) { + // Probe results are reported inside sendParallelHttpProbes. result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl); } else { result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP); + reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result); } long endTime = SystemClock.elapsedRealtime(); @@ -1484,6 +1512,7 @@ public class NetworkMonitor extends StateMachine { log("isCaptivePortal: isSuccessful()=" + result.isSuccessful() + " isPortal()=" + result.isPortal() + " RedirectUrl=" + result.redirectUrl + + " isPartialConnectivity()=" + result.isPartialConnectivity() + " Time=" + (endTime - startTime) + "ms"); return result; @@ -1498,6 +1527,10 @@ public class NetworkMonitor extends StateMachine { // Only do this if HttpURLConnection is about to, to avoid any potentially // unnecessary resolution. final String host = (proxy != null) ? proxy.getHost() : url.getHost(); + // This method cannot safely report probe results because it might not be running on the + // state machine thread. Reporting results here would cause races and potentially send + // information to callers that does not make sense because the state machine has already + // changed state. sendDnsProbe(host); return sendHttpProbe(url, probeType, null); } @@ -1682,10 +1715,12 @@ public class NetworkMonitor extends StateMachine { // Look for a conclusive probe result first. if (httpResult.isPortal()) { + reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, httpResult); return httpResult; } // httpsResult.isPortal() is not expected, but check it nonetheless. if (httpsResult.isPortal() || httpsResult.isSuccessful()) { + reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTPS, httpsResult); return httpsResult; } // If a fallback method exists, use it to retry portal detection. @@ -1695,6 +1730,7 @@ public class NetworkMonitor extends StateMachine { CaptivePortalProbeResult fallbackProbeResult = null; if (fallbackUrl != null) { fallbackProbeResult = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec); + reportHttpProbeResult(NETWORK_VALIDATION_PROBE_FALLBACK, fallbackProbeResult); if (fallbackProbeResult.isPortal()) { return fallbackProbeResult; } @@ -1702,10 +1738,15 @@ public class NetworkMonitor extends StateMachine { // Otherwise wait until http and https probes completes and use their results. try { httpProbe.join(); + reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, httpProbe.result()); + if (httpProbe.result().isPortal()) { return httpProbe.result(); } + httpsProbe.join(); + reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTPS, httpsProbe.result()); + final boolean isHttpSuccessful = (httpProbe.result().isSuccessful() || (fallbackProbeResult != null && fallbackProbeResult.isSuccessful())); @@ -2024,4 +2065,79 @@ public class NetworkMonitor extends StateMachine { return result; } + + // Class to keep state of evaluation results and probe results. + // The main purpose is to ensure NetworkMonitor can notify ConnectivityService of probe results + // as soon as they happen, without triggering any other changes. This requires keeping state on + // the most recent evaluation result. Calling reportProbeResult will ensure that the results + // reported to ConnectivityService contain the previous evaluation result, and thus won't + // trigger a validation or partial connectivity state change. + @VisibleForTesting + protected class EvaluationState { + // The latest validation result for this network. This is a bitmask of + // INetworkMonitor.NETWORK_VALIDATION_RESULT_* constants. + private int mEvaluationResult = NETWORK_VALIDATION_RESULT_INVALID; + // Indicates which probes have completed since clearProbeResults was called. + // This is a bitmask of INetworkMonitor.NETWORK_VALIDATION_PROBE_* constants. + private int mProbeResults = 0; + // The latest redirect URL. + private String mRedirectUrl; + + protected void clearProbeResults() { + mProbeResults = 0; + } + + // Probe result for http probe should be updated from reportHttpProbeResult(). + protected void reportProbeResult(int probeResult, boolean succeeded) { + if (succeeded) { + mProbeResults |= probeResult; + } else { + mProbeResults &= ~probeResult; + } + notifyNetworkTested(getNetworkTestResult(), mRedirectUrl); + } + + protected void reportEvaluationResult(int result, @Nullable String redirectUrl) { + mEvaluationResult = result; + mRedirectUrl = redirectUrl; + notifyNetworkTested(getNetworkTestResult(), mRedirectUrl); + } + + protected int getNetworkTestResult() { + return mEvaluationResult | mProbeResults; + } + } + + @VisibleForTesting + protected EvaluationState getEvaluationState() { + return mEvaluationState; + } + + private void maybeDisableHttpsProbing(boolean acceptPartial) { + mAcceptPartialConnectivity = acceptPartial; + // Ignore https probe in next validation if user accept partial connectivity on a partial + // connectivity network. + if (((mEvaluationState.getNetworkTestResult() & NETWORK_VALIDATION_RESULT_PARTIAL) != 0) + && mAcceptPartialConnectivity) { + mUseHttps = false; + } + } + + // Report HTTP, HTTP or FALLBACK probe result. + @VisibleForTesting + protected void reportHttpProbeResult(int probeResult, + @NonNull final CaptivePortalProbeResult result) { + boolean succeeded = result.isSuccessful(); + // The success of a HTTP probe does not tell us whether the DNS probe succeeded. + // The DNS and HTTP probes run one after the other in sendDnsAndHttpProbes, and that + // method cannot report the result of the DNS probe because that it could be running + // on a different thread which is racing with the main state machine thread. So, if + // an HTTP or HTTPS probe succeeded, assume that the DNS probe succeeded. But if an + // HTTP or HTTPS probe failed, don't assume that DNS is not working. + // TODO: fix this. + if (succeeded) { + probeResult |= NETWORK_VALIDATION_PROBE_DNS; + } + mEvaluationState.reportProbeResult(probeResult, succeeded); + } } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java index 764e2d07ce3d..a538a5b0e7d8 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java @@ -410,6 +410,7 @@ public class IpMemoryStoreDatabase { private static final String[] DATA_COLUMN = new String[] { PrivateDataContract.COLNAME_DATA }; + @Nullable static byte[] retrieveBlob(@NonNull final SQLiteDatabase db, @NonNull final String key, @NonNull final String clientId, @NonNull final String name) { @@ -432,6 +433,57 @@ public class IpMemoryStoreDatabase { } /** + * Wipe all data in tables when network factory reset occurs. + */ + static void wipeDataUponNetworkReset(@NonNull final SQLiteDatabase db) { + for (int remainingRetries = 3; remainingRetries > 0; --remainingRetries) { + db.beginTransaction(); + try { + db.delete(NetworkAttributesContract.TABLENAME, null, null); + db.delete(PrivateDataContract.TABLENAME, null, null); + final Cursor cursorNetworkAttributes = db.query( + // table name + NetworkAttributesContract.TABLENAME, + // column name + new String[] { NetworkAttributesContract.COLNAME_L2KEY }, + null, // selection + null, // selectionArgs + null, // groupBy + null, // having + null, // orderBy + "1"); // limit + if (0 != cursorNetworkAttributes.getCount()) { + cursorNetworkAttributes.close(); + continue; + } + cursorNetworkAttributes.close(); + final Cursor cursorPrivateData = db.query( + // table name + PrivateDataContract.TABLENAME, + // column name + new String[] { PrivateDataContract.COLNAME_L2KEY }, + null, // selection + null, // selectionArgs + null, // groupBy + null, // having + null, // orderBy + "1"); // limit + if (0 != cursorPrivateData.getCount()) { + cursorPrivateData.close(); + continue; + } + cursorPrivateData.close(); + db.setTransactionSuccessful(); + return; + } catch (SQLiteException e) { + Log.e(TAG, "Could not wipe the data in database", e); + } finally { + db.endTransaction(); + } + } + } + + /** * The following is a horrible hack that is necessary because the Android SQLite API does not * have a way to query a binary blob. This, almost certainly, is an overlook. * diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java index 8312dfeb1a3b..55ab8d4d1376 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java @@ -60,7 +60,6 @@ import java.util.concurrent.Executors; */ public class IpMemoryStoreService extends IIpMemoryStore.Stub { private static final String TAG = IpMemoryStoreService.class.getSimpleName(); - private static final int MAX_CONCURRENT_THREADS = 4; private static final int DATABASE_SIZE_THRESHOLD = 10 * 1024 * 1024; //10MB private static final int MAX_DROP_RECORD_TIMES = 500; private static final int MIN_DELETE_NUM = 5; @@ -107,23 +106,17 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { db = null; } mDb = db; - // The work-stealing thread pool executor will spawn threads as needed up to - // the max only when there is no free thread available. This generally behaves - // exactly like one would expect it intuitively : - // - When work arrives, it will spawn a new thread iff there are no available threads - // - When there is no work to do it will shutdown threads after a while (the while - // being equal to 2 seconds (not configurable) when max threads are spun up and - // twice as much for every one less thread) - // - When all threads are busy the work is enqueued and waits for any worker - // to become available. - // Because the stealing pool is made for very heavily parallel execution of - // small tasks that spawn others, it creates a queue per thread that in this - // case is overhead. However, the three behaviors above make it a superior - // choice to cached or fixedThreadPoolExecutor, neither of which can actually - // enqueue a task waiting for a thread to be free. This can probably be solved - // with judicious subclassing of ThreadPoolExecutor, but that's a lot of dangerous - // complexity for little benefit in this case. - mExecutor = Executors.newWorkStealingPool(MAX_CONCURRENT_THREADS); + // The single thread executor guarantees that all work is executed sequentially on the + // same thread, and no two tasks can be active at the same time. This is required to + // ensure operations from multiple clients don't interfere with each other (in particular, + // operations involving a transaction must not run concurrently with other operations + // as the other operations might be taken as part of the transaction). By default, the + // single thread executor runs off an unbounded queue. + // TODO : investigate replacing this scheme with a scheme where each thread has its own + // instance of the database, as it may be faster. It is likely however that IpMemoryStore + // operations are mostly IO-bound anyway, and additional contention is unlikely to bring + // benefits. Alternatively, a read-write lock might increase throughput. + mExecutor = Executors.newSingleThreadExecutor(); RegularMaintenanceJobService.schedule(mContext, this); } @@ -410,8 +403,12 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { }); } + /** + * Wipe the data in IpMemoryStore database upon network factory reset. + */ @Override public void factoryReset() { + mExecutor.execute(() -> IpMemoryStoreDatabase.wipeDataUponNetworkReset(mDb)); } /** Get db size threshold. */ diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/unit/Android.bp index 039f6bf791fb..6cc80543fc74 100644 --- a/packages/NetworkStack/tests/Android.bp +++ b/packages/NetworkStack/tests/unit/Android.bp @@ -72,7 +72,7 @@ android_test { "libutilscallstack", "libziparchive", "libz", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V2-cpp", ], } @@ -94,7 +94,7 @@ cc_library_shared { "liblog", "libcutils", "libnativehelper", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V2-cpp", ], static_libs: [ "libapf", diff --git a/packages/NetworkStack/tests/AndroidManifest.xml b/packages/NetworkStack/tests/unit/AndroidManifest.xml index 5dcf6ff1b514..5dcf6ff1b514 100644 --- a/packages/NetworkStack/tests/AndroidManifest.xml +++ b/packages/NetworkStack/tests/unit/AndroidManifest.xml diff --git a/packages/NetworkStack/tests/AndroidTest.xml b/packages/NetworkStack/tests/unit/AndroidTest.xml index 047bc2e67808..047bc2e67808 100644 --- a/packages/NetworkStack/tests/AndroidTest.xml +++ b/packages/NetworkStack/tests/unit/AndroidTest.xml diff --git a/packages/NetworkStack/tests/jni/apf_jni.cpp b/packages/NetworkStack/tests/unit/jni/apf_jni.cpp index 4222adf9e06b..4222adf9e06b 100644 --- a/packages/NetworkStack/tests/jni/apf_jni.cpp +++ b/packages/NetworkStack/tests/unit/jni/apf_jni.cpp diff --git a/packages/NetworkStack/tests/res/raw/apf.pcap b/packages/NetworkStack/tests/unit/res/raw/apf.pcap Binary files differindex 963165f19f73..963165f19f73 100644 --- a/packages/NetworkStack/tests/res/raw/apf.pcap +++ b/packages/NetworkStack/tests/unit/res/raw/apf.pcap diff --git a/packages/NetworkStack/tests/res/raw/apfPcap.pcap b/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap Binary files differindex 6f69c4add0f8..6f69c4add0f8 100644 --- a/packages/NetworkStack/tests/res/raw/apfPcap.pcap +++ b/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java index 8f2b96807860..8f2b96807860 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java diff --git a/packages/NetworkStack/tests/src/android/net/apf/Bpf2Apf.java b/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java index 5d57cde22fb1..5d57cde22fb1 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/Bpf2Apf.java +++ b/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java diff --git a/packages/NetworkStack/tests/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java b/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java index f948086ac79b..f948086ac79b 100644 --- a/packages/NetworkStack/tests/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java index 27d725540d34..27d725540d34 100644 --- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java index a30d3e492406..a30d3e492406 100644 --- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java index f0e2f1b8d459..f0e2f1b8d459 100644 --- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java index 57a87a4d3fb1..57a87a4d3fb1 100644 --- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java index 5f8000634ffa..5f8000634ffa 100644 --- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpReachabilityMonitorTest.java b/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java index 64b168ae2b5a..64b168ae2b5a 100644 --- a/packages/NetworkStack/tests/src/android/net/ip/IpReachabilityMonitorTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java diff --git a/packages/NetworkStack/tests/src/android/net/util/ConnectivityPacketSummaryTest.java b/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java index 71be8b38d3fe..71be8b38d3fe 100644 --- a/packages/NetworkStack/tests/src/android/net/util/ConnectivityPacketSummaryTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java diff --git a/packages/NetworkStack/tests/src/android/net/util/PacketReaderTest.java b/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java index 289dcade99a6..289dcade99a6 100644 --- a/packages/NetworkStack/tests/src/android/net/util/PacketReaderTest.java +++ b/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index 26186751c282..262641d1901a 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -17,9 +17,13 @@ package com.android.server.connectivity; import static android.net.CaptivePortal.APP_RETURN_DISMISSED; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD; import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; @@ -98,6 +102,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.mockito.verification.VerificationWithTimeout; import java.io.IOException; import java.net.HttpURLConnection; @@ -149,6 +154,19 @@ public class NetworkMonitorTest { private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204"; private static final String TEST_MCCMNC = "123456"; + private static final int VALIDATION_RESULT_INVALID = 0; + private static final int VALIDATION_RESULT_PORTAL = 0; + private static final String TEST_REDIRECT_URL = "android.com"; + private static final int VALIDATION_RESULT_PARTIAL = NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTP + | NETWORK_VALIDATION_RESULT_PARTIAL; + private static final int VALIDATION_RESULT_FALLBACK_PARTIAL = NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_FALLBACK + | NETWORK_VALIDATION_RESULT_PARTIAL; + private static final int VALIDATION_RESULT_VALID = NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_RESULT_VALID; + private static final int RETURN_CODE_DNS_SUCCESS = 0; private static final int RETURN_CODE_DNS_TIMEOUT = 255; private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5; @@ -297,6 +315,7 @@ public class NetworkMonitorTest { setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL); setFallbackSpecs(null); // Test with no fallback spec by default when(mRandom.nextInt()).thenReturn(0); + // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise, // it will fail the test because of timeout expired for querying AAAA and A sequentially. when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) @@ -471,8 +490,7 @@ public class NetworkMonitorTest { public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException { setSslException(mHttpsConnection); setPortal302(mHttpConnection); - - runPortalNetworkTest(); + runPortalNetworkTest(VALIDATION_RESULT_PORTAL); } @Test @@ -488,8 +506,7 @@ public class NetworkMonitorTest { setSslException(mHttpsConnection); setStatus(mHttpConnection, 500); setPortal302(mFallbackConnection); - - runPortalNetworkTest(); + runPortalNetworkTest(VALIDATION_RESULT_INVALID); } @Test @@ -517,7 +534,7 @@ public class NetworkMonitorTest { when(mRandom.nextInt()).thenReturn(2); // First check always uses the first fallback URL: inconclusive - final NetworkMonitor monitor = runNetworkTest(NETWORK_TEST_RESULT_INVALID); + final NetworkMonitor monitor = runNetworkTest(VALIDATION_RESULT_INVALID); assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); verify(mFallbackConnection, times(1)).getResponseCode(); verify(mOtherFallbackConnection, never()).getResponseCode(); @@ -547,8 +564,7 @@ public class NetworkMonitorTest { setSslException(mHttpsConnection); setStatus(mHttpConnection, 500); setPortal302(mOtherFallbackConnection); - - runPortalNetworkTest(); + runPortalNetworkTest(VALIDATION_RESULT_INVALID); verify(mOtherFallbackConnection, times(1)).getResponseCode(); verify(mFallbackConnection, never()).getResponseCode(); } @@ -571,7 +587,7 @@ public class NetworkMonitorTest { set302(mOtherFallbackConnection, "https://www.google.com/test?q=3"); // HTTPS failed, fallback spec went through -> partial connectivity - runPartialConnectivityNetworkTest(); + runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL); verify(mOtherFallbackConnection, times(1)).getResponseCode(); verify(mFallbackConnection, never()).getResponseCode(); } @@ -580,8 +596,7 @@ public class NetworkMonitorTest { public void testIsCaptivePortal_FallbackSpecIsPortal() throws IOException { setupFallbackSpec(); set302(mOtherFallbackConnection, "http://login.portal.example.com"); - - runPortalNetworkTest(); + runPortalNetworkTest(VALIDATION_RESULT_INVALID); } @Test @@ -590,7 +605,7 @@ public class NetworkMonitorTest { setSslException(mHttpsConnection); setPortal302(mHttpConnection); - runNotPortalNetworkTest(); + runNoValidationNetworkTest(); } @Test @@ -676,7 +691,8 @@ public class NetworkMonitorTest { @Test public void testNoInternetCapabilityValidated() throws Exception { - runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_TEST_RESULT_VALID); + runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_VALIDATION_RESULT_VALID, + getGeneralVerification()); verify(mCleartextDnsNetwork, never()).openConnection(any()); } @@ -712,10 +728,11 @@ public class NetworkMonitorTest { setStatus(mHttpsConnection, 204); setStatus(mHttpConnection, 204); + reset(mCallbacks); nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); - + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) + .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP + | NETWORK_VALIDATION_RESULT_VALID), any()); assertEquals(0, mRegisteredReceivers.size()); } @@ -729,7 +746,8 @@ public class NetworkMonitorTest { wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); + .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), + eq(null)); } @Test @@ -742,38 +760,47 @@ public class NetworkMonitorTest { WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) + .notifyNetworkTested( + eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), + eq(null)); // Fix DNS and retry, expect validation to succeed. reset(mCallbacks); mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}); wnm.forceReevaluation(Process.myUid()); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) + .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), + eq(null)); // Change configuration to an invalid DNS name, expect validation to fail. reset(mCallbacks); mFakeDns.setAnswer("dns.bad", new String[0]); wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0])); + // Strict mode hostname resolve fail. Expect only notification for evaluation fail. No probe + // notification. verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); + .notifyNetworkTested(eq(VALIDATION_RESULT_VALID), eq(null)); // Change configuration back to working again, but make private DNS not work. // Expect validation to fail. reset(mCallbacks); mFakeDns.setNonBypassPrivateDnsWorking(false); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); + wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", + new InetAddress[0])); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) + .notifyNetworkTested( + eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), + eq(null)); // Make private DNS work again. Expect validation to succeed. reset(mCallbacks); mFakeDns.setNonBypassPrivateDnsWorking(true); wnm.forceReevaluation(Process.myUid()); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce()) + .notifyNetworkTested( + eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null)); } @Test @@ -833,12 +860,14 @@ public class NetworkMonitorTest { public void testIgnoreHttpsProbe() throws Exception { setSslException(mHttpsConnection); setStatus(mHttpConnection, 204); + // Expect to send HTTP, HTTPS, FALLBACK probe and evaluation result notifications to CS. + final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PARTIAL); - final NetworkMonitor nm = runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY); - + reset(mCallbacks); nm.setAcceptPartialConnectivity(); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), any()); + // Expect to update evaluation result notifications to CS. + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( + eq(VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID), eq(null)); } @Test @@ -846,12 +875,13 @@ public class NetworkMonitorTest { setStatus(mHttpsConnection, 500); setStatus(mHttpConnection, 204); setStatus(mFallbackConnection, 500); - runPartialConnectivityNetworkTest(); + runPartialConnectivityNetworkTest(VALIDATION_RESULT_PARTIAL); + reset(mCallbacks); setStatus(mHttpsConnection, 500); setStatus(mHttpConnection, 500); setStatus(mFallbackConnection, 204); - runPartialConnectivityNetworkTest(); + runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL); } private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) { @@ -895,6 +925,86 @@ public class NetworkMonitorTest { } } + @Test + public void testNotifyNetwork_WithforceReevaluation() throws Exception { + final NetworkMonitor nm = runValidatedNetworkTest(); + // Verify forceReevalution will not reset the validation result but only probe result until + // getting the validation result. + reset(mCallbacks); + setSslException(mHttpsConnection); + setStatus(mHttpConnection, 500); + setStatus(mFallbackConnection, 204); + nm.forceReevaluation(Process.myUid()); + final ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class); + // Expect to send HTTP, HTTPs, FALLBACK and evaluation results. + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(4)) + .notifyNetworkTested(intCaptor.capture(), any()); + List<Integer> intArgs = intCaptor.getAllValues(); + + // None of these exact values can be known in advance except for intArgs.get(0) because the + // HTTP and HTTPS probes race and the order in which they complete is non-deterministic. + // Thus, check only exact value for intArgs.get(0) and only check the validation result for + // the rest ones. + assertEquals(Integer.valueOf(NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_FALLBACK | NETWORK_VALIDATION_RESULT_VALID), + intArgs.get(0)); + assertTrue((intArgs.get(1) & NETWORK_VALIDATION_RESULT_VALID) != 0); + assertTrue((intArgs.get(2) & NETWORK_VALIDATION_RESULT_VALID) != 0); + assertTrue((intArgs.get(3) & NETWORK_VALIDATION_RESULT_PARTIAL) != 0); + assertTrue((intArgs.get(3) & NETWORK_VALIDATION_RESULT_VALID) == 0); + } + + @Test + public void testEvaluationState_clearProbeResults() throws Exception { + final NetworkMonitor nm = runValidatedNetworkTest(); + nm.getEvaluationState().clearProbeResults(); + // Verify probe results are all reset and only evaluation result left. + assertEquals(NETWORK_VALIDATION_RESULT_VALID, + nm.getEvaluationState().getNetworkTestResult()); + } + + @Test + public void testEvaluationState_reportProbeResult() throws Exception { + final NetworkMonitor nm = runValidatedNetworkTest(); + + reset(mCallbacks); + + nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.SUCCESS); + // Verify result should be appended and notifyNetworkTested callback is triggered once. + assertEquals(nm.getEvaluationState().getNetworkTestResult(), + VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_HTTP); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( + eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_HTTP), any()); + + nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.FAILED); + // Verify DNS probe result should not be cleared. + assertTrue((nm.getEvaluationState().getNetworkTestResult() & NETWORK_VALIDATION_PROBE_DNS) + == NETWORK_VALIDATION_PROBE_DNS); + } + + @Test + public void testEvaluationState_reportEvaluationResult() throws Exception { + final NetworkMonitor nm = runValidatedNetworkTest(); + + nm.getEvaluationState().reportEvaluationResult(NETWORK_VALIDATION_RESULT_PARTIAL, + null /* redirectUrl */); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( + eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null)); + + nm.getEvaluationState().reportEvaluationResult( + NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL, + null /* redirectUrl */); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( + eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null)); + + nm.getEvaluationState().reportEvaluationResult(VALIDATION_RESULT_INVALID, + TEST_REDIRECT_URL); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested( + eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS), + eq(TEST_REDIRECT_URL)); + } + private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { for (int i = 0; i < count; i++) { wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( @@ -953,39 +1063,64 @@ public class NetworkMonitorTest { eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode); } - private void runPortalNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_INVALID); + private void runPortalNetworkTest(int result) { + // The network test event will be triggered twice with the same result. Expect to capture + // the second one with direct url. + runPortalNetworkTest(result, + (VerificationWithTimeout) timeout(HANDLER_TIMEOUT_MS).times(2)); + } + + private void runPortalNetworkTest(int result, VerificationWithTimeout mode) { + runNetworkTest(result, mode); assertEquals(1, mRegisteredReceivers.size()); assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue()); } private void runNotPortalNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_VALID); + runNetworkTest(VALIDATION_RESULT_VALID); + assertEquals(0, mRegisteredReceivers.size()); + assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); + } + + private void runNoValidationNetworkTest() { + runNetworkTest(NETWORK_VALIDATION_RESULT_VALID); assertEquals(0, mRegisteredReceivers.size()); assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); } private void runFailedNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_INVALID); + runNetworkTest(VALIDATION_RESULT_INVALID); assertEquals(0, mRegisteredReceivers.size()); assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); } - private void runPartialConnectivityNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY); + private void runPartialConnectivityNetworkTest(int result) { + runNetworkTest(result); assertEquals(0, mRegisteredReceivers.size()); assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); } + private NetworkMonitor runValidatedNetworkTest() throws Exception { + setStatus(mHttpsConnection, 204); + setStatus(mHttpConnection, 204); + // Expect to send HTTPs and evaluation results. + return runNetworkTest(VALIDATION_RESULT_VALID); + } + private NetworkMonitor runNetworkTest(int testResult) { - return runNetworkTest(METERED_CAPABILITIES, testResult); + return runNetworkTest(METERED_CAPABILITIES, testResult, getGeneralVerification()); + } + + private NetworkMonitor runNetworkTest(int testResult, VerificationWithTimeout mode) { + return runNetworkTest(METERED_CAPABILITIES, testResult, mode); } - private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult) { + private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult, + VerificationWithTimeout mode) { final NetworkMonitor monitor = makeMonitor(nc); monitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc); try { - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) + verify(mCallbacks, mode) .notifyNetworkTested(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture()); } catch (RemoteException e) { fail("Unexpected exception: " + e); @@ -1017,5 +1152,10 @@ public class NetworkMonitorTest { stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */); } } + + private VerificationWithTimeout getGeneralVerification() { + return (VerificationWithTimeout) timeout(HANDLER_TIMEOUT_MS).atLeastOnce(); + } + } diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java index 87346e5d6a28..64fe3a6f8e39 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java +++ b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java @@ -62,6 +62,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -77,7 +78,11 @@ public class IpMemoryStoreServiceTest { private static final int DEFAULT_TIMEOUT_MS = 5000; private static final int LONG_TIMEOUT_MS = 30000; private static final int FAKE_KEY_COUNT = 20; + private static final long LEASE_EXPIRY_NULL = -1L; + private static final int MTU_NULL = -1; private static final String[] FAKE_KEYS; + private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12, + -128, 0, 89, 112, 91, -34 }; static { FAKE_KEYS = new String[FAKE_KEY_COUNT]; for (int i = 0; i < FAKE_KEYS.length; ++i) { @@ -124,6 +129,29 @@ public class IpMemoryStoreServiceTest { mDbFile.delete(); } + /** Helper method to build test network attributes */ + private static NetworkAttributes.Builder buildTestNetworkAttributes( + final Inet4Address ipAddress, final long expiry, final String hint, + final List<InetAddress> dnsServers, final int mtu) { + final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); + if (null != ipAddress) { + na.setAssignedV4Address(ipAddress); + } + if (LEASE_EXPIRY_NULL != expiry) { + na.setAssignedV4AddressExpiry(expiry); + } + if (null != hint) { + na.setGroupHint(hint); + } + if (null != dnsServers) { + na.setDnsAddresses(dnsServers); + } + if (MTU_NULL != mtu) { + na.setMtu(mtu); + } + return na; + } + /** Helper method to make a vanilla IOnStatusListener */ private IOnStatusListener onStatus(Consumer<Status> functor) { return new IOnStatusListener() { @@ -265,7 +293,7 @@ public class IpMemoryStoreServiceTest { } } - // Helper methods to factorize more boilerplate + // Helper method to store network attributes to database private void storeAttributes(final String l2Key, final NetworkAttributes na) { storeAttributes("Did not complete storing attributes", l2Key, na); } @@ -278,15 +306,28 @@ public class IpMemoryStoreServiceTest { }))); } + // Helper method to store blob data to database + private void storeBlobOrFail(final String l2Key, final Blob b, final byte[] data) { + storeBlobOrFail("Did not complete storing private data", l2Key, b, data); + } + private void storeBlobOrFail(final String timeoutMessage, final String l2Key, final Blob b, + final byte[] data) { + b.data = data; + doLatched(timeoutMessage, latch -> mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, + b, onStatus(status -> { + assertTrue("Store status not successful : " + status.resultCode, + status.isSuccess()); + latch.countDown(); + }))); + } + /** Insert large data that db size will be over threshold for maintenance test usage. */ private void insertFakeDataAndOverThreshold() { try { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setGroupHint("hint1"); - na.setMtu(219); - na.setDnsAddresses(Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6"))); - final byte[] data = new byte[]{-3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34}; + final NetworkAttributes.Builder na = buildTestNetworkAttributes( + (Inet4Address) Inet4Address.getByName("1.2.3.4"), LEASE_EXPIRY_NULL, + "hint1", Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6")), + 219); final long time = System.currentTimeMillis() - 1; for (int i = 0; i < 1000; i++) { int errorCode = IpMemoryStoreDatabase.storeNetworkAttributes( @@ -298,7 +339,8 @@ public class IpMemoryStoreServiceTest { assertEquals(errorCode, Status.SUCCESS); errorCode = IpMemoryStoreDatabase.storeBlob( - mService.mDb, "fakeKey" + i, TEST_CLIENT_ID, TEST_DATA_NAME, data); + mService.mDb, "fakeKey" + i, TEST_CLIENT_ID, TEST_DATA_NAME, + TEST_BLOB_DATA); assertEquals(errorCode, Status.SUCCESS); } @@ -320,12 +362,10 @@ public class IpMemoryStoreServiceTest { @Test public void testNetworkAttributes() throws UnknownHostException { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000); - na.setGroupHint("hint1"); - na.setMtu(219); final String l2Key = FAKE_KEYS[0]; + final NetworkAttributes.Builder na = buildTestNetworkAttributes( + (Inet4Address) Inet4Address.getByName("1.2.3.4"), + System.currentTimeMillis() + 7_200_000, "hint1", null, 219); NetworkAttributes attributes = na.build(); storeAttributes(l2Key, attributes); @@ -420,16 +460,9 @@ public class IpMemoryStoreServiceTest { @Test public void testPrivateData() { - final Blob b = new Blob(); - b.data = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 }; final String l2Key = FAKE_KEYS[0]; - doLatched("Did not complete storing private data", latch -> - mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - onStatus(status -> { - assertTrue("Store status not successful : " + status.resultCode, - status.isSuccess()); - latch.countDown(); - }))); + final Blob b = new Blob(); + storeBlobOrFail(l2Key, b, TEST_BLOB_DATA); doLatched("Did not complete retrieving private data", latch -> mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved( @@ -564,11 +597,10 @@ public class IpMemoryStoreServiceTest { @Test public void testIsSameNetwork() throws UnknownHostException { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setGroupHint("hint1"); - na.setMtu(219); - na.setDnsAddresses(Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6"))); + final NetworkAttributes.Builder na = buildTestNetworkAttributes( + (Inet4Address) Inet4Address.getByName("1.2.3.4"), LEASE_EXPIRY_NULL, + "hint1", Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6")), + 219); storeAttributes(FAKE_KEYS[0], na.build()); // 0 and 1 have identical attributes @@ -601,7 +633,6 @@ public class IpMemoryStoreServiceTest { }))); } - @Test public void testFullMaintenance() { insertFakeDataAndOverThreshold(); @@ -660,4 +691,66 @@ public class IpMemoryStoreServiceTest { // still be over the threshold. assertTrue(mService.isDbSizeOverThreshold()); } + + @Test + public void testFactoryReset() throws UnknownHostException { + final String l2Key = FAKE_KEYS[0]; + + // store network attributes + final NetworkAttributes.Builder na = buildTestNetworkAttributes( + (Inet4Address) Inet4Address.getByName("1.2.3.4"), + System.currentTimeMillis() + 7_200_000, "hint1", null, 219); + storeAttributes(l2Key, na.build()); + + // store private data blob + final Blob b = new Blob(); + storeBlobOrFail(l2Key, b, TEST_BLOB_DATA); + + // wipe all data in Database + mService.factoryReset(); + + // retrieved network attributes should be null + doLatched("Did not complete retrieving attributes", latch -> + mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved( + (status, key, attr) -> { + assertTrue("Retrieve network attributes not successful : " + + status.resultCode, status.isSuccess()); + assertEquals(l2Key, key); + assertNull(attr); + latch.countDown(); + }))); + + // retrieved private data blob should be null + doLatched("Did not complete retrieving private data", latch -> + mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved( + (status, key, name, data) -> { + assertTrue("Retrieve blob status not successful : " + status.resultCode, + status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(name, TEST_DATA_NAME); + assertNull(data); + latch.countDown(); + }))); + } + + public void testTasksAreSerial() { + final long sleepTimeMs = 1000; + final long startTime = System.currentTimeMillis(); + mService.retrieveNetworkAttributes("somekey", onNetworkAttributesRetrieved( + (status, key, attr) -> { + assertTrue("Unexpected status : " + status.resultCode, status.isSuccess()); + try { + Thread.sleep(sleepTimeMs); + } catch (InterruptedException e) { + fail("InterruptedException"); + } + })); + doLatched("Serial tasks timing out", latch -> + mService.retrieveNetworkAttributes("somekey", onNetworkAttributesRetrieved( + (status, key, attr) -> { + assertTrue("Unexpected status : " + status.resultCode, + status.isSuccess()); + assertTrue(System.currentTimeMillis() >= startTime + sleepTimeMs); + })), DEFAULT_TIMEOUT_MS); + } } diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java index 3d3aabc66e70..3d3aabc66e70 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java +++ b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java diff --git a/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java index b1db051d2bd8..b1db051d2bd8 100644 --- a/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java +++ b/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java |