From 60df4aaea6467183067e0ba4c38461da598f050d Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Fri, 27 Feb 2015 22:55:47 -0500 Subject: [PATCH] Add captive portal API. This API allows apps other than the system's CaptivePortalLogin to handle signing in to captive portals. bug:19416463 Change-Id: I27fce5856b635233e6ff66396d50ccabedd76cf5 --- .../java/android/net/ConnectivityManager.java | 114 ++++++++++++++++++ .../android/net/IConnectivityManager.aidl | 2 + .../android/server/ConnectivityService.java | 10 ++ 3 files changed, 126 insertions(+) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index cab893b6e4..7b758bb80f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -109,6 +109,35 @@ public class ConnectivityManager { public static final String CONNECTIVITY_ACTION_IMMEDIATE = "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE"; + /** + * The device has connected to a network that has presented a captive + * portal, which is blocking Internet connectivity. The user was presented + * with a notification that network sign in is required, + * and the user invoked the notification's action indicating they + * desire to sign in to the network. Apps handling this action should + * facilitate signing in to the network. This action includes a + * {@link Network} typed extra called {@link #EXTRA_NETWORK} that represents + * the network presenting the captive portal; all communication with the + * captive portal must be done using this {@code Network} object. + *

+ * When the app handling this action believes the user has signed in to + * the network and the captive portal has been dismissed, the app should call + * {@link #reportCaptivePortalDismissed} so the system can reevaluate the network. + * If reevaluation finds the network no longer subject to a captive portal, + * the network may become the default active data network. + *

+ * When the app handling this action believes the user explicitly wants + * to ignore the captive portal and the network, the app should call + * {@link #ignoreNetworkWithCaptivePortal}. + *

+ * Note that this action includes a {@code String} extra named + * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} that must + * be passed in to {@link #reportCaptivePortalDismissed} and + * {@link #ignoreNetworkWithCaptivePortal}. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL"; + /** * The lookup key for a {@link NetworkInfo} object. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. @@ -171,6 +200,15 @@ public class ConnectivityManager { */ public static final String EXTRA_INET_CONDITION = "inetCondition"; + /** + * The lookup key for a string that is sent out with + * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}. This string must be + * passed in to {@link #reportCaptivePortalDismissed} and + * {@link #ignoreNetworkWithCaptivePortal}. Retrieve it with + * {@link android.content.Intent#getStringExtra(String)}. + */ + public static final String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken"; + /** * Broadcast action to indicate the change of data activity status * (idle or active) on a network in a recent period. @@ -1751,6 +1789,82 @@ public class ConnectivityManager { } } + /** {@hide} */ + public static final int CAPTIVE_PORTAL_APP_RETURN_DISMISSED = 0; + /** {@hide} */ + public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1; + /** {@hide} */ + public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2; + + /** + * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} + * action to indicate to the system that the captive portal has been + * dismissed. In response the framework will re-evaluate the network's + * connectivity and might take further action thereafter. + * + * @param network The {@link Network} object passed via + * {@link #EXTRA_NETWORK} with the + * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action. + * @param actionToken The {@code String} passed via + * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the + * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action. + */ + public void reportCaptivePortalDismissed(Network network, String actionToken) { + try { + mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_DISMISSED, + actionToken); + } catch (RemoteException e) { + } + } + + /** + * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} + * action to indicate that the user does not want to pursue signing in to + * captive portal and the system should continue to prefer other networks + * without captive portals for use as the default active data network. The + * system will not retest the network for a captive portal so as to avoid + * disturbing the user with further sign in to network notifications. + * + * @param network The {@link Network} object passed via + * {@link #EXTRA_NETWORK} with the + * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action. + * @param actionToken The {@code String} passed via + * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the + * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action. + */ + public void ignoreNetworkWithCaptivePortal(Network network, String actionToken) { + try { + mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_UNWANTED, + actionToken); + } catch (RemoteException e) { + } + } + + /** + * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} + * action to indicate the user wants to use this network as is, even though + * the captive portal is still in place. The system will treat the network + * as if it did not have a captive portal when selecting the network to use + * as the default active data network. This may result in this network + * becoming the default active data network, which could disrupt network + * connectivity for apps because the captive portal is still in place. + * + * @param network The {@link Network} object passed via + * {@link #EXTRA_NETWORK} with the + * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action. + * @param actionToken The {@code String} passed via + * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the + * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action. + * @hide + */ + public void useNetworkWithCaptivePortal(Network network, String actionToken) { + try { + mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS, + actionToken); + } catch (RemoteException e) { + } + } + /** * Set a network-independent global http proxy. This is not normally what you want * for typical HTTP proxies - they are general network dependent. However if you're diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index e94ee064fa..6e06aa5ebe 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -97,6 +97,8 @@ interface IConnectivityManager void reportNetworkConnectivity(in Network network, boolean hasConnectivity); + void captivePortalAppResponse(in Network network, int response, String actionToken); + ProxyInfo getGlobalProxy(); void setGlobalProxy(in ProxyInfo p); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d83ef6fedb..ffda5a7403 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2493,6 +2493,16 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + public void captivePortalAppResponse(Network network, int response, String actionToken) { + if (response == ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS) { + enforceConnectivityInternalPermission(); + } + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); + if (nai == null) return; + nai.networkMonitor.sendMessage(NetworkMonitor.CMD_CAPTIVE_PORTAL_APP_FINISHED, response, 0, + actionToken); + } + public ProxyInfo getDefaultProxy() { // this information is already available as a world read/writable jvm property // so this API change wouldn't have a benifit. It also breaks the passing