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
19da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.BroadcastReceiver;
20da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.Context;
21da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.Intent;
22da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.IntentFilter;
23108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brennerimport android.database.ContentObserver;
24da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.net.ConnectivityManager;
25da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.net.IConnectivityManager;
261ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.net.wifi.WifiInfo;
271ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.net.wifi.WifiManager;
28108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brennerimport android.os.Handler;
29da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.os.Message;
30da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.os.RemoteException;
311ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.os.SystemClock;
32da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.provider.Settings;
331ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellIdentityCdma;
341ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellIdentityGsm;
351ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellIdentityLte;
361ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellIdentityWcdma;
371ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellInfo;
381ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellInfoCdma;
391ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellInfoGsm;
401ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellInfoLte;
411ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport android.telephony.CellInfoWcdma;
42b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriffimport android.telephony.TelephonyManager;
43da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
449538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport com.android.internal.util.State;
459538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport com.android.internal.util.StateMachine;
469538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
47da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.io.IOException;
48da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.HttpURLConnection;
49da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.InetAddress;
50da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.Inet4Address;
51bf34122a96ef3d02e9b7935e07eb4f9b04034828Wink Savilleimport java.net.SocketTimeoutException;
52da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.URL;
53da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.UnknownHostException;
541ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammeeimport java.util.List;
55da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
56da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff/**
579538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff * This class allows captive portal detection on a network.
58da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * @hide
59da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff */
609538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffpublic class CaptivePortalTracker extends StateMachine {
61bf34122a96ef3d02e9b7935e07eb4f9b04034828Wink Saville    private static final boolean DBG = true;
62da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final String TAG = "CaptivePortalTracker";
63da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
64da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final String DEFAULT_SERVER = "clients3.google.com";
65da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
66da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final int SOCKET_TIMEOUT_MS = 10000;
67da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
681ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
691ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            "android.net.conn.NETWORK_CONDITIONS_MEASURED";
701ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
711ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
721ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
731ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
741ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_CELL_ID = "extra_cellid";
751ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_SSID = "extra_ssid";
761ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_BSSID = "extra_bssid";
771ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    /** real time since boot */
781ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
791ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
801ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
811ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
821ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            "android.permission.ACCESS_NETWORK_CONDITIONS";
831ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
84da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private String mServer;
85da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private String mUrl;
86da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private boolean mIsCaptivePortalCheckEnabled = false;
87da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private IConnectivityManager mConnService;
88b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff    private TelephonyManager mTelephonyManager;
891ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    private WifiManager mWifiManager;
90da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private Context mContext;
91da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private NetworkInfo mNetworkInfo;
92da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
939538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int CMD_DETECT_PORTAL          = 0;
949538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int CMD_CONNECTIVITY_CHANGE    = 1;
959538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int CMD_DELAYED_CAPTIVE_CHECK  = 2;
96da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
979538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    /* This delay happens every time before we do a captive check on a network */
989538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int DELAYED_CHECK_INTERVAL_MS = 10000;
999538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private int mDelayedCheckToken = 0;
1009538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
1019538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mDefaultState = new DefaultState();
1029538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mNoActiveNetworkState = new NoActiveNetworkState();
1039538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mActiveNetworkState = new ActiveNetworkState();
1049538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState();
1059538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
106108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private static final String SETUP_WIZARD_PACKAGE = "com.google.android.setupwizard";
107108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private boolean mDeviceProvisioned = false;
108108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private ProvisioningObserver mProvisioningObserver;
109108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
1109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private CaptivePortalTracker(Context context, IConnectivityManager cs) {
1119538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        super(TAG);
112da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
113da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mContext = context;
114da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mConnService = cs;
115b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1161ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
117108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        mProvisioningObserver = new ProvisioningObserver();
118da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
119da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        IntentFilter filter = new IntentFilter();
120da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
121108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
122da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mContext.registerReceiver(mReceiver, filter);
123da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
124625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey        mServer = Settings.Global.getString(mContext.getContentResolver(),
125625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.CAPTIVE_PORTAL_SERVER);
126da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        if (mServer == null) mServer = DEFAULT_SERVER;
127da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
128625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey        mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
129625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
1309538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
1319538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        addState(mDefaultState);
1329538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            addState(mNoActiveNetworkState, mDefaultState);
1339538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            addState(mActiveNetworkState, mDefaultState);
1349538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                addState(mDelayedCaptiveCheckState, mActiveNetworkState);
1359538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        setInitialState(mNoActiveNetworkState);
136da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
137da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
138108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private class ProvisioningObserver extends ContentObserver {
139108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        ProvisioningObserver() {
140108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            super(new Handler());
141108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
142108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                    Settings.Global.DEVICE_PROVISIONED), false, this);
143108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            onChange(false); // load initial value
144108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        }
145108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
146108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        @Override
147108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        public void onChange(boolean selfChange) {
148108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
149108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                    Settings.Global.DEVICE_PROVISIONED, 0) != 0;
150108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        }
151108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    }
152108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
153da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
154da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        @Override
155da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        public void onReceive(Context context, Intent intent) {
156da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            String action = intent.getAction();
157108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            // Normally, we respond to CONNECTIVITY_ACTION, allowing time for the change in
158108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            // connectivity to stabilize, but if the device is not yet provisioned, respond
159108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            // immediately to speed up transit through the setup wizard.
160108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            if ((mDeviceProvisioned && action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
161108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                    || (!mDeviceProvisioned
162108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            && action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE))) {
163da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                NetworkInfo info = intent.getParcelableExtra(
164da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                        ConnectivityManager.EXTRA_NETWORK_INFO);
1659538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info));
166da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            }
167da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
168da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    };
169da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
1709538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    public static CaptivePortalTracker makeCaptivePortalTracker(Context context,
171da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            IConnectivityManager cs) {
1729538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, cs);
1739538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        captivePortal.start();
174da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        return captivePortal;
175da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
176da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
1779538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    public void detectCaptivePortal(NetworkInfo info) {
1789538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        sendMessage(obtainMessage(CMD_DETECT_PORTAL, info));
1799538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    }
1809538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
1819538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class DefaultState extends State {
182da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
183da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        @Override
1849538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
185948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville            if (DBG) log(getName() + message.toString());
1869538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
1879538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_DETECT_PORTAL:
1889538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    NetworkInfo info = (NetworkInfo) message.obj;
1899538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    // Checking on a secondary connection is not supported
1909538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    // yet
1919538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    notifyPortalCheckComplete(info);
192da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                    break;
1939538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_CONNECTIVITY_CHANGE:
1949538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_DELAYED_CAPTIVE_CHECK:
1959538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
1969538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                default:
1979538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    loge("Ignoring " + message);
1989538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
1999538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
2009538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
2019538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
2029538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    }
203da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
2049538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class NoActiveNetworkState extends State {
2059538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2069538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public void enter() {
2073ec8e7fb79f2e662c2b06cfec118a0eeaefd9370Wink Saville            setNotificationOff();
2089538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            mNetworkInfo = null;
2099538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
210da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
2119538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2129538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
213948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville            if (DBG) log(getName() + message.toString());
2149538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            InetAddress server;
2159538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            NetworkInfo info;
2169538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
2179538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_CONNECTIVITY_CHANGE:
2189538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    info = (NetworkInfo) message.obj;
219948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                    if (info.getType() == ConnectivityManager.TYPE_WIFI) {
220948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                        if (info.isConnected() && isActiveNetwork(info)) {
221948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                            mNetworkInfo = info;
222948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                            transitionTo(mDelayedCaptiveCheckState);
223948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                        }
224948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                    } else {
225948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                        log(getName() + " not a wifi connectivity change, ignore");
226da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                    }
227da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                    break;
228da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                default:
2299538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    return NOT_HANDLED;
230da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            }
2319538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
2329538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
2339538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    }
2349538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2359538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class ActiveNetworkState extends State {
2369538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2379538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
2389538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            NetworkInfo info;
2399538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
2409538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff               case CMD_CONNECTIVITY_CHANGE:
2419538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    info = (NetworkInfo) message.obj;
2429538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    if (!info.isConnected()
2439538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            && info.getType() == mNetworkInfo.getType()) {
2449538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        if (DBG) log("Disconnected from active network " + info);
2459538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        transitionTo(mNoActiveNetworkState);
2469538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    } else if (info.getType() != mNetworkInfo.getType() &&
2479538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            info.isConnected() &&
2489538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            isActiveNetwork(info)) {
2499538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        if (DBG) log("Active network switched " + info);
2509538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        deferMessage(message);
2519538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        transitionTo(mNoActiveNetworkState);
2529538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    }
2539538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
2549538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                default:
2559538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    return NOT_HANDLED;
2569538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
2579538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
258da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
259da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
260da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
2619538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2629538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2639538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class DelayedCaptiveCheckState extends State {
2649538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2659538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public void enter() {
266108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
267108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            if (mDeviceProvisioned) {
268108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
269108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            } else {
270108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                sendMessage(message);
271108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            }
2729538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
2739538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2749538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2759538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
276948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville            if (DBG) log(getName() + message.toString());
2779538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
2789538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_DELAYED_CAPTIVE_CHECK:
2793ec8e7fb79f2e662c2b06cfec118a0eeaefd9370Wink Saville                    setNotificationOff();
2803ec8e7fb79f2e662c2b06cfec118a0eeaefd9370Wink Saville
2819538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    if (message.arg1 == mDelayedCheckToken) {
2829538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        InetAddress server = lookupHost(mServer);
283108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        boolean captive = server != null && isCaptivePortal(server);
284108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        if (captive) {
285108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            if (DBG) log("Captive network " + mNetworkInfo);
286108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        } else {
287108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            if (DBG) log("Not captive network " + mNetworkInfo);
288108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        }
289d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville                        notifyPortalCheckCompleted(mNetworkInfo, captive);
290108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        if (mDeviceProvisioned) {
291108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            if (captive) {
292108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                                // Setup Wizard will assist the user in connecting to a captive
293108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                                // portal, so make the notification visible unless during setup
294948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                                try {
295948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                                    mConnService.setProvisioningNotificationVisible(true,
296948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                                        mNetworkInfo.getType(), mNetworkInfo.getExtraInfo(), mUrl);
297948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                                } catch(RemoteException e) {
298948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                                    e.printStackTrace();
299948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                                }
3009538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            }
301108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        } else {
302108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            Intent intent = new Intent(
303108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                                    ConnectivityManager.ACTION_CAPTIVE_PORTAL_TEST_COMPLETED);
304108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            intent.putExtra(ConnectivityManager.EXTRA_IS_CAPTIVE_PORTAL, captive);
305108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            intent.setPackage(SETUP_WIZARD_PACKAGE);
306108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            mContext.sendBroadcast(intent);
3079538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        }
308108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
3099538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        transitionTo(mActiveNetworkState);
3109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    }
3119538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
3129538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                default:
3139538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    return NOT_HANDLED;
3149538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
3159538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
3169538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
317da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
318da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
3199538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private void notifyPortalCheckComplete(NetworkInfo info) {
3209538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        if (info == null) {
3219538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            loge("notifyPortalCheckComplete on null");
3229538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return;
3239538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
324da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
325d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville            if (DBG) log("notifyPortalCheckComplete: ni=" + info);
3269538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            mConnService.captivePortalCheckComplete(info);
327da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch(RemoteException e) {
328da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            e.printStackTrace();
329da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
330da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
331da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
332d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville    private void notifyPortalCheckCompleted(NetworkInfo info, boolean isCaptivePortal) {
333d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville        if (info == null) {
334d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville            loge("notifyPortalCheckComplete on null");
335d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville            return;
336d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville        }
337d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville        try {
338d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville            if (DBG) log("notifyPortalCheckCompleted: captive=" + isCaptivePortal + " ni=" + info);
339d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville            mConnService.captivePortalCheckCompleted(info, isCaptivePortal);
340d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville        } catch(RemoteException e) {
341d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville            e.printStackTrace();
342d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville        }
343d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville    }
344d747cbc898ef44e59c3fbf74a8327b6a12aad397Wink Saville
3459538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private boolean isActiveNetwork(NetworkInfo info) {
346da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
3479538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            NetworkInfo active = mConnService.getActiveNetworkInfo();
3489538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (active != null && active.getType() == info.getType()) {
3499538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                return true;
3509538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
351da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch (RemoteException e) {
352da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            e.printStackTrace();
353da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
3549538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        return false;
355da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
356da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
357948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville    private void setNotificationOff() {
358948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville        try {
3593ec8e7fb79f2e662c2b06cfec118a0eeaefd9370Wink Saville            if (mNetworkInfo != null) {
3603ec8e7fb79f2e662c2b06cfec118a0eeaefd9370Wink Saville                mConnService.setProvisioningNotificationVisible(false, mNetworkInfo.getType(),
361948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville                    null, null);
3623ec8e7fb79f2e662c2b06cfec118a0eeaefd9370Wink Saville            }
363948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville        } catch (RemoteException e) {
364948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville            log("setNotificationOff: " + e);
365948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville        }
366948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville    }
367948282b0e6cf5310f09db97a4ae939db7c1cef72Wink Saville
368da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    /**
3691ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee     * Do a URL fetch on a known server to see if we get the data we expect.
3701ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee     * Measure the response time and broadcast that.
371da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff     */
372da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private boolean isCaptivePortal(InetAddress server) {
373da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        HttpURLConnection urlConnection = null;
374da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        if (!mIsCaptivePortalCheckEnabled) return false;
375da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
376da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mUrl = "http://" + server.getHostAddress() + "/generate_204";
3779538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        if (DBG) log("Checking " + mUrl);
3781ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        long requestTimestamp = -1;
379da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
380da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            URL url = new URL(mUrl);
381da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection = (HttpURLConnection) url.openConnection();
382da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setInstanceFollowRedirects(false);
383da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
384da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
385da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setUseCaches(false);
3861ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
3871ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            // Time how long it takes to get a response to our request
3881ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            requestTimestamp = SystemClock.elapsedRealtime();
3891ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
390da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.getInputStream();
3911ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
3921ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            // Time how long it takes to get a response to our request
3931ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            long responseTimestamp = SystemClock.elapsedRealtime();
3941ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
395da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            // we got a valid response, but not from the real google
3969d4204d6257bf69885cf0718aa3adaa17457c318Wink Saville            int rspCode = urlConnection.getResponseCode();
3979d4204d6257bf69885cf0718aa3adaa17457c318Wink Saville            boolean isCaptivePortal = rspCode != 204;
3981ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
3991ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            sendNetworkConditionsBroadcast(true /* response received */, isCaptivePortal,
4001ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                    requestTimestamp, responseTimestamp);
4019d4204d6257bf69885cf0718aa3adaa17457c318Wink Saville
4029d4204d6257bf69885cf0718aa3adaa17457c318Wink Saville            if (DBG) log("isCaptivePortal: ret=" + isCaptivePortal + " rspCode=" + rspCode);
4031ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            return isCaptivePortal;
404da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch (IOException e) {
405da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            if (DBG) log("Probably not a portal: exception " + e);
4061ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            if (requestTimestamp != -1) {
4071ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                sendFailedCaptivePortalCheckBroadcast(requestTimestamp);
4081ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            } // else something went wrong with setting up the urlConnection
409da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            return false;
410da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } finally {
411da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            if (urlConnection != null) {
412da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                urlConnection.disconnect();
413da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            }
414da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
415da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
416da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
417da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private InetAddress lookupHost(String hostname) {
418da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        InetAddress inetAddress[];
419da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
420da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            inetAddress = InetAddress.getAllByName(hostname);
421da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch (UnknownHostException e) {
4221ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            sendFailedCaptivePortalCheckBroadcast(SystemClock.elapsedRealtime());
423da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            return null;
424da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
425da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
426da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        for (InetAddress a : inetAddress) {
427da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            if (a instanceof Inet4Address) return a;
428da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
4291ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
4301ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        sendFailedCaptivePortalCheckBroadcast(SystemClock.elapsedRealtime());
431da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        return null;
432da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
433da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
4341ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    private void sendFailedCaptivePortalCheckBroadcast(long requestTimestampMs) {
4351ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        sendNetworkConditionsBroadcast(false /* response received */, false /* ignored */,
4361ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                requestTimestampMs, 0 /* ignored */);
4371ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    }
4381ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
4391ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    /**
4401ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee     * @param responseReceived - whether or not we received a valid HTTP response to our request.
4411ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee     * If false, isCaptivePortal and responseTimestampMs are ignored
4421ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee     */
4431ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
4441ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            long requestTimestampMs, long responseTimestampMs) {
4451ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        if (Settings.Global.getInt(mContext.getContentResolver(),
4461ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0) {
4471ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            if (DBG) log("Don't send network conditions - lacking user consent.");
4481ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            return;
4491ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        }
4501ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
4511ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED);
4521ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        switch (mNetworkInfo.getType()) {
4531ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            case ConnectivityManager.TYPE_WIFI:
4541ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
4551ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                if (currentWifiInfo != null) {
4561ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                    latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID());
4571ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                    latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID());
4581ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                } else {
4591ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                    if (DBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
4601ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                    return;
4611ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                }
4621ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                break;
4631ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            case ConnectivityManager.TYPE_MOBILE:
4641ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                latencyBroadcast.putExtra(EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType());
4651ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                List<CellInfo> info = mTelephonyManager.getAllCellInfo();
4661ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                if (info == null) return;
4671ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                StringBuffer uniqueCellId = new StringBuffer();
4681ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                int numRegisteredCellInfo = 0;
4691ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                for (CellInfo cellInfo : info) {
4701ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                    if (cellInfo.isRegistered()) {
4711ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        numRegisteredCellInfo++;
4721ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        if (numRegisteredCellInfo > 1) {
4731ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            if (DBG) log("more than one registered CellInfo.  Can't " +
4741ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                                    "tell which is active.  Bailing.");
4751ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            return;
4761ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        }
4771ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        if (cellInfo instanceof CellInfoCdma) {
4781ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
4791ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
4801ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        } else if (cellInfo instanceof CellInfoGsm) {
4811ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
4821ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
4831ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        } else if (cellInfo instanceof CellInfoLte) {
4841ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
4851ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
4861ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        } else if (cellInfo instanceof CellInfoWcdma) {
4871ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
4881ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
4891ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        } else {
4901ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            if (DBG) logw("Registered cellinfo is unrecognized");
4911ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                            return;
4921ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                        }
4931ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                    }
4941ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                }
4951ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                break;
4961ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            default:
4971ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee                return;
4981ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        }
4991ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        latencyBroadcast.putExtra(EXTRA_CONNECTIVITY_TYPE, mNetworkInfo.getType());
5001ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        latencyBroadcast.putExtra(EXTRA_RESPONSE_RECEIVED, responseReceived);
5011ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        latencyBroadcast.putExtra(EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs);
5021ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee
5031ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        if (responseReceived) {
5041ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            latencyBroadcast.putExtra(EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal);
5051ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee            latencyBroadcast.putExtra(EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs);
5061ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        }
5071ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee        mContext.sendBroadcast(latencyBroadcast, PERMISSION_ACCESS_NETWORK_CONDITIONS);
5081ed51627d94b140e8893b8e1dcde9bf37e2adca7Brian Williammee    }
509da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff}
510