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
This commit is contained in:
@@ -16996,6 +16996,7 @@ package android.net {
|
||||
method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
|
||||
method public deprecated int getNetworkPreference();
|
||||
method public static deprecated android.net.Network getProcessDefaultNetwork();
|
||||
method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
|
||||
method public boolean isActiveNetworkMetered();
|
||||
method public boolean isDefaultNetworkActive();
|
||||
method public static boolean isNetworkTypeValid(int);
|
||||
@@ -17003,6 +17004,7 @@ package android.net {
|
||||
method public void releaseNetworkRequest(android.app.PendingIntent);
|
||||
method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
|
||||
method public deprecated void reportBadNetwork(android.net.Network);
|
||||
method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
|
||||
method public void reportNetworkConnectivity(android.net.Network, boolean);
|
||||
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
|
||||
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
|
||||
@@ -17013,8 +17015,10 @@ package android.net {
|
||||
method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
|
||||
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
|
||||
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
|
||||
field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
|
||||
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
|
||||
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
|
||||
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
|
||||
field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
|
||||
field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
|
||||
field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
|
||||
|
||||
@@ -18257,6 +18257,7 @@ package android.net {
|
||||
method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
|
||||
method public deprecated int getNetworkPreference();
|
||||
method public static deprecated android.net.Network getProcessDefaultNetwork();
|
||||
method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
|
||||
method public boolean isActiveNetworkMetered();
|
||||
method public boolean isDefaultNetworkActive();
|
||||
method public static boolean isNetworkTypeValid(int);
|
||||
@@ -18264,6 +18265,7 @@ package android.net {
|
||||
method public void releaseNetworkRequest(android.app.PendingIntent);
|
||||
method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
|
||||
method public deprecated void reportBadNetwork(android.net.Network);
|
||||
method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
|
||||
method public void reportNetworkConnectivity(android.net.Network, boolean);
|
||||
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
|
||||
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
|
||||
@@ -18274,8 +18276,10 @@ package android.net {
|
||||
method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
|
||||
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
|
||||
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
|
||||
field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
|
||||
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
|
||||
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
|
||||
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
|
||||
field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
|
||||
field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
|
||||
field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
|
||||
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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}.
|
||||
* <p/>
|
||||
* 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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
<protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
|
||||
<protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
|
||||
|
||||
<protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" />
|
||||
<protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
<protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
|
||||
<protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" />
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
|
||||
|
||||
<application android:label="@string/app_name" >
|
||||
<activity
|
||||
@@ -28,9 +29,8 @@
|
||||
android:label="@string/action_bar_label"
|
||||
android:theme="@style/AppTheme" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_SEND"/>
|
||||
<action android:name="android.net.conn.CAPTIVE_PORTAL"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
@@ -58,24 +58,13 @@ public class CaptivePortalLoginActivity extends Activity {
|
||||
private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
|
||||
private static final int SOCKET_TIMEOUT_MS = 10000;
|
||||
|
||||
// Keep this in sync with NetworkMonitor.
|
||||
// Intent broadcast to ConnectivityService indicating sign-in is complete.
|
||||
// Extras:
|
||||
// EXTRA_TEXT = netId
|
||||
// LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
|
||||
// RESPONSE_TOKEN = data fragment from launching Intent
|
||||
private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
|
||||
"android.net.netmon.captive_portal_logged_in";
|
||||
private static final String LOGGED_IN_RESULT = "result";
|
||||
private static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
|
||||
private static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
|
||||
private static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
|
||||
private static final String RESPONSE_TOKEN = "response_token";
|
||||
private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
|
||||
|
||||
private URL mURL;
|
||||
private int mNetId;
|
||||
private Network mNetwork;
|
||||
private String mResponseToken;
|
||||
private NetworkCallback mNetworkCallback;
|
||||
private ConnectivityManager mCm;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -83,23 +72,19 @@ public class CaptivePortalLoginActivity extends Activity {
|
||||
|
||||
String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
|
||||
if (server == null) server = DEFAULT_SERVER;
|
||||
mCm = ConnectivityManager.from(this);
|
||||
try {
|
||||
mURL = new URL("http", server, "/generate_204");
|
||||
final Uri dataUri = getIntent().getData();
|
||||
if (!dataUri.getScheme().equals("netid")) {
|
||||
throw new MalformedURLException();
|
||||
}
|
||||
mNetId = Integer.parseInt(dataUri.getSchemeSpecificPart());
|
||||
mResponseToken = dataUri.getFragment();
|
||||
} catch (MalformedURLException|NumberFormatException e) {
|
||||
} catch (MalformedURLException e) {
|
||||
// System misconfigured, bail out in a way that at least provides network access.
|
||||
done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
|
||||
Log.e(TAG, "Invalid captive portal URL, server=" + server);
|
||||
done(Result.WANTED_AS_IS);
|
||||
}
|
||||
mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
|
||||
mResponseToken = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN);
|
||||
|
||||
final ConnectivityManager cm = ConnectivityManager.from(this);
|
||||
final Network network = new Network(mNetId);
|
||||
// Also initializes proxy system properties.
|
||||
cm.bindProcessToNetwork(network);
|
||||
mCm.bindProcessToNetwork(mNetwork);
|
||||
|
||||
// Proxy system properties must be initialized before setContentView is called because
|
||||
// setContentView initializes the WebView logic which in turn reads the system properties.
|
||||
@@ -108,7 +93,7 @@ public class CaptivePortalLoginActivity extends Activity {
|
||||
getActionBar().setDisplayShowHomeEnabled(false);
|
||||
|
||||
// Exit app if Network disappears.
|
||||
final NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(network);
|
||||
final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
|
||||
if (networkCapabilities == null) {
|
||||
finish();
|
||||
return;
|
||||
@@ -116,14 +101,14 @@ public class CaptivePortalLoginActivity extends Activity {
|
||||
mNetworkCallback = new NetworkCallback() {
|
||||
@Override
|
||||
public void onLost(Network lostNetwork) {
|
||||
if (network.equals(lostNetwork)) done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
|
||||
if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
|
||||
}
|
||||
};
|
||||
final NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
||||
for (int transportType : networkCapabilities.getTransportTypes()) {
|
||||
builder.addTransportType(transportType);
|
||||
}
|
||||
cm.registerNetworkCallback(builder.build(), mNetworkCallback);
|
||||
mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
|
||||
|
||||
final WebView myWebView = (WebView) findViewById(R.id.webview);
|
||||
myWebView.clearCache(true);
|
||||
@@ -160,15 +145,21 @@ public class CaptivePortalLoginActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
private void done(int result) {
|
||||
private void done(Result result) {
|
||||
if (mNetworkCallback != null) {
|
||||
ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback);
|
||||
mCm.unregisterNetworkCallback(mNetworkCallback);
|
||||
}
|
||||
switch (result) {
|
||||
case DISMISSED:
|
||||
mCm.reportCaptivePortalDismissed(mNetwork, mResponseToken);
|
||||
break;
|
||||
case UNWANTED:
|
||||
mCm.ignoreNetworkWithCaptivePortal(mNetwork, mResponseToken);
|
||||
break;
|
||||
case WANTED_AS_IS:
|
||||
mCm.useNetworkWithCaptivePortal(mNetwork, mResponseToken);
|
||||
break;
|
||||
}
|
||||
Intent intent = new Intent(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetId));
|
||||
intent.putExtra(LOGGED_IN_RESULT, String.valueOf(result));
|
||||
intent.putExtra(RESPONSE_TOKEN, mResponseToken);
|
||||
sendBroadcast(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -192,11 +183,11 @@ public class CaptivePortalLoginActivity extends Activity {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.action_use_network) {
|
||||
done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
|
||||
done(Result.WANTED_AS_IS);
|
||||
return true;
|
||||
}
|
||||
if (id == R.id.action_do_not_use_network) {
|
||||
done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
|
||||
done(Result.UNWANTED);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -225,7 +216,7 @@ public class CaptivePortalLoginActivity extends Activity {
|
||||
if (urlConnection != null) urlConnection.disconnect();
|
||||
}
|
||||
if (httpResponseCode == 204) {
|
||||
done(CAPTIVE_PORTAL_APP_RETURN_APPEASED);
|
||||
done(Result.DISMISSED);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -87,17 +87,6 @@ public class NetworkMonitor extends StateMachine {
|
||||
private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
|
||||
"android.permission.ACCESS_NETWORK_CONDITIONS";
|
||||
|
||||
// Keep these in sync with CaptivePortalLoginActivity.java.
|
||||
// Intent broadcast from CaptivePortalLogin indicating sign-in is complete.
|
||||
// Extras:
|
||||
// EXTRA_TEXT = netId
|
||||
// LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
|
||||
// RESPONSE_TOKEN = data fragment from launching Intent
|
||||
private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
|
||||
"android.net.netmon.captive_portal_logged_in";
|
||||
private static final String LOGGED_IN_RESULT = "result";
|
||||
private static final String RESPONSE_TOKEN = "response_token";
|
||||
|
||||
// After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
|
||||
// The network should be used as a default internet connection. It was found to be:
|
||||
// 1. a functioning network providing internet access, or
|
||||
@@ -170,11 +159,12 @@ public class NetworkMonitor extends StateMachine {
|
||||
|
||||
/**
|
||||
* Message to self indicating captive portal app finished.
|
||||
* arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_APPEASED,
|
||||
* arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
|
||||
* CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
|
||||
* CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS
|
||||
* obj = mCaptivePortalLoggedInResponseToken as String
|
||||
*/
|
||||
private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
|
||||
public static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
|
||||
|
||||
/**
|
||||
* Request ConnectivityService display provisioning notification.
|
||||
@@ -185,26 +175,11 @@ public class NetworkMonitor extends StateMachine {
|
||||
public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10;
|
||||
|
||||
/**
|
||||
* Message to self indicating sign-in app bypassed captive portal.
|
||||
* Message to self indicating sign-in app should be launched.
|
||||
* Sent by mLaunchCaptivePortalAppBroadcastReceiver when the
|
||||
* user touches the sign in notification.
|
||||
*/
|
||||
private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 11;
|
||||
|
||||
/**
|
||||
* Message to self indicating no sign-in app responded.
|
||||
*/
|
||||
private static final int EVENT_NO_APP_RESPONSE = BASE + 12;
|
||||
|
||||
/**
|
||||
* Message to self indicating sign-in app indicates sign-in is not possible.
|
||||
*/
|
||||
private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 13;
|
||||
|
||||
/**
|
||||
* Return codes from captive portal sign-in app.
|
||||
*/
|
||||
public static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
|
||||
public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
|
||||
public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
|
||||
private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
|
||||
|
||||
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
|
||||
// Default to 30s linger time-out.
|
||||
@@ -259,7 +234,7 @@ public class NetworkMonitor extends StateMachine {
|
||||
private final State mCaptivePortalState = new CaptivePortalState();
|
||||
private final State mLingeringState = new LingeringState();
|
||||
|
||||
private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver = null;
|
||||
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
|
||||
private String mCaptivePortalLoggedInResponseToken = null;
|
||||
|
||||
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
|
||||
@@ -323,9 +298,9 @@ public class NetworkMonitor extends StateMachine {
|
||||
return HANDLED;
|
||||
case CMD_NETWORK_DISCONNECTED:
|
||||
if (DBG) log("Disconnected - quitting");
|
||||
if (mCaptivePortalLoggedInBroadcastReceiver != null) {
|
||||
mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver);
|
||||
mCaptivePortalLoggedInBroadcastReceiver = null;
|
||||
if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
|
||||
mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
|
||||
mLaunchCaptivePortalAppBroadcastReceiver = null;
|
||||
}
|
||||
quit();
|
||||
return HANDLED;
|
||||
@@ -336,14 +311,21 @@ public class NetworkMonitor extends StateMachine {
|
||||
transitionTo(mEvaluatingState);
|
||||
return HANDLED;
|
||||
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
|
||||
// Previous token was broadcast, come up with a new one.
|
||||
if (!mCaptivePortalLoggedInResponseToken.equals((String)message.obj))
|
||||
return HANDLED;
|
||||
// Previous token was sent out, come up with a new one.
|
||||
mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong());
|
||||
switch (message.arg1) {
|
||||
case CAPTIVE_PORTAL_APP_RETURN_APPEASED:
|
||||
case CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
|
||||
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_DISMISSED:
|
||||
sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */,
|
||||
0 /* INITIAL_ATTEMPTS */);
|
||||
break;
|
||||
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
|
||||
// TODO: Distinguish this from a network that actually validates.
|
||||
// Displaying the "!" on the system UI icon may still be a good idea.
|
||||
transitionTo(mValidatedState);
|
||||
break;
|
||||
case CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
|
||||
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
|
||||
mUserDoesNotWant = true;
|
||||
// TODO: Should teardown network.
|
||||
transitionTo(mOfflineState);
|
||||
@@ -420,6 +402,25 @@ public class NetworkMonitor extends StateMachine {
|
||||
// Being in the MaybeNotifyState State indicates the user may have been notified that sign-in
|
||||
// is required. This State takes care to clear the notification upon exit from the State.
|
||||
private class MaybeNotifyState extends State {
|
||||
@Override
|
||||
public boolean processMessage(Message message) {
|
||||
if (DBG) log(getName() + message.toString());
|
||||
switch (message.what) {
|
||||
case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
|
||||
final Intent intent = new Intent(
|
||||
ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
|
||||
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mNetworkAgentInfo.network);
|
||||
intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN,
|
||||
mCaptivePortalLoggedInResponseToken);
|
||||
intent.setFlags(
|
||||
Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
|
||||
return HANDLED;
|
||||
default:
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
|
||||
@@ -516,7 +517,9 @@ public class NetworkMonitor extends StateMachine {
|
||||
mContext.registerReceiver(this, new IntentFilter(mAction));
|
||||
}
|
||||
public PendingIntent getPendingIntent() {
|
||||
return PendingIntent.getBroadcast(mContext, 0, new Intent(mAction), 0);
|
||||
final Intent intent = new Intent(mAction);
|
||||
intent.setPackage(mContext.getPackageName());
|
||||
return PendingIntent.getBroadcast(mContext, 0, intent, 0);
|
||||
}
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -524,48 +527,29 @@ public class NetworkMonitor extends StateMachine {
|
||||
}
|
||||
}
|
||||
|
||||
private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
|
||||
mNetworkAgentInfo.network.netId &&
|
||||
mCaptivePortalLoggedInResponseToken.equals(
|
||||
intent.getStringExtra(RESPONSE_TOKEN))) {
|
||||
sendMessage(obtainMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED,
|
||||
Integer.parseInt(intent.getStringExtra(LOGGED_IN_RESULT)), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Being in the CaptivePortalState State indicates a captive portal was detected and the user
|
||||
// has been shown a notification to sign-in.
|
||||
private class CaptivePortalState extends State {
|
||||
private static final String ACTION_LAUNCH_CAPTIVE_PORTAL_APP =
|
||||
"android.net.netmon.launchCaptivePortalApp";
|
||||
|
||||
@Override
|
||||
public void enter() {
|
||||
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
|
||||
NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
|
||||
|
||||
// Assemble Intent to launch captive portal sign-in app.
|
||||
final Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
// Intent cannot use extras because PendingIntent.getActivity will merge matching
|
||||
// Intents erasing extras. Use data instead of extras to encode NetID.
|
||||
intent.setData(Uri.fromParts("netid", Integer.toString(mNetworkAgentInfo.network.netId),
|
||||
mCaptivePortalLoggedInResponseToken));
|
||||
intent.setComponent(new ComponentName("com.android.captiveportallogin",
|
||||
"com.android.captiveportallogin.CaptivePortalLoginActivity"));
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (mCaptivePortalLoggedInBroadcastReceiver == null) {
|
||||
// Create a CustomIntentReceiver that sends us a
|
||||
// CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
|
||||
// touches the notification.
|
||||
if (mLaunchCaptivePortalAppBroadcastReceiver == null) {
|
||||
// Wait for result.
|
||||
mCaptivePortalLoggedInBroadcastReceiver =
|
||||
new CaptivePortalLoggedInBroadcastReceiver();
|
||||
final IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
|
||||
mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter);
|
||||
mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver(
|
||||
ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(),
|
||||
CMD_LAUNCH_CAPTIVE_PORTAL_APP);
|
||||
}
|
||||
// Initiate notification to sign-in.
|
||||
// Display the sign in notification.
|
||||
Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
|
||||
mNetworkAgentInfo.network.netId,
|
||||
PendingIntent.getActivity(mContext, 0, intent, 0));
|
||||
mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent());
|
||||
mConnectivityServiceHandler.sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user