CaptivePortalTracker.java revision 9538bdd3c77968c7673719c580ae653ede4654d6
1da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff/* 2da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * Copyright (C) 2012 The Android Open Source Project 3da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * 4da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * Licensed under the Apache License, Version 2.0 (the "License"); 5da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * you may not use this file except in compliance with the License. 6da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * You may obtain a copy of the License at 7da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * 8da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * http://www.apache.org/licenses/LICENSE-2.0 9da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * 10da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * Unless required by applicable law or agreed to in writing, software 11da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * distributed under the License is distributed on an "AS IS" BASIS, 12da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * See the License for the specific language governing permissions and 14da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * limitations under the License. 15da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff */ 16da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 17da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffpackage android.net; 18da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 199538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport android.app.Activity; 20da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.app.Notification; 21da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.app.NotificationManager; 22da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.app.PendingIntent; 23da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.BroadcastReceiver; 24da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.Context; 25da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.Intent; 26da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.IntentFilter; 27da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.res.Resources; 28da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.net.ConnectivityManager; 29da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.net.IConnectivityManager; 309538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport android.os.UserHandle; 31da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.os.Message; 32da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.os.RemoteException; 33da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.provider.Settings; 34da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.util.Log; 35da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 369538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport com.android.internal.util.State; 379538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport com.android.internal.util.StateMachine; 389538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 39da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.io.IOException; 40da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.HttpURLConnection; 41da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.InetAddress; 42da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.Inet4Address; 43da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.URL; 44da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.UnknownHostException; 45da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 46da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport com.android.internal.R; 47da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 48da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff/** 499538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff * This class allows captive portal detection on a network. 50da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * @hide 51da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff */ 529538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffpublic class CaptivePortalTracker extends StateMachine { 539538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private static final boolean DBG = false; 54da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private static final String TAG = "CaptivePortalTracker"; 55da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 56da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private static final String DEFAULT_SERVER = "clients3.google.com"; 57da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private static final String NOTIFICATION_ID = "CaptivePortal.Notification"; 58da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 59da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private static final int SOCKET_TIMEOUT_MS = 10000; 60da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 61da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private String mServer; 62da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private String mUrl; 63da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private boolean mNotificationShown = false; 64da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private boolean mIsCaptivePortalCheckEnabled = false; 65da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private IConnectivityManager mConnService; 66da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private Context mContext; 67da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private NetworkInfo mNetworkInfo; 68da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 699538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private static final int CMD_DETECT_PORTAL = 0; 709538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private static final int CMD_CONNECTIVITY_CHANGE = 1; 719538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private static final int CMD_DELAYED_CAPTIVE_CHECK = 2; 72da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 739538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff /* This delay happens every time before we do a captive check on a network */ 749538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private static final int DELAYED_CHECK_INTERVAL_MS = 10000; 759538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private int mDelayedCheckToken = 0; 769538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 779538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private State mDefaultState = new DefaultState(); 789538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private State mNoActiveNetworkState = new NoActiveNetworkState(); 799538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private State mActiveNetworkState = new ActiveNetworkState(); 809538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState(); 819538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 829538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private CaptivePortalTracker(Context context, IConnectivityManager cs) { 839538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff super(TAG); 84da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 85da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mContext = context; 86da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mConnService = cs; 87da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 88da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff IntentFilter filter = new IntentFilter(); 89da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 90da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mContext.registerReceiver(mReceiver, filter); 91da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 92da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mServer = Settings.Secure.getString(mContext.getContentResolver(), 93da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff Settings.Secure.CAPTIVE_PORTAL_SERVER); 94da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff if (mServer == null) mServer = DEFAULT_SERVER; 95da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 96da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mIsCaptivePortalCheckEnabled = Settings.Secure.getInt(mContext.getContentResolver(), 97da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff Settings.Secure.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1; 989538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 999538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff addState(mDefaultState); 1009538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff addState(mNoActiveNetworkState, mDefaultState); 1019538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff addState(mActiveNetworkState, mDefaultState); 1029538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff addState(mDelayedCaptiveCheckState, mActiveNetworkState); 1039538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff setInitialState(mNoActiveNetworkState); 104da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 105da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 106da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 107da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff @Override 108da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff public void onReceive(Context context, Intent intent) { 109da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff String action = intent.getAction(); 1109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { 111da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff NetworkInfo info = intent.getParcelableExtra( 112da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff ConnectivityManager.EXTRA_NETWORK_INFO); 1139538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info)); 114da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 115da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 116da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff }; 117da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 1189538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public static CaptivePortalTracker makeCaptivePortalTracker(Context context, 119da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff IConnectivityManager cs) { 1209538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, cs); 1219538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff captivePortal.start(); 122da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff return captivePortal; 123da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 124da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 1259538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public void detectCaptivePortal(NetworkInfo info) { 1269538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff sendMessage(obtainMessage(CMD_DETECT_PORTAL, info)); 1279538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 1289538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 1299538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private class DefaultState extends State { 1309538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff @Override 1319538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public void enter() { 1329538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log(getName() + "\n"); 133da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 134da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 135da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff @Override 1369538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public boolean processMessage(Message message) { 1379538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log(getName() + message.toString() + "\n"); 1389538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff switch (message.what) { 1399538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff case CMD_DETECT_PORTAL: 1409538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff NetworkInfo info = (NetworkInfo) message.obj; 1419538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff // Checking on a secondary connection is not supported 1429538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff // yet 1439538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff notifyPortalCheckComplete(info); 144da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff break; 1459538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff case CMD_CONNECTIVITY_CHANGE: 1469538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff case CMD_DELAYED_CAPTIVE_CHECK: 1479538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff break; 1489538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff default: 1499538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff loge("Ignoring " + message); 1509538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff break; 1519538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 1529538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return HANDLED; 1539538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 1549538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 155da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 1569538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private class NoActiveNetworkState extends State { 1579538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff @Override 1589538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public void enter() { 1599538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log(getName() + "\n"); 1609538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff mNetworkInfo = null; 1619538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff /* Clear any previous notification */ 1629538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff setNotificationVisible(false); 1639538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 164da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 1659538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff @Override 1669538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public boolean processMessage(Message message) { 1679538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log(getName() + message.toString() + "\n"); 1689538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff InetAddress server; 1699538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff NetworkInfo info; 1709538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff switch (message.what) { 1719538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff case CMD_CONNECTIVITY_CHANGE: 1729538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff info = (NetworkInfo) message.obj; 1739538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (info.isConnected() && isActiveNetwork(info)) { 1749538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff mNetworkInfo = info; 1759538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff transitionTo(mDelayedCaptiveCheckState); 176da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 177da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff break; 178da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff default: 1799538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return NOT_HANDLED; 180da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 1819538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return HANDLED; 1829538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 1839538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 1849538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 1859538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private class ActiveNetworkState extends State { 1869538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff @Override 1879538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public void enter() { 1889538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log(getName() + "\n"); 189da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 190da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 1919538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff @Override 1929538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public boolean processMessage(Message message) { 1939538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff NetworkInfo info; 1949538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff switch (message.what) { 1959538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff case CMD_CONNECTIVITY_CHANGE: 1969538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff info = (NetworkInfo) message.obj; 1979538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (!info.isConnected() 1989538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff && info.getType() == mNetworkInfo.getType()) { 1999538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log("Disconnected from active network " + info); 2009538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff transitionTo(mNoActiveNetworkState); 2019538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } else if (info.getType() != mNetworkInfo.getType() && 2029538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff info.isConnected() && 2039538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff isActiveNetwork(info)) { 2049538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log("Active network switched " + info); 2059538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff deferMessage(message); 2069538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff transitionTo(mNoActiveNetworkState); 2079538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 2089538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff break; 2099538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff default: 2109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return NOT_HANDLED; 2119538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 2129538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return HANDLED; 213da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 214da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 215da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 2169538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 2179538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 2189538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private class DelayedCaptiveCheckState extends State { 2199538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff @Override 2209538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public void enter() { 2219538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log(getName() + "\n"); 2229538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff sendMessageDelayed(obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, 2239538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff ++mDelayedCheckToken, 0), DELAYED_CHECK_INTERVAL_MS); 2249538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 2259538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff 2269538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff @Override 2279538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff public boolean processMessage(Message message) { 2289538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log(getName() + message.toString() + "\n"); 2299538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff switch (message.what) { 2309538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff case CMD_DELAYED_CAPTIVE_CHECK: 2319538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (message.arg1 == mDelayedCheckToken) { 2329538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff InetAddress server = lookupHost(mServer); 2339538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (server != null) { 2349538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (isCaptivePortal(server)) { 2359538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log("Captive network " + mNetworkInfo); 2369538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff setNotificationVisible(true); 2379538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 2389538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 2399538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log("Not captive network " + mNetworkInfo); 2409538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff transitionTo(mActiveNetworkState); 2419538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 2429538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff break; 2439538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff default: 2449538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return NOT_HANDLED; 2459538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 2469538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return HANDLED; 2479538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 248da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 249da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 2509538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private void notifyPortalCheckComplete(NetworkInfo info) { 2519538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (info == null) { 2529538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff loge("notifyPortalCheckComplete on null"); 2539538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return; 2549538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 255da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff try { 2569538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff mConnService.captivePortalCheckComplete(info); 257da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } catch(RemoteException e) { 258da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff e.printStackTrace(); 259da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 260da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 261da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 2629538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff private boolean isActiveNetwork(NetworkInfo info) { 263da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff try { 2649538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff NetworkInfo active = mConnService.getActiveNetworkInfo(); 2659538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (active != null && active.getType() == info.getType()) { 2669538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return true; 2679538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 268da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } catch (RemoteException e) { 269da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff e.printStackTrace(); 270da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 2719538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff return false; 272da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 273da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 274da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff /** 275da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * Do a URL fetch on a known server to see if we get the data we expect 276da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff */ 277da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private boolean isCaptivePortal(InetAddress server) { 278da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff HttpURLConnection urlConnection = null; 279da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff if (!mIsCaptivePortalCheckEnabled) return false; 280da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 281da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mUrl = "http://" + server.getHostAddress() + "/generate_204"; 2829538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (DBG) log("Checking " + mUrl); 283da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff try { 284da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff URL url = new URL(mUrl); 285da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff urlConnection = (HttpURLConnection) url.openConnection(); 286da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff urlConnection.setInstanceFollowRedirects(false); 287da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); 288da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); 289da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff urlConnection.setUseCaches(false); 290da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff urlConnection.getInputStream(); 291da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff // we got a valid response, but not from the real google 292da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff return urlConnection.getResponseCode() != 204; 293da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } catch (IOException e) { 294da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff if (DBG) log("Probably not a portal: exception " + e); 295da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff return false; 296da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } finally { 297da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff if (urlConnection != null) { 298da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff urlConnection.disconnect(); 299da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 300da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 301da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 302da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 303da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private InetAddress lookupHost(String hostname) { 304da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff InetAddress inetAddress[]; 305da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff try { 306da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff inetAddress = InetAddress.getAllByName(hostname); 307da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } catch (UnknownHostException e) { 308da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff return null; 309da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 310da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 311da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff for (InetAddress a : inetAddress) { 312da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff if (a instanceof Inet4Address) return a; 313da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 314da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff return null; 315da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 316da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 317da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private void setNotificationVisible(boolean visible) { 318da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff // if it should be hidden and it is already hidden, then noop 319da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff if (!visible && !mNotificationShown) { 320da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff return; 321da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 322da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 323da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff Resources r = Resources.getSystem(); 324da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff NotificationManager notificationManager = (NotificationManager) mContext 325da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff .getSystemService(Context.NOTIFICATION_SERVICE); 326da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 327da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff if (visible) { 3289538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff CharSequence title; 3299538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff if (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) { 3309538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff title = r.getString(R.string.wifi_available_sign_in, 0); 3319538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } else { 3329538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff title = r.getString(R.string.network_available_sign_in, 0); 3339538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff } 3347f0aaac7f2a2bff6168467132c704fce2c7ca170Irfan Sheriff CharSequence details = r.getString(R.string.network_available_sign_in_detailed, 335da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mNetworkInfo.getExtraInfo()); 336da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 337da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff Notification notification = new Notification(); 338da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff notification.when = 0; 339da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff notification.icon = com.android.internal.R.drawable.stat_notify_wifi_in_range; 340da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff notification.flags = Notification.FLAG_AUTO_CANCEL; 3419538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl)); 3429538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | 3439538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff Intent.FLAG_ACTIVITY_NEW_TASK); 3449538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0); 345da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff notification.tickerText = title; 346da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff notification.setLatestEventInfo(mContext, title, details, notification.contentIntent); 347da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 348da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff notificationManager.notify(NOTIFICATION_ID, 1, notification); 349da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } else { 350da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff notificationManager.cancel(NOTIFICATION_ID, 1); 351da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 352da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff mNotificationShown = visible; 353da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 354da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 355da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private static void log(String s) { 356da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff Log.d(TAG, s); 357da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 358da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 359da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff private static void loge(String s) { 360da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff Log.e(TAG, s); 361da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff } 362da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff 363da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff} 364