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