chromium/components/security_interstitials/content/android/java/src/org/chromium/components/security_interstitials/CaptivePortalHelper.java

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.security_interstitials;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;

import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

import org.chromium.base.ContextUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/** Helper class for captive portal related methods on Android. */
@JNINamespace("security_interstitials")
public class CaptivePortalHelper {
    private static final String DEFAULT_PORTAL_CHECK_URL =
            "http://connectivitycheck.gstatic.com/generate_204";

    public static void setCaptivePortalCertificateForTesting(String spkiHash) {
        CaptivePortalHelperJni.get().setCaptivePortalCertificateForTesting(spkiHash);
    }

    public static void setOSReportsCaptivePortalForTesting(boolean osReportsCaptivePortal) {
        CaptivePortalHelperJni.get().setOSReportsCaptivePortalForTesting(osReportsCaptivePortal);
    }

    @CalledByNative
    private static String getCaptivePortalServerUrl() {
        // Since Android N MR2 it is possible that a captive portal was detected with a
        // different URL than getCaptivePortalServerUrl(). By default, Android uses the URL from
        // getCaptivePortalServerUrl() first, but there are also two additional fallback HTTP
        // URLs to probe if the first HTTP probe does not find anything. Using the default URL
        // is acceptable as the return value is only used by the captive portal interstitial.
        try {
            Context context = ContextUtils.getApplicationContext();
            ConnectivityManager connectivityManager =
                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            Method getCaptivePortalServerUrlMethod =
                    connectivityManager.getClass().getMethod("getCaptivePortalServerUrl");
            return (String) getCaptivePortalServerUrlMethod.invoke(connectivityManager);
        } catch (NoSuchMethodException e) {
            // To avoid crashing, return the default portal check URL on Android.
            return DEFAULT_PORTAL_CHECK_URL;
        } catch (IllegalAccessException e) {
            return DEFAULT_PORTAL_CHECK_URL;
        } catch (InvocationTargetException e) {
            return DEFAULT_PORTAL_CHECK_URL;
        }
    }

    @CalledByNative
    private static void reportNetworkConnectivity() {
        // Call reportNetworkConnectivity on all networks, including the current network.
        Context context = ContextUtils.getApplicationContext();
        ConnectivityManager connectivityManager =
                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

        for (Network network : connectivityManager.getAllNetworks()) {
            // Try both true and false for |hasConnectivity|, that's what reportBadNetwork does.
            // See
            // https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/net/ConnectivityManager.java#L2463
            // for details.
            connectivityManager.reportNetworkConnectivity(network, true);
            connectivityManager.reportNetworkConnectivity(network, false);
        }
    }

    private CaptivePortalHelper() {}

    @NativeMethods
    interface Natives {
        void setCaptivePortalCertificateForTesting(String spkiHash);

        void setOSReportsCaptivePortalForTesting(boolean osReportsCaptivePortal);
    }
}