summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-03-09 09:23:38 +0000
committerChiachang Wang <chiachangwang@google.com>2020-03-10 02:13:14 +0000
commitfaac06e3e1412364512e46f38e63c6d2ca7548c1 (patch)
tree4fad8113a7070a3306e668816b072b01619ff607
parent046a14ee618731ad5ef8c6b6c79bf0d84ab00fa6 (diff)
Use redirect URL to start webview
NetworkMonitor will detect captive portal and may get a redirect URL from WiFi AP. Redirect URL should able to send to captive portal app to open the webview instead of detecting again by captive portal app. Bug: 134892996 Test: Manually test with captive portal AP Test: atest NetworkStackTests NetworkStackNextTests Change-Id: Idf363c79b7243a899121be8a68b32d0541dff14f Merged-In: Idf363c79b7243a899121be8a68b32d0541dff14f
-rw-r--r--src/android/net/util/NetworkStackUtils.java30
-rw-r--r--src/com/android/server/connectivity/NetworkMonitor.java33
-rw-r--r--tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java57
3 files changed, 117 insertions, 3 deletions
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java
index d147b45..9d913fc 100644
--- a/src/android/net/util/NetworkStackUtils.java
+++ b/src/android/net/util/NetworkStackUtils.java
@@ -141,6 +141,14 @@ public class NetworkStackUtils {
*/
public static final String DHCP_IP_CONFLICT_DETECT_VERSION = "dhcp_ip_conflict_detect_version";
+ /**
+ * Minimum module version at which to enable dismissal CaptivePortalLogin app in validated
+ * network feature. CaptivePortalLogin app will also use validation facilities in
+ * {@link NetworkMonitor} to perform portal validation if feature is enabled.
+ */
+ public static final String DISMISS_PORTAL_IN_VALIDATED_NETWORK =
+ "dismiss_portal_in_validated_network";
+
static {
System.loadLibrary("networkstackutilsjni");
}
@@ -270,12 +278,32 @@ public class NetworkStackUtils {
*/
public static boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
@NonNull String name) {
+ final int propertyVersion = getDeviceConfigPropertyInt(namespace, name,
+ 0 /* default value */);
+ return isFeatureEnabled(context, namespace, name, false);
+ }
+
+ /**
+ * Check whether or not one specific experimental feature for a particular namespace from
+ * {@link DeviceConfig} is enabled by comparing NetworkStack module version {@link NetworkStack}
+ * with current version of property. If this property version is valid, the corresponding
+ * experimental feature would be enabled, otherwise disabled.
+ * @param context The global context information about an app environment.
+ * @param namespace The namespace containing the property to look up.
+ * @param name The name of the property to look up.
+ * @param defaultEnabled The value to return if the property does not exist or its value is
+ * null.
+ * @return true if this feature is enabled, or false if disabled.
+ */
+ public static boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
+ @NonNull String name, boolean defaultEnabled) {
try {
final int propertyVersion = getDeviceConfigPropertyInt(namespace, name,
0 /* default value */);
final long packageVersion = context.getPackageManager().getPackageInfo(
context.getPackageName(), 0).getLongVersionCode();
- return (propertyVersion != 0 && packageVersion >= (long) propertyVersion);
+ return (propertyVersion == 0 && defaultEnabled)
+ || (propertyVersion != 0 && packageVersion >= (long) propertyVersion);
} catch (NameNotFoundException e) {
Log.e(TAG, "Could not find the package name", e);
return false;
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java
index e914a55..438080c 100644
--- a/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -65,6 +65,7 @@ import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_PROMPT;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USER_AGENT;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
+import static android.net.util.NetworkStackUtils.DISMISS_PORTAL_IN_VALIDATED_NETWORK;
import static android.net.util.NetworkStackUtils.isEmpty;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
@@ -960,7 +961,11 @@ public class NetworkMonitor extends StateMachine {
final Network network = new Network(mCleartextDnsNetwork);
appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
- appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
+ // Use redirect URL from AP if exists.
+ final String portalUrl =
+ (useRedirectUrlForPortal() && probeRes.redirectUrl != null)
+ ? probeRes.redirectUrl : probeRes.detectUrl;
+ appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, portalUrl);
if (probeRes.probeSpec != null) {
final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
@@ -977,6 +982,15 @@ public class NetworkMonitor extends StateMachine {
}
}
+ private boolean useRedirectUrlForPortal() {
+ // It must match the conditions in CaptivePortalLogin in which the redirect URL is not
+ // used to validate that the portal is gone.
+ final boolean aboveQ =
+ ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q);
+ return aboveQ && mDependencies.isFeatureEnabled(mContext, NAMESPACE_CONNECTIVITY,
+ DISMISS_PORTAL_IN_VALIDATED_NETWORK, aboveQ /* defaultEnabled */);
+ }
+
@Override
public void exit() {
if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
@@ -2385,6 +2399,23 @@ public class NetworkMonitor extends StateMachine {
NetworkMonitorUtils.PERMISSION_ACCESS_NETWORK_CONDITIONS);
}
+ /**
+ * Check whether or not one specific experimental feature for a particular namespace from
+ * {@link DeviceConfig} is enabled by comparing NetworkStack module version
+ * {@link NetworkStack} with current version of property. If this property version is valid,
+ * the corresponding experimental feature would be enabled, otherwise disabled.
+ * @param context The global context information about an app environment.
+ * @param namespace The namespace containing the property to look up.
+ * @param name The name of the property to look up.
+ * @param defaultEnabled The value to return if the property does not exist or its value is
+ * null.
+ * @return true if this feature is enabled, or false if disabled.
+ */
+ public boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
+ @NonNull String name, boolean defaultEnabled) {
+ return NetworkStackUtils.isFeatureEnabled(context, namespace, name, defaultEnabled);
+ }
+
public static final Dependencies DEFAULT = new Dependencies();
}
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
index b2492aa..72d50b6 100644
--- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -37,6 +37,7 @@ import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_TCP;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
+import static android.net.util.NetworkStackUtils.DISMISS_PORTAL_IN_VALIDATED_NETWORK;
import static com.android.networkstack.apishim.ConstantsShim.DETECTION_METHOD_DNS_EVENTS;
import static com.android.networkstack.apishim.ConstantsShim.DETECTION_METHOD_TCP_METRICS;
@@ -59,6 +60,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -478,6 +480,7 @@ public class NetworkMonitorTest {
mCreatedNetworkMonitors = new HashSet<>();
mRegisteredReceivers = new HashSet<>();
+ setDismissPortalInValidatedNetwork(false);
}
@After
@@ -1087,7 +1090,7 @@ public class NetworkMonitorTest {
public void testLaunchCaptivePortalApp() throws Exception {
setSslException(mHttpsConnection);
setPortal302(mHttpConnection);
-
+ when(mHttpConnection.getHeaderField(eq("location"))).thenReturn(TEST_LOGIN_URL);
final NetworkMonitor nm = makeMonitor(METERED_CAPABILITIES);
notifyNetworkConnected(nm, METERED_CAPABILITIES);
@@ -1111,6 +1114,9 @@ public class NetworkMonitorTest {
// framework and only intended for the captive portal app, but the framework needs
// the network to identify the right NetworkMonitor.
assertEquals(TEST_NETID, networkCaptor.getValue().netId);
+ // Portal URL should be detection URL.
+ final String redirectUrl = bundle.getString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
+ assertEquals(TEST_HTTP_URL, redirectUrl);
// Have the app report that the captive portal is dismissed, and check that we revalidate.
setStatus(mHttpsConnection, 204);
@@ -1443,6 +1449,50 @@ public class NetworkMonitorTest {
}
@Test
+ public void testDismissPortalInValidatedNetworkEnabledOsSupported() throws Exception {
+ assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
+ testDismissPortalInValidatedNetworkEnabled(TEST_LOGIN_URL);
+ }
+
+ @Test
+ public void testDismissPortalInValidatedNetworkEnabledOsNotSupported() throws Exception {
+ assumeFalse(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q));
+ testDismissPortalInValidatedNetworkEnabled(TEST_HTTP_URL);
+ }
+
+ private void testDismissPortalInValidatedNetworkEnabled(String portalUrl) throws Exception {
+ setDismissPortalInValidatedNetwork(true);
+ setSslException(mHttpsConnection);
+ setPortal302(mHttpConnection);
+ when(mHttpConnection.getHeaderField(eq("location"))).thenReturn(TEST_LOGIN_URL);
+ final NetworkMonitor nm = makeMonitor(METERED_CAPABILITIES);
+ notifyNetworkConnected(nm, METERED_CAPABILITIES);
+
+ verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .showProvisioningNotification(any(), any());
+
+ assertEquals(1, mRegisteredReceivers.size());
+ // Check that startCaptivePortalApp sends the expected intent.
+ nm.launchCaptivePortalApp();
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+ verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
+ verify(mNotifier).notifyCaptivePortalValidationPending(networkCaptor.getValue());
+ final Bundle bundle = bundleCaptor.getValue();
+ final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
+ assertEquals(TEST_NETID, bundleNetwork.netId);
+ // Network is passed both in bundle and as parameter, as the bundle is opaque to the
+ // framework and only intended for the captive portal app, but the framework needs
+ // the network to identify the right NetworkMonitor.
+ assertEquals(TEST_NETID, networkCaptor.getValue().netId);
+ // Portal URL should be redirect URL.
+ final String redirectUrl = bundle.getString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
+ assertEquals(portalUrl, redirectUrl);
+ }
+
+ @Test
public void testEvaluationState_clearProbeResults() throws Exception {
final NetworkMonitor nm = runValidatedNetworkTest();
nm.getEvaluationState().clearProbeResults();
@@ -1602,6 +1652,11 @@ public class NetworkMonitorTest {
eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode);
}
+ private void setDismissPortalInValidatedNetwork(boolean enabled) {
+ when(mDependencies.isFeatureEnabled(any(), any(),
+ eq(DISMISS_PORTAL_IN_VALIDATED_NETWORK), anyBoolean())).thenReturn(enabled);
+ }
+
private void runPortalNetworkTest(int result) {
runNetworkTest(result);
assertEquals(1, mRegisteredReceivers.size());