summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/CaptivePortalTracker.java247
-rw-r--r--services/java/com/android/server/ConnectivityService.java12
2 files changed, 170 insertions, 89 deletions
diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java
index 8218e37..24dc898 100644
--- a/core/java/android/net/CaptivePortalTracker.java
+++ b/core/java/android/net/CaptivePortalTracker.java
@@ -16,6 +16,7 @@
package android.net;
+import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -24,33 +25,32 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
+import android.os.UserHandle;
import android.os.Message;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.URL;
import java.net.UnknownHostException;
-import java.util.concurrent.atomic.AtomicBoolean;
import com.android.internal.R;
/**
- * This class allows captive portal detection
+ * This class allows captive portal detection on a network.
* @hide
*/
-public class CaptivePortalTracker {
- private static final boolean DBG = true;
+public class CaptivePortalTracker extends StateMachine {
+ private static final boolean DBG = false;
private static final String TAG = "CaptivePortalTracker";
private static final String DEFAULT_SERVER = "clients3.google.com";
@@ -62,37 +62,31 @@ public class CaptivePortalTracker {
private String mUrl;
private boolean mNotificationShown = false;
private boolean mIsCaptivePortalCheckEnabled = false;
- private InternalHandler mHandler;
private IConnectivityManager mConnService;
private Context mContext;
private NetworkInfo mNetworkInfo;
- private boolean mIsCaptivePortal = false;
- private static final int DETECT_PORTAL = 0;
- private static final int HANDLE_CONNECT = 1;
+ private static final int CMD_DETECT_PORTAL = 0;
+ private static final int CMD_CONNECTIVITY_CHANGE = 1;
+ private static final int CMD_DELAYED_CAPTIVE_CHECK = 2;
- /**
- * Activity Action: Switch to the captive portal network
- * <p>Input: Nothing.
- * <p>Output: Nothing.
- */
- public static final String ACTION_SWITCH_TO_CAPTIVE_PORTAL
- = "android.net.SWITCH_TO_CAPTIVE_PORTAL";
+ /* This delay happens every time before we do a captive check on a network */
+ private static final int DELAYED_CHECK_INTERVAL_MS = 10000;
+ private int mDelayedCheckToken = 0;
+
+ private State mDefaultState = new DefaultState();
+ private State mNoActiveNetworkState = new NoActiveNetworkState();
+ private State mActiveNetworkState = new ActiveNetworkState();
+ private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState();
+
+ private CaptivePortalTracker(Context context, IConnectivityManager cs) {
+ super(TAG);
- private CaptivePortalTracker(Context context, NetworkInfo info, IConnectivityManager cs) {
mContext = context;
- mNetworkInfo = info;
mConnService = cs;
- HandlerThread handlerThread = new HandlerThread("CaptivePortalThread");
- handlerThread.start();
- mHandler = new InternalHandler(handlerThread.getLooper());
- mHandler.obtainMessage(DETECT_PORTAL).sendToTarget();
-
IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_SWITCH_TO_CAPTIVE_PORTAL);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-
mContext.registerReceiver(mReceiver, filter);
mServer = Settings.Secure.getString(mContext.getContentResolver(),
@@ -101,100 +95,180 @@ public class CaptivePortalTracker {
mIsCaptivePortalCheckEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
+
+ addState(mDefaultState);
+ addState(mNoActiveNetworkState, mDefaultState);
+ addState(mActiveNetworkState, mDefaultState);
+ addState(mDelayedCaptiveCheckState, mActiveNetworkState);
+ setInitialState(mNoActiveNetworkState);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(ACTION_SWITCH_TO_CAPTIVE_PORTAL)) {
- notifyPortalCheckComplete();
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo info = intent.getParcelableExtra(
ConnectivityManager.EXTRA_NETWORK_INFO);
- mHandler.obtainMessage(HANDLE_CONNECT, info).sendToTarget();
+ sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info));
}
}
};
- public static CaptivePortalTracker detect(Context context, NetworkInfo info,
+ public static CaptivePortalTracker makeCaptivePortalTracker(Context context,
IConnectivityManager cs) {
- CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, info, cs);
+ CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, cs);
+ captivePortal.start();
return captivePortal;
}
- private class InternalHandler extends Handler {
- public InternalHandler(Looper looper) {
- super(looper);
+ public void detectCaptivePortal(NetworkInfo info) {
+ sendMessage(obtainMessage(CMD_DETECT_PORTAL, info));
+ }
+
+ private class DefaultState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
}
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case DETECT_PORTAL:
- InetAddress server = lookupHost(mServer);
- if (server != null) {
- requestRouteToHost(server);
- if (isCaptivePortal(server)) {
- if (DBG) log("Captive portal " + mNetworkInfo);
- setNotificationVisible(true);
- mIsCaptivePortal = true;
- break;
- }
- }
- notifyPortalCheckComplete();
- quit();
+ public boolean processMessage(Message message) {
+ if (DBG) log(getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_DETECT_PORTAL:
+ NetworkInfo info = (NetworkInfo) message.obj;
+ // Checking on a secondary connection is not supported
+ // yet
+ notifyPortalCheckComplete(info);
break;
- case HANDLE_CONNECT:
- NetworkInfo info = (NetworkInfo) msg.obj;
- if (info.getType() != mNetworkInfo.getType()) break;
+ case CMD_CONNECTIVITY_CHANGE:
+ case CMD_DELAYED_CAPTIVE_CHECK:
+ break;
+ default:
+ loge("Ignoring " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
- if (info.getState() == NetworkInfo.State.CONNECTED ||
- info.getState() == NetworkInfo.State.DISCONNECTED) {
- setNotificationVisible(false);
- }
+ private class NoActiveNetworkState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ mNetworkInfo = null;
+ /* Clear any previous notification */
+ setNotificationVisible(false);
+ }
- /* Connected to a captive portal */
- if (info.getState() == NetworkInfo.State.CONNECTED &&
- mIsCaptivePortal) {
- launchBrowser();
- quit();
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log(getName() + message.toString() + "\n");
+ InetAddress server;
+ NetworkInfo info;
+ switch (message.what) {
+ case CMD_CONNECTIVITY_CHANGE:
+ info = (NetworkInfo) message.obj;
+ if (info.isConnected() && isActiveNetwork(info)) {
+ mNetworkInfo = info;
+ transitionTo(mDelayedCaptiveCheckState);
}
break;
default:
- loge("Unhandled message " + msg);
- break;
+ return NOT_HANDLED;
}
+ return HANDLED;
+ }
+ }
+
+ private class ActiveNetworkState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
}
- private void quit() {
- mIsCaptivePortal = false;
- getLooper().quit();
- mContext.unregisterReceiver(mReceiver);
+ @Override
+ public boolean processMessage(Message message) {
+ NetworkInfo info;
+ switch (message.what) {
+ case CMD_CONNECTIVITY_CHANGE:
+ info = (NetworkInfo) message.obj;
+ if (!info.isConnected()
+ && info.getType() == mNetworkInfo.getType()) {
+ if (DBG) log("Disconnected from active network " + info);
+ transitionTo(mNoActiveNetworkState);
+ } else if (info.getType() != mNetworkInfo.getType() &&
+ info.isConnected() &&
+ isActiveNetwork(info)) {
+ if (DBG) log("Active network switched " + info);
+ deferMessage(message);
+ transitionTo(mNoActiveNetworkState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
}
}
- private void launchBrowser() {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl));
- intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
+
+
+ private class DelayedCaptiveCheckState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ sendMessageDelayed(obtainMessage(CMD_DELAYED_CAPTIVE_CHECK,
+ ++mDelayedCheckToken, 0), DELAYED_CHECK_INTERVAL_MS);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log(getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_DELAYED_CAPTIVE_CHECK:
+ if (message.arg1 == mDelayedCheckToken) {
+ InetAddress server = lookupHost(mServer);
+ if (server != null) {
+ if (isCaptivePortal(server)) {
+ if (DBG) log("Captive network " + mNetworkInfo);
+ setNotificationVisible(true);
+ }
+ }
+ if (DBG) log("Not captive network " + mNetworkInfo);
+ transitionTo(mActiveNetworkState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
}
- private void notifyPortalCheckComplete() {
+ private void notifyPortalCheckComplete(NetworkInfo info) {
+ if (info == null) {
+ loge("notifyPortalCheckComplete on null");
+ return;
+ }
try {
- mConnService.captivePortalCheckComplete(mNetworkInfo);
+ mConnService.captivePortalCheckComplete(info);
} catch(RemoteException e) {
e.printStackTrace();
}
}
- private void requestRouteToHost(InetAddress server) {
+ private boolean isActiveNetwork(NetworkInfo info) {
try {
- mConnService.requestRouteToHostAddress(mNetworkInfo.getType(),
- server.getAddress());
+ NetworkInfo active = mConnService.getActiveNetworkInfo();
+ if (active != null && active.getType() == info.getType()) {
+ return true;
+ }
} catch (RemoteException e) {
e.printStackTrace();
}
+ return false;
}
/**
@@ -205,6 +279,7 @@ public class CaptivePortalTracker {
if (!mIsCaptivePortalCheckEnabled) return false;
mUrl = "http://" + server.getHostAddress() + "/generate_204";
+ if (DBG) log("Checking " + mUrl);
try {
URL url = new URL(mUrl);
urlConnection = (HttpURLConnection) url.openConnection();
@@ -250,7 +325,12 @@ public class CaptivePortalTracker {
.getSystemService(Context.NOTIFICATION_SERVICE);
if (visible) {
- CharSequence title = r.getString(R.string.wifi_available_sign_in, 0);
+ CharSequence title;
+ if (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ title = r.getString(R.string.wifi_available_sign_in, 0);
+ } else {
+ title = r.getString(R.string.network_available_sign_in, 0);
+ }
CharSequence details = r.getString(R.string.network_available_sign_in_detailed,
mNetworkInfo.getExtraInfo());
@@ -258,9 +338,10 @@ public class CaptivePortalTracker {
notification.when = 0;
notification.icon = com.android.internal.R.drawable.stat_notify_wifi_in_range;
notification.flags = Notification.FLAG_AUTO_CANCEL;
- notification.contentIntent = PendingIntent.getBroadcast(mContext, 0,
- new Intent(CaptivePortalTracker.ACTION_SWITCH_TO_CAPTIVE_PORTAL), 0);
-
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl));
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
notification.tickerText = title;
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 5c7a3ed..9676eb9 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -32,6 +32,7 @@ import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import android.app.Activity;
import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -548,6 +549,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
mSettingsObserver.observe(mContext);
+ mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
loadGlobalProxy();
}
@@ -1694,7 +1696,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info));
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -1825,7 +1827,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Intent intent = new Intent(bcastType);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info));
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -1882,7 +1884,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info));
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
@@ -2075,8 +2077,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
if (isNewNetTypePreferredOverCurrentNetType(type)) {
if (DBG) log("Captive check on " + info.getTypeName());
- mCaptivePortalTracker = CaptivePortalTracker.detect(mContext, info,
- ConnectivityService.this);
+ mCaptivePortalTracker.detectCaptivePortal(new NetworkInfo(info));
return;
} else {
if (DBG) log("Tear down low priority net " + info.getTypeName());
@@ -2092,7 +2093,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
/** @hide */
public void captivePortalCheckComplete(NetworkInfo info) {
mNetTrackers[info.getType()].captivePortalCheckComplete();
- mCaptivePortalTracker = null;
}
/**