am be3736d3: Merge "Don\'t let NetworkMonitor state stop user-initiated transitions." into lmp-mr1-dev
* commit 'be3736d38774210549deb2999bb226d83a29f1ee': Don't let NetworkMonitor state stop user-initiated transitions.
This commit is contained in:
@@ -62,13 +62,19 @@ public class CaptivePortalLoginActivity extends Activity {
|
|||||||
// Intent broadcast to ConnectivityService indicating sign-in is complete.
|
// Intent broadcast to ConnectivityService indicating sign-in is complete.
|
||||||
// Extras:
|
// Extras:
|
||||||
// EXTRA_TEXT = netId
|
// EXTRA_TEXT = netId
|
||||||
// LOGGED_IN_RESULT = "1" if we should use network, "0" if not.
|
// 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 =
|
private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
|
||||||
"android.net.netmon.captive_portal_logged_in";
|
"android.net.netmon.captive_portal_logged_in";
|
||||||
private static final String LOGGED_IN_RESULT = "result";
|
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 URL mURL;
|
private URL mURL;
|
||||||
private int mNetId;
|
private int mNetId;
|
||||||
|
private String mResponseToken;
|
||||||
private NetworkCallback mNetworkCallback;
|
private NetworkCallback mNetworkCallback;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,12 +84,18 @@ public class CaptivePortalLoginActivity extends Activity {
|
|||||||
String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
|
String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
|
||||||
if (server == null) server = DEFAULT_SERVER;
|
if (server == null) server = DEFAULT_SERVER;
|
||||||
try {
|
try {
|
||||||
mURL = new URL("http://" + server + "/generate_204");
|
mURL = new URL("http", server, "/generate_204");
|
||||||
} catch (MalformedURLException e) {
|
final Uri dataUri = getIntent().getData();
|
||||||
done(true);
|
if (!dataUri.getScheme().equals("netid")) {
|
||||||
|
throw new MalformedURLException();
|
||||||
|
}
|
||||||
|
mNetId = Integer.parseInt(dataUri.getSchemeSpecificPart());
|
||||||
|
mResponseToken = dataUri.getFragment();
|
||||||
|
} catch (MalformedURLException|NumberFormatException e) {
|
||||||
|
// System misconfigured, bail out in a way that at least provides network access.
|
||||||
|
done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
|
||||||
}
|
}
|
||||||
|
|
||||||
mNetId = Integer.parseInt(getIntent().getStringExtra(Intent.EXTRA_TEXT));
|
|
||||||
final Network network = new Network(mNetId);
|
final Network network = new Network(mNetId);
|
||||||
ConnectivityManager.setProcessDefaultNetwork(network);
|
ConnectivityManager.setProcessDefaultNetwork(network);
|
||||||
|
|
||||||
@@ -121,7 +133,7 @@ public class CaptivePortalLoginActivity extends Activity {
|
|||||||
mNetworkCallback = new NetworkCallback() {
|
mNetworkCallback = new NetworkCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onLost(Network lostNetwork) {
|
public void onLost(Network lostNetwork) {
|
||||||
if (network.equals(lostNetwork)) done(false);
|
if (network.equals(lostNetwork)) done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
final NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
final NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
||||||
@@ -165,11 +177,14 @@ public class CaptivePortalLoginActivity extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void done(boolean use_network) {
|
private void done(int result) {
|
||||||
ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback);
|
if (mNetworkCallback != null) {
|
||||||
|
ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback);
|
||||||
|
}
|
||||||
Intent intent = new Intent(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
|
Intent intent = new Intent(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetId));
|
intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetId));
|
||||||
intent.putExtra(LOGGED_IN_RESULT, use_network ? "1" : "0");
|
intent.putExtra(LOGGED_IN_RESULT, String.valueOf(result));
|
||||||
|
intent.putExtra(RESPONSE_TOKEN, mResponseToken);
|
||||||
sendBroadcast(intent);
|
sendBroadcast(intent);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
@@ -194,11 +209,11 @@ public class CaptivePortalLoginActivity extends Activity {
|
|||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
if (id == R.id.action_use_network) {
|
if (id == R.id.action_use_network) {
|
||||||
done(true);
|
done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (id == R.id.action_do_not_use_network) {
|
if (id == R.id.action_do_not_use_network) {
|
||||||
done(false);
|
done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
@@ -227,7 +242,7 @@ public class CaptivePortalLoginActivity extends Activity {
|
|||||||
if (urlConnection != null) urlConnection.disconnect();
|
if (urlConnection != null) urlConnection.disconnect();
|
||||||
}
|
}
|
||||||
if (httpResponseCode == 204) {
|
if (httpResponseCode == 204) {
|
||||||
done(true);
|
done(CAPTIVE_PORTAL_APP_RETURN_APPEASED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
|||||||
@@ -45,7 +45,14 @@ public class NetworkAgentInfo {
|
|||||||
public NetworkCapabilities networkCapabilities;
|
public NetworkCapabilities networkCapabilities;
|
||||||
public final NetworkMonitor networkMonitor;
|
public final NetworkMonitor networkMonitor;
|
||||||
public final NetworkMisc networkMisc;
|
public final NetworkMisc networkMisc;
|
||||||
|
// Indicates if netd has been told to create this Network. Once created the appropriate routing
|
||||||
|
// rules are setup and routes are added so packets can begin flowing over the Network.
|
||||||
|
// NOTE: This is a sticky bit; once set it is never cleared.
|
||||||
public boolean created;
|
public boolean created;
|
||||||
|
// Set to true if this Network successfully passed validation or if it did not satisfy the
|
||||||
|
// default NetworkRequest in which case validation will not be attempted.
|
||||||
|
// NOTE: This is a sticky bit; once set it is never cleared even if future validation attempts
|
||||||
|
// fail.
|
||||||
public boolean validated;
|
public boolean validated;
|
||||||
|
|
||||||
// This represents the last score received from the NetworkAgent.
|
// This represents the last score received from the NetworkAgent.
|
||||||
@@ -58,6 +65,9 @@ public class NetworkAgentInfo {
|
|||||||
|
|
||||||
// The list of NetworkRequests being satisfied by this Network.
|
// The list of NetworkRequests being satisfied by this Network.
|
||||||
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
|
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
|
||||||
|
// The list of NetworkRequests that this Network previously satisfied with the highest
|
||||||
|
// score. A non-empty list indicates that if this Network was validated it is lingered.
|
||||||
|
// NOTE: This list is only used for debugging.
|
||||||
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
|
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
|
||||||
|
|
||||||
public final Messenger messenger;
|
public final Messenger messenger;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import android.net.NetworkCapabilities;
|
|||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.TrafficStats;
|
import android.net.TrafficStats;
|
||||||
|
import android.net.Uri;
|
||||||
import android.net.wifi.WifiInfo;
|
import android.net.wifi.WifiInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -59,6 +60,7 @@ import java.io.IOException;
|
|||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@hide}
|
* {@hide}
|
||||||
@@ -88,10 +90,12 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
// Intent broadcast from CaptivePortalLogin indicating sign-in is complete.
|
// Intent broadcast from CaptivePortalLogin indicating sign-in is complete.
|
||||||
// Extras:
|
// Extras:
|
||||||
// EXTRA_TEXT = netId
|
// EXTRA_TEXT = netId
|
||||||
// LOGGED_IN_RESULT = "1" if we should use network, "0" if not.
|
// 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 =
|
private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
|
||||||
"android.net.netmon.captive_portal_logged_in";
|
"android.net.netmon.captive_portal_logged_in";
|
||||||
private static final String LOGGED_IN_RESULT = "result";
|
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.
|
// 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:
|
// The network should be used as a default internet connection. It was found to be:
|
||||||
@@ -163,17 +167,12 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
public static final int CMD_FORCE_REEVALUATION = BASE + 8;
|
public static final int CMD_FORCE_REEVALUATION = BASE + 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to self indicating captive portal login is complete.
|
* Message to self indicating captive portal app finished.
|
||||||
* arg1 = Token to ignore old messages.
|
* arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_APPEASED,
|
||||||
* arg2 = 1 if we should use this network, 0 otherwise.
|
* CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
|
||||||
|
* CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS
|
||||||
*/
|
*/
|
||||||
private static final int CMD_CAPTIVE_PORTAL_LOGGED_IN = BASE + 9;
|
private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
|
||||||
|
|
||||||
/**
|
|
||||||
* Message to self indicating user desires to log into captive portal.
|
|
||||||
* arg1 = Token to ignore old messages.
|
|
||||||
*/
|
|
||||||
private static final int CMD_USER_WANTS_SIGN_IN = BASE + 10;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request ConnectivityService display provisioning notification.
|
* Request ConnectivityService display provisioning notification.
|
||||||
@@ -181,22 +180,29 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
* arg2 = NetID.
|
* arg2 = NetID.
|
||||||
* obj = Intent to be launched when notification selected by user, null if !arg1.
|
* obj = Intent to be launched when notification selected by user, null if !arg1.
|
||||||
*/
|
*/
|
||||||
public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 11;
|
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 bypassed captive portal.
|
||||||
*/
|
*/
|
||||||
private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 12;
|
private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 11;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to self indicating no sign-in app responded.
|
* Message to self indicating no sign-in app responded.
|
||||||
*/
|
*/
|
||||||
private static final int EVENT_NO_APP_RESPONSE = BASE + 13;
|
private static final int EVENT_NO_APP_RESPONSE = BASE + 12;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to self indicating sign-in app indicates sign-in is not possible.
|
* Message to self indicating sign-in app indicates sign-in is not possible.
|
||||||
*/
|
*/
|
||||||
private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 14;
|
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 String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
|
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
|
||||||
// Default to 30s linger time-out.
|
// Default to 30s linger time-out.
|
||||||
@@ -214,9 +220,6 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
private static final int INVALID_UID = -1;
|
private static final int INVALID_UID = -1;
|
||||||
private int mUidResponsibleForReeval = INVALID_UID;
|
private int mUidResponsibleForReeval = INVALID_UID;
|
||||||
|
|
||||||
private int mCaptivePortalLoggedInToken = 0;
|
|
||||||
private int mUserPromptedToken = 0;
|
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final Handler mConnectivityServiceHandler;
|
private final Handler mConnectivityServiceHandler;
|
||||||
private final NetworkAgentInfo mNetworkAgentInfo;
|
private final NetworkAgentInfo mNetworkAgentInfo;
|
||||||
@@ -233,13 +236,16 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
|
|
||||||
public boolean systemReady = false;
|
public boolean systemReady = false;
|
||||||
|
|
||||||
private State mDefaultState = new DefaultState();
|
private final State mDefaultState = new DefaultState();
|
||||||
private State mOfflineState = new OfflineState();
|
private final State mOfflineState = new OfflineState();
|
||||||
private State mValidatedState = new ValidatedState();
|
private final State mValidatedState = new ValidatedState();
|
||||||
private State mEvaluatingState = new EvaluatingState();
|
private final State mMaybeNotifyState = new MaybeNotifyState();
|
||||||
private State mUserPromptedState = new UserPromptedState();
|
private final State mEvaluatingState = new EvaluatingState();
|
||||||
private State mCaptivePortalState = new CaptivePortalState();
|
private final State mCaptivePortalState = new CaptivePortalState();
|
||||||
private State mLingeringState = new LingeringState();
|
private final State mLingeringState = new LingeringState();
|
||||||
|
|
||||||
|
private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver = null;
|
||||||
|
private String mCaptivePortalLoggedInResponseToken = null;
|
||||||
|
|
||||||
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
|
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
|
||||||
NetworkRequest defaultRequest) {
|
NetworkRequest defaultRequest) {
|
||||||
@@ -257,9 +263,9 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
addState(mDefaultState);
|
addState(mDefaultState);
|
||||||
addState(mOfflineState, mDefaultState);
|
addState(mOfflineState, mDefaultState);
|
||||||
addState(mValidatedState, mDefaultState);
|
addState(mValidatedState, mDefaultState);
|
||||||
addState(mEvaluatingState, mDefaultState);
|
addState(mMaybeNotifyState, mDefaultState);
|
||||||
addState(mUserPromptedState, mDefaultState);
|
addState(mEvaluatingState, mMaybeNotifyState);
|
||||||
addState(mCaptivePortalState, mDefaultState);
|
addState(mCaptivePortalState, mMaybeNotifyState);
|
||||||
addState(mLingeringState, mDefaultState);
|
addState(mLingeringState, mDefaultState);
|
||||||
setInitialState(mDefaultState);
|
setInitialState(mDefaultState);
|
||||||
|
|
||||||
@@ -274,14 +280,18 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
|
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
|
||||||
Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
|
Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
|
||||||
|
|
||||||
|
mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong());
|
||||||
|
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void log(String s) {
|
protected void log(String s) {
|
||||||
Log.d(TAG + mNetworkAgentInfo.name(), s);
|
Log.d(TAG + "/" + mNetworkAgentInfo.name(), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultState is the parent of all States. It exists only to handle CMD_* messages but
|
||||||
|
// does not entail any real state (hence no enter() or exit() routines).
|
||||||
private class DefaultState extends State {
|
private class DefaultState extends State {
|
||||||
@Override
|
@Override
|
||||||
public boolean processMessage(Message message) {
|
public boolean processMessage(Message message) {
|
||||||
@@ -297,6 +307,10 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
return HANDLED;
|
return HANDLED;
|
||||||
case CMD_NETWORK_DISCONNECTED:
|
case CMD_NETWORK_DISCONNECTED:
|
||||||
if (DBG) log("Disconnected - quitting");
|
if (DBG) log("Disconnected - quitting");
|
||||||
|
if (mCaptivePortalLoggedInBroadcastReceiver != null) {
|
||||||
|
mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver);
|
||||||
|
mCaptivePortalLoggedInBroadcastReceiver = null;
|
||||||
|
}
|
||||||
quit();
|
quit();
|
||||||
return HANDLED;
|
return HANDLED;
|
||||||
case CMD_FORCE_REEVALUATION:
|
case CMD_FORCE_REEVALUATION:
|
||||||
@@ -304,12 +318,28 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
mUidResponsibleForReeval = message.arg1;
|
mUidResponsibleForReeval = message.arg1;
|
||||||
transitionTo(mEvaluatingState);
|
transitionTo(mEvaluatingState);
|
||||||
return HANDLED;
|
return HANDLED;
|
||||||
|
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
|
||||||
|
// Previous token was broadcast, 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:
|
||||||
|
transitionTo(mValidatedState);
|
||||||
|
break;
|
||||||
|
case CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
|
||||||
|
mUserDoesNotWant = true;
|
||||||
|
// TODO: Should teardown network.
|
||||||
|
transitionTo(mOfflineState);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return HANDLED;
|
||||||
default:
|
default:
|
||||||
return HANDLED;
|
return HANDLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Being in the OfflineState State indicates a Network is unwanted or failed validation.
|
||||||
private class OfflineState extends State {
|
private class OfflineState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
@@ -332,6 +362,10 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Being in the ValidatedState State indicates a Network is:
|
||||||
|
// - Successfully validated, or
|
||||||
|
// - Wanted "as is" by the user, or
|
||||||
|
// - Does not satsify the default NetworkRequest and so validation has been skipped.
|
||||||
private class ValidatedState extends State {
|
private class ValidatedState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
@@ -353,6 +387,19 @@ 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 void exit() {
|
||||||
|
Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
|
||||||
|
mNetworkAgentInfo.network.netId, null);
|
||||||
|
mConnectivityServiceHandler.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Being in the EvaluatingState State indicates the Network is being evaluated for internet
|
||||||
|
// connectivity.
|
||||||
private class EvaluatingState extends State {
|
private class EvaluatingState extends State {
|
||||||
private int mRetries;
|
private int mRetries;
|
||||||
|
|
||||||
@@ -395,11 +442,17 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
transitionTo(mValidatedState);
|
transitionTo(mValidatedState);
|
||||||
return HANDLED;
|
return HANDLED;
|
||||||
}
|
}
|
||||||
|
// Note: This call to isCaptivePortal() could take minutes. Resolving the
|
||||||
|
// server's IP addresses could hit the DNS timeout and attempting connections
|
||||||
|
// to each of the server's several (e.g. 11) IP addresses could each take
|
||||||
|
// SOCKET_TIMEOUT_MS. During this time this StateMachine will be unresponsive.
|
||||||
|
// isCaptivePortal() could be executed on another Thread if this is found to
|
||||||
|
// cause problems.
|
||||||
int httpResponseCode = isCaptivePortal();
|
int httpResponseCode = isCaptivePortal();
|
||||||
if (httpResponseCode == 204) {
|
if (httpResponseCode == 204) {
|
||||||
transitionTo(mValidatedState);
|
transitionTo(mValidatedState);
|
||||||
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
|
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
|
||||||
transitionTo(mUserPromptedState);
|
transitionTo(mCaptivePortalState);
|
||||||
} else if (++mRetries > MAX_RETRIES) {
|
} else if (++mRetries > MAX_RETRIES) {
|
||||||
transitionTo(mOfflineState);
|
transitionTo(mOfflineState);
|
||||||
} else if (mReevaluateDelayMs >= 0) {
|
} else if (mReevaluateDelayMs >= 0) {
|
||||||
@@ -423,10 +476,12 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
|
|
||||||
// BroadcastReceiver that waits for a particular Intent and then posts a message.
|
// BroadcastReceiver that waits for a particular Intent and then posts a message.
|
||||||
private class CustomIntentReceiver extends BroadcastReceiver {
|
private class CustomIntentReceiver extends BroadcastReceiver {
|
||||||
private final Message mMessage;
|
private final int mToken;
|
||||||
|
private final int mWhat;
|
||||||
private final String mAction;
|
private final String mAction;
|
||||||
CustomIntentReceiver(String action, int token, int message) {
|
CustomIntentReceiver(String action, int token, int what) {
|
||||||
mMessage = obtainMessage(message, token);
|
mToken = token;
|
||||||
|
mWhat = what;
|
||||||
mAction = action + "_" + mNetworkAgentInfo.network.netId + "_" + token;
|
mAction = action + "_" + mNetworkAgentInfo.network.netId + "_" + token;
|
||||||
mContext.registerReceiver(this, new IntentFilter(mAction));
|
mContext.registerReceiver(this, new IntentFilter(mAction));
|
||||||
}
|
}
|
||||||
@@ -435,120 +490,68 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent.getAction().equals(mAction)) sendMessage(mMessage);
|
if (intent.getAction().equals(mAction)) sendMessage(obtainMessage(mWhat, mToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UserPromptedState extends State {
|
private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver {
|
||||||
// Intent broadcast when user selects sign-in notification.
|
@Override
|
||||||
private static final String ACTION_SIGN_IN_REQUESTED =
|
public void onReceive(Context context, Intent intent) {
|
||||||
"android.net.netmon.sign_in_requested";
|
if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
|
||||||
|
mNetworkAgentInfo.network.netId &&
|
||||||
private CustomIntentReceiver mUserRespondedBroadcastReceiver;
|
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 {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
|
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
|
||||||
NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
|
NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
|
||||||
// Wait for user to select sign-in notifcation.
|
|
||||||
mUserRespondedBroadcastReceiver = new CustomIntentReceiver(ACTION_SIGN_IN_REQUESTED,
|
|
||||||
++mUserPromptedToken, CMD_USER_WANTS_SIGN_IN);
|
|
||||||
// Initiate notification to sign-in.
|
|
||||||
Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
|
|
||||||
mNetworkAgentInfo.network.netId,
|
|
||||||
mUserRespondedBroadcastReceiver.getPendingIntent());
|
|
||||||
mConnectivityServiceHandler.sendMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Assemble Intent to launch captive portal sign-in app.
|
||||||
public boolean processMessage(Message message) {
|
final Intent intent = new Intent(Intent.ACTION_SEND);
|
||||||
if (DBG) log(getName() + message.toString());
|
// Intent cannot use extras because PendingIntent.getActivity will merge matching
|
||||||
switch (message.what) {
|
// Intents erasing extras. Use data instead of extras to encode NetID.
|
||||||
case CMD_USER_WANTS_SIGN_IN:
|
intent.setData(Uri.fromParts("netid", Integer.toString(mNetworkAgentInfo.network.netId),
|
||||||
if (message.arg1 != mUserPromptedToken)
|
mCaptivePortalLoggedInResponseToken));
|
||||||
return HANDLED;
|
|
||||||
transitionTo(mCaptivePortalState);
|
|
||||||
return HANDLED;
|
|
||||||
default:
|
|
||||||
return NOT_HANDLED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exit() {
|
|
||||||
Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
|
|
||||||
mNetworkAgentInfo.network.netId, null);
|
|
||||||
mConnectivityServiceHandler.sendMessage(message);
|
|
||||||
mContext.unregisterReceiver(mUserRespondedBroadcastReceiver);
|
|
||||||
mUserRespondedBroadcastReceiver = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CaptivePortalState extends State {
|
|
||||||
private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver {
|
|
||||||
private final int mToken;
|
|
||||||
|
|
||||||
CaptivePortalLoggedInBroadcastReceiver(int token) {
|
|
||||||
mToken = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
|
|
||||||
mNetworkAgentInfo.network.netId) {
|
|
||||||
sendMessage(obtainMessage(CMD_CAPTIVE_PORTAL_LOGGED_IN, mToken,
|
|
||||||
Integer.parseInt(intent.getStringExtra(LOGGED_IN_RESULT))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enter() {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetworkAgentInfo.network.netId));
|
|
||||||
intent.setType("text/plain");
|
|
||||||
intent.setComponent(new ComponentName("com.android.captiveportallogin",
|
intent.setComponent(new ComponentName("com.android.captiveportallogin",
|
||||||
"com.android.captiveportallogin.CaptivePortalLoginActivity"));
|
"com.android.captiveportallogin.CaptivePortalLoginActivity"));
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
// Wait for result.
|
if (mCaptivePortalLoggedInBroadcastReceiver == null) {
|
||||||
mCaptivePortalLoggedInBroadcastReceiver = new CaptivePortalLoggedInBroadcastReceiver(
|
// Wait for result.
|
||||||
++mCaptivePortalLoggedInToken);
|
mCaptivePortalLoggedInBroadcastReceiver =
|
||||||
IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
|
new CaptivePortalLoggedInBroadcastReceiver();
|
||||||
mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter);
|
final IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
|
||||||
// Initiate app to log in.
|
mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter);
|
||||||
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
|
}
|
||||||
|
// Initiate notification to sign-in.
|
||||||
|
Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
|
||||||
|
mNetworkAgentInfo.network.netId,
|
||||||
|
PendingIntent.getActivity(mContext, 0, intent, 0));
|
||||||
|
mConnectivityServiceHandler.sendMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processMessage(Message message) {
|
public boolean processMessage(Message message) {
|
||||||
if (DBG) log(getName() + message.toString());
|
if (DBG) log(getName() + message.toString());
|
||||||
switch (message.what) {
|
return NOT_HANDLED;
|
||||||
case CMD_CAPTIVE_PORTAL_LOGGED_IN:
|
|
||||||
if (message.arg1 != mCaptivePortalLoggedInToken)
|
|
||||||
return HANDLED;
|
|
||||||
if (message.arg2 == 0) {
|
|
||||||
mUserDoesNotWant = true;
|
|
||||||
// TODO: Should teardown network.
|
|
||||||
transitionTo(mOfflineState);
|
|
||||||
} else {
|
|
||||||
transitionTo(mValidatedState);
|
|
||||||
}
|
|
||||||
return HANDLED;
|
|
||||||
default:
|
|
||||||
return NOT_HANDLED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exit() {
|
|
||||||
mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver);
|
|
||||||
mCaptivePortalLoggedInBroadcastReceiver = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Being in the LingeringState State indicates a Network's validated bit is true and it once
|
||||||
|
// was the highest scoring Network satisfying a particular NetworkRequest, but since then
|
||||||
|
// another Network satsified the NetworkRequest with a higher score and hence this Network
|
||||||
|
// is "lingered" for a fixed period of time before it is disconnected. This period of time
|
||||||
|
// allows apps to wrap up communication and allows for seamless reactivation if the other
|
||||||
|
// higher scoring Network happens to disconnect.
|
||||||
private class LingeringState extends State {
|
private class LingeringState extends State {
|
||||||
private static final String ACTION_LINGER_EXPIRED = "android.net.netmon.lingerExpired";
|
private static final String ACTION_LINGER_EXPIRED = "android.net.netmon.lingerExpired";
|
||||||
|
|
||||||
@@ -557,7 +560,8 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
mBroadcastReceiver = new CustomIntentReceiver(ACTION_LINGER_EXPIRED, ++mLingerToken,
|
mLingerToken = new Random().nextInt();
|
||||||
|
mBroadcastReceiver = new CustomIntentReceiver(ACTION_LINGER_EXPIRED, mLingerToken,
|
||||||
CMD_LINGER_EXPIRED);
|
CMD_LINGER_EXPIRED);
|
||||||
mIntent = mBroadcastReceiver.getPendingIntent();
|
mIntent = mBroadcastReceiver.getPendingIntent();
|
||||||
long wakeupTime = SystemClock.elapsedRealtime() + mLingerDelayMs;
|
long wakeupTime = SystemClock.elapsedRealtime() + mLingerDelayMs;
|
||||||
@@ -586,6 +590,20 @@ public class NetworkMonitor extends StateMachine {
|
|||||||
// timeout. Lingering is the result of score assessment; validity is
|
// timeout. Lingering is the result of score assessment; validity is
|
||||||
// irrelevant.
|
// irrelevant.
|
||||||
return HANDLED;
|
return HANDLED;
|
||||||
|
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
|
||||||
|
// Ignore user network determination as this could abort linger timeout.
|
||||||
|
// Networks are only lingered once validated because:
|
||||||
|
// - Unvalidated networks are never lingered (see rematchNetworkAndRequests).
|
||||||
|
// - Once validated, a Network's validated bit is never cleared.
|
||||||
|
// Since networks are only lingered after being validated a user's
|
||||||
|
// determination will not change the death sentence that lingering entails:
|
||||||
|
// - If the user wants to use the network or bypasses the captive portal,
|
||||||
|
// the network's score will not be increased beyond its current value
|
||||||
|
// because it is already validated. Without a score increase there is no
|
||||||
|
// chance of reactivation (i.e. aborting linger timeout).
|
||||||
|
// - If the user does not want the network, lingering will disconnect the
|
||||||
|
// network anyhow.
|
||||||
|
return HANDLED;
|
||||||
default:
|
default:
|
||||||
return NOT_HANDLED;
|
return NOT_HANDLED;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user