NetworkMonitor.java revision a68d21350266a6abc16aeb3fec84eafd85b77cc8
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2014 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * you may not use this file except in compliance with the License.
669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * You may obtain a copy of the License at
769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * distributed under the License is distributed on an "AS IS" BASIS,
1269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * See the License for the specific language governing permissions and
1469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage com.android.server.connectivity;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.PendingIntent;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.BroadcastReceiver;
210795272aa226f4e965968a03daddc53ce30b7cdaMathias Agopianimport android.content.ComponentName;
220795272aa226f4e965968a03daddc53ce30b7cdaMathias Agopianimport android.content.Context;
230795272aa226f4e965968a03daddc53ce30b7cdaMathias Agopianimport android.content.Intent;
24f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kastenimport android.content.IntentFilter;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.ConnectivityManager;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Network;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.NetworkCapabilities;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.NetworkInfo;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.wifi.WifiInfo;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.wifi.WifiManager;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemProperties;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.UserHandle;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.Settings;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.telephony.CellIdentityCdma;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.telephony.CellIdentityGsm;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.telephony.CellIdentityLte;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.telephony.CellIdentityWcdma;
416af763bec7c3f4d50fee8dd0046409bb8a7fe8f6Glenn Kastenimport android.telephony.CellInfo;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.telephony.CellInfoCdma;
43957e58670baad8c5995f1368e3b5280f0dbd891fSan Mehatimport android.telephony.CellInfoGsm;
44160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport android.telephony.CellInfoLte;
45a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehatimport android.telephony.CellInfoWcdma;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.telephony.TelephonyManager;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
48160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport com.android.internal.util.Protocol;
49160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport com.android.internal.util.State;
50160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport com.android.internal.util.StateMachine;
51160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport com.android.server.ConnectivityService;
52160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport com.android.server.connectivity.NetworkAgentInfo;
53f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten
54f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kastenimport java.io.IOException;
556793ac943afeb16642f477c43ddfd27e498db37bGlenn Kastenimport java.net.HttpURLConnection;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.net.URL;
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide}
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class NetworkMonitor extends StateMachine {
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final boolean DBG = true;
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "NetworkMonitor";
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String DEFAULT_SERVER = "clients3.google.com";
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int SOCKET_TIMEOUT_MS = 10000;
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            "android.net.conn.NETWORK_CONDITIONS_MEASURED";
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String EXTRA_CELL_ID = "extra_cellid";
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String EXTRA_SSID = "extra_ssid";
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String EXTRA_BSSID = "extra_bssid";
766793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten    /** real time since boot */
77e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
78e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
79e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat
80e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
81e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat            "android.permission.ACCESS_NETWORK_CONDITIONS";
82e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat
83e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    // Intent broadcast when user selects sign-in notification.
84e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    private static final String ACTION_SIGN_IN_REQUESTED =
85e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat            "android.net.netmon.sign_in_requested";
86e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat
87e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    // Keep these in sync with CaptivePortalLoginActivity.java.
88e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    // Intent broadcast from CaptivePortalLogin indicating sign-in is complete.
89e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    // Extras:
90e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    //     EXTRA_TEXT       = netId
91e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    //     LOGGED_IN_RESULT = "1" if we should use network, "0" if not.
92e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
93e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat            "android.net.netmon.captive_portal_logged_in";
94e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    private static final String LOGGED_IN_RESULT = "result";
95e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat
96e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    private static final int BASE = Protocol.BASE_NETWORK_MONITOR;
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Inform NetworkMonitor that their network is connected.
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Initiates Network Validation.
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10280b12fcaaec458377d966803c3a61504f0897ea1Romain Guy    public static final int CMD_NETWORK_CONNECTED = BASE + 1;
10380b12fcaaec458377d966803c3a61504f0897ea1Romain Guy
10480b12fcaaec458377d966803c3a61504f0897ea1Romain Guy    /**
10580b12fcaaec458377d966803c3a61504f0897ea1Romain Guy     * Inform ConnectivityService that the network is validated.
10680b12fcaaec458377d966803c3a61504f0897ea1Romain Guy     * obj = NetworkAgentInfo
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int EVENT_NETWORK_VALIDATED = BASE + 2;
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Inform NetworkMonitor to linger a network.  The Monitor should
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * start a timer and/or start watching for zero live connections while
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * moving towards LINGER_COMPLETE.  After the Linger period expires
114887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn     * (or other events mark the end of the linger state) the LINGER_COMPLETE
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * event should be sent and the network will be shut down.  If a
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * CMD_NETWORK_CONNECTED happens before the LINGER completes
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it indicates further desire to keep the network alive and so
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the LINGER is aborted.
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes    public static final int CMD_NETWORK_LINGER = BASE + 3;
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes     * Message to self indicating linger delay has expired.
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg1 = Token to ignore old messages.
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CMD_LINGER_EXPIRED = BASE + 4;
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Inform ConnectivityService that the network LINGER period has
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * expired.
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * obj = NetworkAgentInfo
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int EVENT_NETWORK_LINGER_COMPLETE = BASE + 5;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Message to self indicating it's time to evaluate a network's connectivity.
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg1 = Token to ignore old messages.
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CMD_REEVALUATE = BASE + 6;
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Message to self indicating network evaluation is complete.
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg1 = Token to ignore old messages.
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg2 = HTTP response code of network evaluation.
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int EVENT_REEVALUATION_COMPLETE = BASE + 7;
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Inform NetworkMonitor that the network has disconnected.
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes    public static final int CMD_NETWORK_DISCONNECTED = BASE + 8;
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes     * Force evaluation even if it has succeeded in the past.
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int CMD_FORCE_REEVALUATION = BASE + 9;
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Message to self indicating captive portal login is complete.
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg1 = Token to ignore old messages.
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg2 = 1 if we should use this network, 0 otherwise.
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CMD_CAPTIVE_PORTAL_LOGGED_IN = BASE + 10;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Message to self indicating user desires to log into captive portal.
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg1 = Token to ignore old messages.
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CMD_USER_WANTS_SIGN_IN = BASE + 11;
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Request ConnectivityService display provisioning notification.
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg1    = Whether to make the notification visible.
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * arg2    = NetID.
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * obj     = Intent to be launched when notification selected by user, null if !arg1.
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 12;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    /**
180e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat     * Message to self indicating sign-in app bypassed captive portal.
181f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten     */
182f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 13;
183f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten
184887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn    /**
185f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten     * Message to self indicating no sign-in app responded.
186e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat     */
187e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat    private static final int EVENT_NO_APP_RESPONSE = BASE + 14;
188e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat
18969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes    /**
1903e458241d9930465a20a861ecb42744355d48e48San Mehat     * Message to self indicating sign-in app indicates sign-in is not possible.
191f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten     */
1923e458241d9930465a20a861ecb42744355d48e48San Mehat    private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 15;
1933e458241d9930465a20a861ecb42744355d48e48San Mehat
1943e458241d9930465a20a861ecb42744355d48e48San Mehat    private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
1953e458241d9930465a20a861ecb42744355d48e48San Mehat    // Default to 30s linger time-out.
1963e458241d9930465a20a861ecb42744355d48e48San Mehat    private static final int DEFAULT_LINGER_DELAY_MS = 30000;
197f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private final int mLingerDelayMs;
1986793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten    private int mLingerToken = 0;
1993e458241d9930465a20a861ecb42744355d48e48San Mehat
2003e458241d9930465a20a861ecb42744355d48e48San Mehat    // Negative values disable reevaluation.
2013e458241d9930465a20a861ecb42744355d48e48San Mehat    private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay";
202f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    // Default to 5s reevaluation delay.
203f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000;
204f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private static final int MAX_RETRIES = 10;
205f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private final int mReevaluateDelayMs;
206f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private int mReevaluateToken = 0;
207f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten
208f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private int mCaptivePortalLoggedInToken = 0;
209a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private int mUserPromptedToken = 0;
210a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat
211a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private final Context mContext;
212a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private final Handler mConnectivityServiceHandler;
213a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private final NetworkAgentInfo mNetworkAgentInfo;
214a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private final TelephonyManager mTelephonyManager;
215a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private final WifiManager mWifiManager;
216a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat
217a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private String mServer;
218a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private boolean mIsCaptivePortalCheckEnabled = false;
219a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat
220a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    public boolean systemReady = false;
221a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat
22269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes    private State mDefaultState = new DefaultState();
223f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private State mOfflineState = new OfflineState();
2245baa3a62a97544669fba6d65a11c07f252e654ddSteve Block    private State mValidatedState = new ValidatedState();
225a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private State mEvaluatingState = new EvaluatingState();
2265baa3a62a97544669fba6d65a11c07f252e654ddSteve Block    private State mUserPromptedState = new UserPromptedState();
227a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private State mCaptivePortalState = new CaptivePortalState();
228a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat    private State mLingeringState = new LingeringState();
2293e458241d9930465a20a861ecb42744355d48e48San Mehat
2303e458241d9930465a20a861ecb42744355d48e48San Mehat    public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) {
2311fd0ec738b0a2b97cc28701aa37b1a9869afc684San Mehat        // Add suffix indicating which NetworkMonitor we're talking about.
2321fd0ec738b0a2b97cc28701aa37b1a9869afc684San Mehat        super(TAG + networkAgentInfo.name());
2336793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten
2343e458241d9930465a20a861ecb42744355d48e48San Mehat        mContext = context;
2353e458241d9930465a20a861ecb42744355d48e48San Mehat        mConnectivityServiceHandler = handler;
2363e458241d9930465a20a861ecb42744355d48e48San Mehat        mNetworkAgentInfo = networkAgentInfo;
2373e458241d9930465a20a861ecb42744355d48e48San Mehat        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
2387e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
2397e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat
2407e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat        addState(mDefaultState);
2413e458241d9930465a20a861ecb42744355d48e48San Mehat        addState(mOfflineState, mDefaultState);
2423e458241d9930465a20a861ecb42744355d48e48San Mehat        addState(mValidatedState, mDefaultState);
2437e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat        addState(mEvaluatingState, mDefaultState);
2447e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat        addState(mUserPromptedState, mDefaultState);
2457e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat        addState(mCaptivePortalState, mDefaultState);
2463762c311729fe9f3af085c14c5c1fb471d994c03Steve Block        addState(mLingeringState, mDefaultState);
2477e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat        setInitialState(mOfflineState);
2487e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat
2491fd0ec738b0a2b97cc28701aa37b1a9869afc684San Mehat        mServer = Settings.Global.getString(mContext.getContentResolver(),
25007b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten                Settings.Global.CAPTIVE_PORTAL_SERVER);
25107b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten        if (mServer == null) mServer = DEFAULT_SERVER;
25207b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten
25307b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten        mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
25407b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten        mReevaluateDelayMs = SystemProperties.getInt(REEVALUATE_DELAY_PROPERTY,
25507b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten                DEFAULT_REEVALUATE_DELAY_MS);
25607b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten
25707b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten        mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
25807b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten                Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
2597e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat
26007b0465095bd9ab3412caefa4fcacbdc3825c64bGlenn Kasten        start();
261f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    }
262f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten
263f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten    private class DefaultState extends State {
264f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten        @Override
2657e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat        public boolean processMessage(Message message) {
26669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            if (DBG) log(getName() + message.toString());
267f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten            switch (message.what) {
268f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten                case CMD_NETWORK_LINGER:
269f1b56449f58963e4f0473d5e26961f68c31759f4Glenn Kasten                    if (DBG) log("Lingering");
2700a42b811aea490a9a605b75f0320101f6eafd283San Mehat                    transitionTo(mLingeringState);
271242d65bf9faf1d2bc3468490e510551140e23462San Mehat                    return HANDLED;
2723e458241d9930465a20a861ecb42744355d48e48San Mehat                case CMD_NETWORK_CONNECTED:
2733e458241d9930465a20a861ecb42744355d48e48San Mehat                    if (DBG) log("Connected");
2743e458241d9930465a20a861ecb42744355d48e48San Mehat                    transitionTo(mEvaluatingState);
2753e458241d9930465a20a861ecb42744355d48e48San Mehat                    return HANDLED;
2769e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                case CMD_NETWORK_DISCONNECTED:
2779e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                    if (DBG) log("Disconnected - quitting");
2789e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                    quit();
2799e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                    return HANDLED;
2809e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                case CMD_FORCE_REEVALUATION:
2819e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                    if (DBG) log("Forcing reevaluation");
2829e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                    transitionTo(mEvaluatingState);
2839e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                    return HANDLED;
2849e57c414f39e1a31349bc215635fdcfaf1902ceeJeff Sharkey                default:
285160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate                    return HANDLED;
286160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            }
287160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        }
288160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate    }
28971f2cf116aab893e224056c38ab146bd1538dd3eSteve Block
290160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate    private class OfflineState extends State {
291160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        @Override
292160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        public boolean processMessage(Message message) {
293160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            if (DBG) log(getName() + message.toString());
294160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            return NOT_HANDLED;
295160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        }
296160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate    }
297160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate
298160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate    private class ValidatedState extends State {
299160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        @Override
300160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        public void enter() {
301160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            if (DBG) log("Validated");
3026793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten            mConnectivityServiceHandler.sendMessage(
3036793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten                    obtainMessage(EVENT_NETWORK_VALIDATED, mNetworkAgentInfo));
3046793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten        }
305cc767191cfb675f744e0165608b0a4196aba2b37Glenn Kasten
3066793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten        @Override
3076793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten        public boolean processMessage(Message message) {
3086793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten            if (DBG) log(getName() + message.toString());
3096793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten            switch (message.what) {
3106793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten                case CMD_NETWORK_CONNECTED:
3116793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten                    transitionTo(mValidatedState);
312cc767191cfb675f744e0165608b0a4196aba2b37Glenn Kasten                    return HANDLED;
313cc767191cfb675f744e0165608b0a4196aba2b37Glenn Kasten                default:
314cc767191cfb675f744e0165608b0a4196aba2b37Glenn Kasten                    return NOT_HANDLED;
3156793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten            }
3166793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten        }
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class EvaluatingState extends State {
320160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        private int mRetries;
321160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate
322160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        private class EvaluateInternetConnectivity extends Thread {
323160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            private int mToken;
324160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            EvaluateInternetConnectivity(int token) {
325160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate                mToken = token;
326160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            }
3273762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            public void run() {
328160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate                sendMessage(EVENT_REEVALUATION_COMPLETE, mToken, isCaptivePortal());
329160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            }
330160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        }
331160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate
332160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        @Override
333160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate        public void enter() {
334160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate            mRetries = 0;
335887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn            sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
336887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn        }
337887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn
3386793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten        @Override
339887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn        public boolean processMessage(Message message) {
3406793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten            if (DBG) log(getName() + message.toString());
341887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn            switch (message.what) {
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case CMD_REEVALUATE:
34369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                    if (message.arg1 != mReevaluateToken)
3446215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block                        return HANDLED;
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (mNetworkAgentInfo.isVPN()) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        transitionTo(mValidatedState);
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return HANDLED;
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // If network provides no internet connectivity adjust evaluation.
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (!mNetworkAgentInfo.networkCapabilities.hasCapability(
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // TODO: Try to verify something works.  Do all gateways respond to pings?
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        transitionTo(mValidatedState);
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return HANDLED;
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Kick off a thread to perform internet connectivity evaluation.
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Thread thread = new EvaluateInternetConnectivity(mReevaluateToken);
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    thread.run();
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return HANDLED;
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case EVENT_REEVALUATION_COMPLETE:
3616793ac943afeb16642f477c43ddfd27e498db37bGlenn Kasten                    if (message.arg1 != mReevaluateToken)
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return HANDLED;
3636215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block                    int httpResponseCode = message.arg2;
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (httpResponseCode == 204) {
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        transitionTo(mValidatedState);
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        transitionTo(mUserPromptedState);
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (++mRetries > MAX_RETRIES) {
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        transitionTo(mOfflineState);
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (mReevaluateDelayMs >= 0) {
37110e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown                        Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
37210e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown                        sendMessageDelayed(msg, mReevaluateDelayMs);
37310e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown                    }
37410e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown                    return HANDLED;
37510e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown                default:
37610e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown                    return NOT_HANDLED;
37710e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown            }
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
37910e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown    }
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class UserPromptedState extends State {
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private class UserRespondedBroadcastReceiver extends BroadcastReceiver {
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            private final int mToken;
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            UserRespondedBroadcastReceiver(int token) {
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mToken = token;
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
38769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            @Override
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onReceive(Context context, Intent intent) {
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
39069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                        mNetworkAgentInfo.network.netId) {
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sendMessage(obtainMessage(CMD_USER_WANTS_SIGN_IN, mToken));
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private UserRespondedBroadcastReceiver mUserRespondedBroadcastReceiver;
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void enter() {
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Wait for user to select sign-in notifcation.
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mUserRespondedBroadcastReceiver = new UserRespondedBroadcastReceiver(
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ++mUserPromptedToken);
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            IntentFilter filter = new IntentFilter(ACTION_SIGN_IN_REQUESTED);
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mContext.registerReceiver(mUserRespondedBroadcastReceiver, filter);
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Initiate notification to sign-in.
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Intent intent = new Intent(ACTION_SIGN_IN_REQUESTED);
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetworkAgentInfo.network.netId));
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mNetworkAgentInfo.network.netId,
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mConnectivityServiceHandler.sendMessage(message);
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
4156215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block        public boolean processMessage(Message message) {
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (DBG) log(getName() + message.toString());
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (message.what) {
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case CMD_USER_WANTS_SIGN_IN:
41959325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn                    if (message.arg1 != mUserPromptedToken)
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return HANDLED;
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    transitionTo(mCaptivePortalState);
42269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                    return HANDLED;
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                default:
4248564c8da817a845353d213acd8636b76f567b234Steve Block                    return NOT_HANDLED;
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
42769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void exit() {
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
43169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                    mNetworkAgentInfo.network.netId, null);
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mConnectivityServiceHandler.sendMessage(message);
4338564c8da817a845353d213acd8636b76f567b234Steve Block            mContext.unregisterReceiver(mUserRespondedBroadcastReceiver);
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mUserRespondedBroadcastReceiver = null;
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class CaptivePortalState extends State {
4390bca96bcbfe559f9330a01f723c5c9cba51ec05aMarco Nelissen        private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver {
44069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            private final int mToken;
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
44259325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            CaptivePortalLoggedInBroadcastReceiver(int token) {
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mToken = token;
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @Override
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onReceive(Context context, Intent intent) {
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mNetworkAgentInfo.network.netId) {
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sendMessage(obtainMessage(CMD_CAPTIVE_PORTAL_LOGGED_IN, mToken,
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            Integer.parseInt(intent.getStringExtra(LOGGED_IN_RESULT))));
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4550bca96bcbfe559f9330a01f723c5c9cba51ec05aMarco Nelissen
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver;
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void enter() {
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Intent intent = new Intent(Intent.ACTION_SEND);
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetworkAgentInfo.network.netId));
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            intent.setType("text/plain");
46369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            intent.setComponent(new ComponentName("com.android.captiveportallogin",
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    "com.android.captiveportallogin.CaptivePortalLoginActivity"));
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
46759325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            // Wait for result.
46859325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            mCaptivePortalLoggedInBroadcastReceiver = new CaptivePortalLoggedInBroadcastReceiver(
46959325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn                    ++mCaptivePortalLoggedInToken);
47059325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
47159325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter);
47259325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            // Initiate app to log in.
47359325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
47459325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn        }
47559325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn
47659325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn        @Override
47759325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn        public boolean processMessage(Message message) {
47859325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            if (DBG) log(getName() + message.toString());
47959325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn            switch (message.what) {
48059325eb31f25704bb88c348160bb69e7c1aa3b48Dianne Hackborn                case CMD_CAPTIVE_PORTAL_LOGGED_IN:
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (message.arg1 != mCaptivePortalLoggedInToken)
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return HANDLED;
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (message.arg2 == 0) {
4846215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block                        // TODO: Should teardown network.
48569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                        transitionTo(mOfflineState);
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
48769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                        transitionTo(mValidatedState);
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return HANDLED;
49069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                default:
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return NOT_HANDLED;
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void exit() {
49769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver);
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCaptivePortalLoggedInBroadcastReceiver = null;
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class LingeringState extends State {
50369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes        @Override
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void enter() {
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Message message = obtainMessage(CMD_LINGER_EXPIRED, ++mLingerToken, 0);
50669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            sendMessageDelayed(message, mLingerDelayMs);
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean processMessage(Message message) {
5116215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block            if (DBG) log(getName() + message.toString());
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (message.what) {
51369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                case CMD_NETWORK_CONNECTED:
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Go straight to active as we've already evaluated.
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    transitionTo(mValidatedState);
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return HANDLED;
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case CMD_LINGER_EXPIRED:
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (message.arg1 != mLingerToken)
51969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                        return HANDLED;
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mConnectivityServiceHandler.sendMessage(
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo));
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return HANDLED;
52369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                default:
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return NOT_HANDLED;
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
52869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
5296215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block    /**
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Do a URL fetch on a known server to see if we get the data we expect.
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns HTTP response code.
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
53369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes    private int isCaptivePortal() {
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!mIsCaptivePortalCheckEnabled) return 204;
53569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        HttpURLConnection urlConnection = null;
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int httpResponseCode = 599;
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            URL url = new URL("http", mServer, "/generate_204");
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (DBG) {
54169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                log("Checking " + url.toString() + " on " +
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mNetworkAgentInfo.networkInfo.getExtraInfo());
5438564c8da817a845353d213acd8636b76f567b234Steve Block            }
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            url = mNetworkAgentInfo.network.getBoundURL(url);
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            urlConnection = (HttpURLConnection) url.openConnection();
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            urlConnection.setInstanceFollowRedirects(false);
54769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
54969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            urlConnection.setUseCaches(false);
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Time how long it takes to get a response to our request
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long requestTimestamp = SystemClock.elapsedRealtime();
5536215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            urlConnection.getInputStream();
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Time how long it takes to get a response to our request
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long responseTimestamp = SystemClock.elapsedRealtime();
558add868cebaf62cffe96e79764ea0b7f2320a03ebAmith Yamasani
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            httpResponseCode = urlConnection.getResponseCode();
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (DBG) {
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                log("isCaptivePortal: ret=" + httpResponseCode +
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        " headers=" + urlConnection.getHeaderFields());
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // portal.  The only example of this seen so far was a captive portal.  For
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the time being go with prior behavior of assuming it's not a captive
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // portal.  If it is considered a captive portal, a different sign-in URL
5686215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block            // is needed (i.e. can't browse a 204).  This could be the result of an HTTP
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // proxy server.
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Consider 200 response with "Content-length=0" to not be a captive portal.
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // There's no point in considering this a captive portal as the user cannot
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // sign-in to an empty page.  Probably the result of a broken transparent proxy.
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // See http://b/9972012.
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) {
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (DBG) log("Empty 200 response interpreted as 204 response.");
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                httpResponseCode = 204;
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sendNetworkConditionsBroadcast(true /* response received */, httpResponseCode == 204,
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    requestTimestamp, responseTimestamp);
58269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes        } catch (IOException e) {
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (DBG) log("Probably not a portal: exception " + e);
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (httpResponseCode == 599) {
5858564c8da817a845353d213acd8636b76f567b234Steve Block                // TODO: Ping gateway and DNS server and log results.
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
58769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes        } finally {
5886215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block            if (urlConnection != null) {
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                urlConnection.disconnect();
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return httpResponseCode;
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
59669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes     * @param responseReceived - whether or not we received a valid HTTP response to our request.
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If false, isCaptivePortal and responseTimestampMs are ignored
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * TODO: This should be moved to the transports.  The latency could be passed to the transports
59969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes     * along with the captive portal result.  Currently the TYPE_MOBILE broadcasts appear unused so
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * perhaps this could just be added to the WiFi transport only.
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long requestTimestampMs, long responseTimestampMs) {
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (Settings.Global.getInt(mContext.getContentResolver(),
60569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0) {
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (DBG) log("Don't send network conditions - lacking user consent.");
60769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            return;
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
60969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (systemReady == false) return;
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED);
61369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes        switch (mNetworkAgentInfo.networkInfo.getType()) {
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case ConnectivityManager.TYPE_WIFI:
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (currentWifiInfo != null) {
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // surrounded by double quotation marks (thus violating the Javadoc), but this
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // was changed to match the Javadoc in API 17. Since clients may have started
62069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                    // sanitizing the output of this method since API 17 was released, we should
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // not change it here as it would become impossible to tell whether the SSID is
62269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                    // simply being surrounded by quotes due to the API, or whether those quotes
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // are actually part of the SSID.
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID());
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID());
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (DBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return;
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
63169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            case ConnectivityManager.TYPE_MOBILE:
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                latencyBroadcast.putExtra(EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType());
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                List<CellInfo> info = mTelephonyManager.getAllCellInfo();
6346215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block                if (info == null) return;
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int numRegisteredCellInfo = 0;
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (CellInfo cellInfo : info) {
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (cellInfo.isRegistered()) {
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        numRegisteredCellInfo++;
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (numRegisteredCellInfo > 1) {
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (DBG) log("more than one registered CellInfo.  Can't " +
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    "tell which is active.  Bailing.");
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            return;
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (cellInfo instanceof CellInfoCdma) {
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else if (cellInfo instanceof CellInfoGsm) {
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else if (cellInfo instanceof CellInfoLte) {
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
65269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else if (cellInfo instanceof CellInfoWcdma) {
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
65669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                        } else {
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (DBG) logw("Registered cellinfo is unrecognized");
65869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                            return;
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
66269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes                break;
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            default:
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        latencyBroadcast.putExtra(EXTRA_CONNECTIVITY_TYPE, mNetworkAgentInfo.networkInfo.getType());
66769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes        latencyBroadcast.putExtra(EXTRA_RESPONSE_RECEIVED, responseReceived);
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        latencyBroadcast.putExtra(EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs);
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (responseReceived) {
67169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            latencyBroadcast.putExtra(EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal);
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            latencyBroadcast.putExtra(EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs);
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.sendBroadcast(latencyBroadcast, PERMISSION_ACCESS_NETWORK_CONDITIONS);
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project