1ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen/* 2ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Copyright (C) 2014 The Android Open Source Project 3ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * 4ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Licensed under the Apache License, Version 2.0 (the "License"); 5ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * you may not use this file except in compliance with the License. 6ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * You may obtain a copy of the License at 7ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * 8ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * http://www.apache.org/licenses/LICENSE-2.0 9ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * 10ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Unless required by applicable law or agreed to in writing, software 11ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * distributed under the License is distributed on an "AS IS" BASIS, 12ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * See the License for the specific language governing permissions and 14ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * limitations under the License. 15ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 16ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 17ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenpackage com.android.server.connectivity; 18ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 1979a08051c5588d8420656813b21993d490e93dd0Paul Jensenimport android.app.AlarmManager; 20869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.app.PendingIntent; 21869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.content.BroadcastReceiver; 22869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.content.ComponentName; 23ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport android.content.Context; 24869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.content.Intent; 25869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.content.IntentFilter; 26869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.ConnectivityManager; 27869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.Network; 28ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport android.net.NetworkCapabilities; 29ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport android.net.NetworkInfo; 307ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensenimport android.net.TrafficStats; 31306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.net.wifi.WifiInfo; 32306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.net.wifi.WifiManager; 33ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport android.os.Handler; 34ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport android.os.Message; 35306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.os.SystemClock; 36ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport android.os.SystemProperties; 37869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.os.UserHandle; 38ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport android.provider.Settings; 39306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellIdentityCdma; 40306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellIdentityGsm; 41306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellIdentityLte; 42306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellIdentityWcdma; 43306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellInfo; 44306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellInfoCdma; 45306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellInfoGsm; 46306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellInfoLte; 47306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.CellInfoWcdma; 48306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport android.telephony.TelephonyManager; 49ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 50ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport com.android.internal.util.Protocol; 51ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport com.android.internal.util.State; 52ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport com.android.internal.util.StateMachine; 53869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport com.android.server.ConnectivityService; 54ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport com.android.server.connectivity.NetworkAgentInfo; 55ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 56ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport java.io.IOException; 57ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport java.net.HttpURLConnection; 58ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenimport java.net.URL; 59306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensenimport java.util.List; 60ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 61ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen/** 62ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * {@hide} 63ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 64ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensenpublic class NetworkMonitor extends StateMachine { 65ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final boolean DBG = true; 66ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final String TAG = "NetworkMonitor"; 67ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final String DEFAULT_SERVER = "clients3.google.com"; 68ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final int SOCKET_TIMEOUT_MS = 10000; 69306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String ACTION_NETWORK_CONDITIONS_MEASURED = 70306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen "android.net.conn.NETWORK_CONDITIONS_MEASURED"; 71306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type"; 72306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_NETWORK_TYPE = "extra_network_type"; 73306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received"; 74306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal"; 75306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_CELL_ID = "extra_cellid"; 76306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_SSID = "extra_ssid"; 77306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_BSSID = "extra_bssid"; 78306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen /** real time since boot */ 79306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms"; 80306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms"; 81306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 82306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS = 83306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen "android.permission.ACCESS_NETWORK_CONDITIONS"; 84ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 85869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Keep these in sync with CaptivePortalLoginActivity.java. 86869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Intent broadcast from CaptivePortalLogin indicating sign-in is complete. 87869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Extras: 88869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // EXTRA_TEXT = netId 89869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // LOGGED_IN_RESULT = "1" if we should use network, "0" if not. 90869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN = 91869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen "android.net.netmon.captive_portal_logged_in"; 92869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private static final String LOGGED_IN_RESULT = "result"; 93869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 94ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. 95ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // The network should be used as a default internet connection. It was found to be: 96ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // 1. a functioning network providing internet access, or 97ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // 2. a captive portal and the user decided to use it as is. 98ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen public static final int NETWORK_TEST_RESULT_VALID = 0; 99ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. 100ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // The network should not be used as a default internet connection. It was found to be: 101ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // 1. a captive portal and the user is prompted to sign-in, or 102ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // 2. a captive portal and the user did not want to use it, or 103ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed). 104ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen public static final int NETWORK_TEST_RESULT_INVALID = 1; 105ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen 106ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final int BASE = Protocol.BASE_NETWORK_MONITOR; 107ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 108ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 109ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Inform NetworkMonitor that their network is connected. 110ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Initiates Network Validation. 111ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 112ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public static final int CMD_NETWORK_CONNECTED = BASE + 1; 113ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 114ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 115ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen * Inform ConnectivityService that the network has been tested. 116ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * obj = NetworkAgentInfo 117ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen * arg1 = One of the NETWORK_TESTED_RESULT_* constants. 118ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 119ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen public static final int EVENT_NETWORK_TESTED = BASE + 2; 120ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 121ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 122ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Inform NetworkMonitor to linger a network. The Monitor should 123ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * start a timer and/or start watching for zero live connections while 124ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * moving towards LINGER_COMPLETE. After the Linger period expires 125ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * (or other events mark the end of the linger state) the LINGER_COMPLETE 126ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * event should be sent and the network will be shut down. If a 127ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * CMD_NETWORK_CONNECTED happens before the LINGER completes 128ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * it indicates further desire to keep the network alive and so 129ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * the LINGER is aborted. 130ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 131ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public static final int CMD_NETWORK_LINGER = BASE + 3; 132ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 133ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 134ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Message to self indicating linger delay has expired. 135ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * arg1 = Token to ignore old messages. 136ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 137ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final int CMD_LINGER_EXPIRED = BASE + 4; 138ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 139ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 140ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Inform ConnectivityService that the network LINGER period has 141ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * expired. 142ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * obj = NetworkAgentInfo 143ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 144ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public static final int EVENT_NETWORK_LINGER_COMPLETE = BASE + 5; 145ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 146ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 147ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Message to self indicating it's time to evaluate a network's connectivity. 148ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * arg1 = Token to ignore old messages. 149ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 150869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private static final int CMD_REEVALUATE = BASE + 6; 151ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 152ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 153ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Inform NetworkMonitor that the network has disconnected. 154ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 1557ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen public static final int CMD_NETWORK_DISCONNECTED = BASE + 7; 156ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 157ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 158ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Force evaluation even if it has succeeded in the past. 1597ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen * arg1 = UID responsible for requesting this reeval. Will be billed for data. 160ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 1617ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen public static final int CMD_FORCE_REEVALUATION = BASE + 8; 162869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 163869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen /** 164869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Message to self indicating captive portal login is complete. 165869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * arg1 = Token to ignore old messages. 166869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * arg2 = 1 if we should use this network, 0 otherwise. 167869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */ 1687ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen private static final int CMD_CAPTIVE_PORTAL_LOGGED_IN = BASE + 9; 169869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 170869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen /** 171869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Message to self indicating user desires to log into captive portal. 172869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * arg1 = Token to ignore old messages. 173869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */ 1747ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen private static final int CMD_USER_WANTS_SIGN_IN = BASE + 10; 175869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 176869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen /** 177869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Request ConnectivityService display provisioning notification. 178869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * arg1 = Whether to make the notification visible. 179fdc4e4af7305514359e041dbec742f4b6561e393Paul Jensen * arg2 = NetID. 180fdc4e4af7305514359e041dbec742f4b6561e393Paul Jensen * obj = Intent to be launched when notification selected by user, null if !arg1. 181869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */ 1827ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 11; 183869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 184869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen /** 185869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Message to self indicating sign-in app bypassed captive portal. 186869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */ 1877ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 12; 188869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 189869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen /** 190869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Message to self indicating no sign-in app responded. 191869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */ 1927ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen private static final int EVENT_NO_APP_RESPONSE = BASE + 13; 193869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 194869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen /** 195869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Message to self indicating sign-in app indicates sign-in is not possible. 196869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */ 1977ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 14; 198ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 199ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; 200ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen // Default to 30s linger time-out. 201ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final int DEFAULT_LINGER_DELAY_MS = 30000; 202ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private final int mLingerDelayMs; 203ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private int mLingerToken = 0; 204ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 205ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen // Negative values disable reevaluation. 206ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay"; 207ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen // Default to 5s reevaluation delay. 208ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000; 209869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private static final int MAX_RETRIES = 10; 210ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private final int mReevaluateDelayMs; 211ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private int mReevaluateToken = 0; 2127ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen private static final int INVALID_UID = -1; 2137ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen private int mUidResponsibleForReeval = INVALID_UID; 214ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 215869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private int mCaptivePortalLoggedInToken = 0; 216869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private int mUserPromptedToken = 0; 217869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 218ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private final Context mContext; 219ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private final Handler mConnectivityServiceHandler; 220ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private final NetworkAgentInfo mNetworkAgentInfo; 221306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen private final TelephonyManager mTelephonyManager; 222306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen private final WifiManager mWifiManager; 22379a08051c5588d8420656813b21993d490e93dd0Paul Jensen private final AlarmManager mAlarmManager; 224ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 225ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private String mServer; 226ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private boolean mIsCaptivePortalCheckEnabled = false; 227ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 228ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. 229ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen private boolean mUserDoesNotWant = false; 230ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen 231fb68f8fbe0213841f393f8bdb5313e4e44f4f116Robert Greenwalt public boolean systemReady = false; 232fb68f8fbe0213841f393f8bdb5313e4e44f4f116Robert Greenwalt 233ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private State mDefaultState = new DefaultState(); 234ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private State mOfflineState = new OfflineState(); 235ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private State mValidatedState = new ValidatedState(); 236ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private State mEvaluatingState = new EvaluatingState(); 237869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private State mUserPromptedState = new UserPromptedState(); 238ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private State mCaptivePortalState = new CaptivePortalState(); 239ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private State mLingeringState = new LingeringState(); 240ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 241ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) { 242ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen // Add suffix indicating which NetworkMonitor we're talking about. 243ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen super(TAG + networkAgentInfo.name()); 244ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 245ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen mContext = context; 246ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen mConnectivityServiceHandler = handler; 247ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen mNetworkAgentInfo = networkAgentInfo; 248306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 249306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 25079a08051c5588d8420656813b21993d490e93dd0Paul Jensen mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 251ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 252ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen addState(mDefaultState); 253ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen addState(mOfflineState, mDefaultState); 254ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen addState(mValidatedState, mDefaultState); 255ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen addState(mEvaluatingState, mDefaultState); 256869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen addState(mUserPromptedState, mDefaultState); 257ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen addState(mCaptivePortalState, mDefaultState); 258ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen addState(mLingeringState, mDefaultState); 25949f63fbed4cd84f5da182c85e8b999037dc64f3bRobert Greenwalt setInitialState(mDefaultState); 260ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 261ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen mServer = Settings.Global.getString(mContext.getContentResolver(), 262ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen Settings.Global.CAPTIVE_PORTAL_SERVER); 263ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (mServer == null) mServer = DEFAULT_SERVER; 264ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 265ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); 266ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen mReevaluateDelayMs = SystemProperties.getInt(REEVALUATE_DELAY_PROPERTY, 267ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen DEFAULT_REEVALUATE_DELAY_MS); 268ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 269869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), 270869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1; 271ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 272ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen start(); 273ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 274ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 275ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private class DefaultState extends State { 276ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 277ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public boolean processMessage(Message message) { 278ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log(getName() + message.toString()); 279ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen switch (message.what) { 280ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_NETWORK_LINGER: 281ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log("Lingering"); 282ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen transitionTo(mLingeringState); 283d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 284ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_NETWORK_CONNECTED: 285ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log("Connected"); 286ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen transitionTo(mEvaluatingState); 287d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 288ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_NETWORK_DISCONNECTED: 2891fd9aeef08fac363ec3ef2eb61cea519a04c51fdRobert Greenwalt if (DBG) log("Disconnected - quitting"); 2901fd9aeef08fac363ec3ef2eb61cea519a04c51fdRobert Greenwalt quit(); 291d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 292ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_FORCE_REEVALUATION: 293ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log("Forcing reevaluation"); 2947ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen mUidResponsibleForReeval = message.arg1; 295ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen transitionTo(mEvaluatingState); 296d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 297ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen default: 298d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 299ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 300ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 301ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 302ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 303ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private class OfflineState extends State { 304ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 305ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen public void enter() { 306ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, 307ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo)); 308ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen } 309ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen 310ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen @Override 311ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public boolean processMessage(Message message) { 312ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log(getName() + message.toString()); 313ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen switch (message.what) { 314ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen case CMD_FORCE_REEVALUATION: 315ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // If the user has indicated they explicitly do not want to use this network, 316ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // don't allow a reevaluation as this will be pointless and could result in 317ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // the user being annoyed with repeated unwanted notifications. 318ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen return mUserDoesNotWant ? HANDLED : NOT_HANDLED; 319ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen default: 320ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen return NOT_HANDLED; 321ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen } 322ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 323ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 324ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 325ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private class ValidatedState extends State { 326ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 327ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public void enter() { 328ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log("Validated"); 329ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, 330ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo)); 331ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 332ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 333ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 334ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public boolean processMessage(Message message) { 335ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log(getName() + message.toString()); 336ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen switch (message.what) { 337ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_NETWORK_CONNECTED: 338ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen transitionTo(mValidatedState); 339d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 340ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen default: 341ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen return NOT_HANDLED; 342ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 343ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 344ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 345ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 346ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private class EvaluatingState extends State { 347869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private int mRetries; 348869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 349ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 350ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public void enter() { 351869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mRetries = 0; 352ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); 3537ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen if (mUidResponsibleForReeval != INVALID_UID) { 3547ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen TrafficStats.setThreadStatsUid(mUidResponsibleForReeval); 3557ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen mUidResponsibleForReeval = INVALID_UID; 3567ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen } 357ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 358ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 359ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 360ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public boolean processMessage(Message message) { 361ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log(getName() + message.toString()); 362ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen switch (message.what) { 363ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_REEVALUATE: 364ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (message.arg1 != mReevaluateToken) 365d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 3666bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen if (mNetworkAgentInfo.isVPN()) { 3676bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen transitionTo(mValidatedState); 368d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 3696bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen } 370ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen // If network provides no internet connectivity adjust evaluation. 371869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (!mNetworkAgentInfo.networkCapabilities.hasCapability( 372ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen NetworkCapabilities.NET_CAPABILITY_INTERNET)) { 373ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen // TODO: Try to verify something works. Do all gateways respond to pings? 374ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen transitionTo(mValidatedState); 375d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 376ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 3777ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen int httpResponseCode = isCaptivePortal(); 378ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (httpResponseCode == 204) { 379ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen transitionTo(mValidatedState); 380ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } else if (httpResponseCode >= 200 && httpResponseCode <= 399) { 381a68d21350266a6abc16aeb3fec84eafd85b77cc8Paul Jensen transitionTo(mUserPromptedState); 382869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } else if (++mRetries > MAX_RETRIES) { 383869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen transitionTo(mOfflineState); 384869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } else if (mReevaluateDelayMs >= 0) { 385869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); 386869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen sendMessageDelayed(msg, mReevaluateDelayMs); 387ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 388d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 3897ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen case CMD_FORCE_REEVALUATION: 3907ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen // Ignore duplicate requests. 3917ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen return HANDLED; 392ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen default: 393ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen return NOT_HANDLED; 394ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 395ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 3967ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen 3977ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen @Override 3987ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen public void exit() { 3997ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen TrafficStats.clearThreadStatsUid(); 4007ccd3dfd53d8d45c447398ff137f052865dfd3b3Paul Jensen } 401ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 402ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 403dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen // BroadcastReceiver that waits for a particular Intent and then posts a message. 404dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen private class CustomIntentReceiver extends BroadcastReceiver { 405dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen private final Message mMessage; 406dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen private final String mAction; 407dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen CustomIntentReceiver(String action, int token, int message) { 408dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen mMessage = obtainMessage(message, token); 409dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen mAction = action + "_" + mNetworkAgentInfo.network.netId + "_" + token; 410dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen mContext.registerReceiver(this, new IntentFilter(mAction)); 411dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen } 412dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen public PendingIntent getPendingIntent() { 413dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen return PendingIntent.getBroadcast(mContext, 0, new Intent(mAction), 0); 414dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen } 415dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen @Override 416dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen public void onReceive(Context context, Intent intent) { 417dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen if (intent.getAction().equals(mAction)) sendMessage(mMessage); 418869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 419dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen } 420dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen 421dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen private class UserPromptedState extends State { 422dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen // Intent broadcast when user selects sign-in notification. 423dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen private static final String ACTION_SIGN_IN_REQUESTED = 424dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen "android.net.netmon.sign_in_requested"; 425869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 426dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen private CustomIntentReceiver mUserRespondedBroadcastReceiver; 427869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 428869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 429869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void enter() { 430ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, 431ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo)); 432869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Wait for user to select sign-in notifcation. 433dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen mUserRespondedBroadcastReceiver = new CustomIntentReceiver(ACTION_SIGN_IN_REQUESTED, 434dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen ++mUserPromptedToken, CMD_USER_WANTS_SIGN_IN); 435869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Initiate notification to sign-in. 436fdc4e4af7305514359e041dbec742f4b6561e393Paul Jensen Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1, 437fdc4e4af7305514359e041dbec742f4b6561e393Paul Jensen mNetworkAgentInfo.network.netId, 438dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen mUserRespondedBroadcastReceiver.getPendingIntent()); 439869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mConnectivityServiceHandler.sendMessage(message); 440869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 441869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 442869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 443869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public boolean processMessage(Message message) { 444869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (DBG) log(getName() + message.toString()); 445869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen switch (message.what) { 446869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen case CMD_USER_WANTS_SIGN_IN: 447869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (message.arg1 != mUserPromptedToken) 448d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 449a68d21350266a6abc16aeb3fec84eafd85b77cc8Paul Jensen transitionTo(mCaptivePortalState); 450d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 451869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen default: 452869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen return NOT_HANDLED; 453869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 454869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 455869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 456869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 457869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void exit() { 458fdc4e4af7305514359e041dbec742f4b6561e393Paul Jensen Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0, 459fdc4e4af7305514359e041dbec742f4b6561e393Paul Jensen mNetworkAgentInfo.network.netId, null); 460869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mConnectivityServiceHandler.sendMessage(message); 461869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mContext.unregisterReceiver(mUserRespondedBroadcastReceiver); 462869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mUserRespondedBroadcastReceiver = null; 463869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 464869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 465869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 466ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private class CaptivePortalState extends State { 467869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver { 468869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private final int mToken; 469869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 470869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen CaptivePortalLoggedInBroadcastReceiver(int token) { 471869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mToken = token; 472869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 473869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 474869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 475869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void onReceive(Context context, Intent intent) { 476869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) == 477869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mNetworkAgentInfo.network.netId) { 478869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen sendMessage(obtainMessage(CMD_CAPTIVE_PORTAL_LOGGED_IN, mToken, 479869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen Integer.parseInt(intent.getStringExtra(LOGGED_IN_RESULT)))); 480869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 481869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 482869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 483869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 484869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver; 485869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 486ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 487ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public void enter() { 488869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen Intent intent = new Intent(Intent.ACTION_SEND); 489869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetworkAgentInfo.network.netId)); 490869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen intent.setType("text/plain"); 491869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen intent.setComponent(new ComponentName("com.android.captiveportallogin", 492869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen "com.android.captiveportallogin.CaptivePortalLoginActivity")); 493869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); 494869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 495869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Wait for result. 496869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mCaptivePortalLoggedInBroadcastReceiver = new CaptivePortalLoggedInBroadcastReceiver( 497869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen ++mCaptivePortalLoggedInToken); 498869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN); 499869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter); 500869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // Initiate app to log in. 501869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mContext.startActivityAsUser(intent, UserHandle.CURRENT); 502ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 503ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 504ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 505ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public boolean processMessage(Message message) { 506ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log(getName() + message.toString()); 507ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen switch (message.what) { 508869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen case CMD_CAPTIVE_PORTAL_LOGGED_IN: 509869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (message.arg1 != mCaptivePortalLoggedInToken) 510d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 511869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (message.arg2 == 0) { 512ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen mUserDoesNotWant = true; 513869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // TODO: Should teardown network. 514869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen transitionTo(mOfflineState); 515869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } else { 516869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen transitionTo(mValidatedState); 517869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 518d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 519ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen default: 520ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen return NOT_HANDLED; 521ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 522ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 523869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen 524869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen @Override 525869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen public void exit() { 526869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver); 527869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen mCaptivePortalLoggedInBroadcastReceiver = null; 528869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 529ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 530ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 531ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private class LingeringState extends State { 53279a08051c5588d8420656813b21993d490e93dd0Paul Jensen private static final String ACTION_LINGER_EXPIRED = "android.net.netmon.lingerExpired"; 53379a08051c5588d8420656813b21993d490e93dd0Paul Jensen 534dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen private CustomIntentReceiver mBroadcastReceiver; 53579a08051c5588d8420656813b21993d490e93dd0Paul Jensen private PendingIntent mIntent; 53679a08051c5588d8420656813b21993d490e93dd0Paul Jensen 537ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 538ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public void enter() { 539dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen mBroadcastReceiver = new CustomIntentReceiver(ACTION_LINGER_EXPIRED, ++mLingerToken, 540dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen CMD_LINGER_EXPIRED); 541dcbe8356138bfba3f4bad31c1a7ad036b86f47f4Paul Jensen mIntent = mBroadcastReceiver.getPendingIntent(); 54279a08051c5588d8420656813b21993d490e93dd0Paul Jensen long wakeupTime = SystemClock.elapsedRealtime() + mLingerDelayMs; 54379a08051c5588d8420656813b21993d490e93dd0Paul Jensen mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime, 54479a08051c5588d8420656813b21993d490e93dd0Paul Jensen // Give a specific window so we aren't subject to unknown inexactitude. 54579a08051c5588d8420656813b21993d490e93dd0Paul Jensen mLingerDelayMs / 6, mIntent); 546ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 547ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 548ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen @Override 549ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen public boolean processMessage(Message message) { 550ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log(getName() + message.toString()); 551ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen switch (message.what) { 552ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_NETWORK_CONNECTED: 553ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen // Go straight to active as we've already evaluated. 554ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen transitionTo(mValidatedState); 555d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 556ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen case CMD_LINGER_EXPIRED: 557ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (message.arg1 != mLingerToken) 558d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 559ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen mConnectivityServiceHandler.sendMessage( 560ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo)); 561d6a3f7ed90d7fa861f579968f12cc30015b3a989Paul Jensen return HANDLED; 562ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen case CMD_FORCE_REEVALUATION: 563ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // Ignore reevaluation attempts when lingering. A reevaluation could result 564ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // in a transition to the validated state which would abort the linger 565ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // timeout. Lingering is the result of score assessment; validity is 566ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen // irrelevant. 567ad50a1fed01e7c24531ad26d9de70c7b0127ea76Paul Jensen return HANDLED; 568ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen default: 569ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen return NOT_HANDLED; 570ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 571ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 57279a08051c5588d8420656813b21993d490e93dd0Paul Jensen 57379a08051c5588d8420656813b21993d490e93dd0Paul Jensen @Override 57479a08051c5588d8420656813b21993d490e93dd0Paul Jensen public void exit() { 57579a08051c5588d8420656813b21993d490e93dd0Paul Jensen mAlarmManager.cancel(mIntent); 57679a08051c5588d8420656813b21993d490e93dd0Paul Jensen mContext.unregisterReceiver(mBroadcastReceiver); 57779a08051c5588d8420656813b21993d490e93dd0Paul Jensen } 578ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 579ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 580ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen /** 581ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Do a URL fetch on a known server to see if we get the data we expect. 582ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen * Returns HTTP response code. 583ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen */ 584ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen private int isCaptivePortal() { 585ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (!mIsCaptivePortalCheckEnabled) return 204; 586ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen 587ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen HttpURLConnection urlConnection = null; 588869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen int httpResponseCode = 599; 589ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen try { 590e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen URL url = new URL("http", mServer, "/generate_204"); 591e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen if (DBG) { 592e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen log("Checking " + url.toString() + " on " + 593e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen mNetworkAgentInfo.networkInfo.getExtraInfo()); 594e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen } 5959f1274b7e43d14c7e3a42148ebfda3905fec8b06Lorenzo Colitti urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url); 596e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen urlConnection.setInstanceFollowRedirects(false); 597e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); 598e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); 599e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen urlConnection.setUseCaches(false); 600306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 601306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // Time how long it takes to get a response to our request 602306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen long requestTimestamp = SystemClock.elapsedRealtime(); 603306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 604e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen urlConnection.getInputStream(); 605306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 606306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // Time how long it takes to get a response to our request 607306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen long responseTimestamp = SystemClock.elapsedRealtime(); 608306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 609e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen httpResponseCode = urlConnection.getResponseCode(); 610e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen if (DBG) { 611e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen log("isCaptivePortal: ret=" + httpResponseCode + 612e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen " headers=" + urlConnection.getHeaderFields()); 613e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen } 614e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive 615e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // portal. The only example of this seen so far was a captive portal. For 616e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // the time being go with prior behavior of assuming it's not a captive 617e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // portal. If it is considered a captive portal, a different sign-in URL 618e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // is needed (i.e. can't browse a 204). This could be the result of an HTTP 619e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // proxy server. 620e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen 621e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // Consider 200 response with "Content-length=0" to not be a captive portal. 622e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // There's no point in considering this a captive portal as the user cannot 623e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // sign-in to an empty page. Probably the result of a broken transparent proxy. 624e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen // See http://b/9972012. 625e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) { 626e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen if (DBG) log("Empty 200 response interpreted as 204 response."); 627e547ff281020b08eb51ef7b2786831f7aacdd73cPaul Jensen httpResponseCode = 204; 628ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 629306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 630306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen sendNetworkConditionsBroadcast(true /* response received */, httpResponseCode == 204, 631306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen requestTimestamp, responseTimestamp); 632ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } catch (IOException e) { 633ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (DBG) log("Probably not a portal: exception " + e); 634869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen if (httpResponseCode == 599) { 635869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen // TODO: Ping gateway and DNS server and log results. 636869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen } 637ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } finally { 638ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen if (urlConnection != null) { 639ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen urlConnection.disconnect(); 640ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 641ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 642ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen return httpResponseCode; 643ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen } 644306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 645306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen /** 646306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen * @param responseReceived - whether or not we received a valid HTTP response to our request. 647306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen * If false, isCaptivePortal and responseTimestampMs are ignored 648306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen * TODO: This should be moved to the transports. The latency could be passed to the transports 649306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen * along with the captive portal result. Currently the TYPE_MOBILE broadcasts appear unused so 650306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen * perhaps this could just be added to the WiFi transport only. 651306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen */ 652306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal, 653306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen long requestTimestampMs, long responseTimestampMs) { 654306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (Settings.Global.getInt(mContext.getContentResolver(), 655306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0) { 656306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (DBG) log("Don't send network conditions - lacking user consent."); 657306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen return; 658306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 659306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 660fb68f8fbe0213841f393f8bdb5313e4e44f4f116Robert Greenwalt if (systemReady == false) return; 661fb68f8fbe0213841f393f8bdb5313e4e44f4f116Robert Greenwalt 662306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED); 663306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen switch (mNetworkAgentInfo.networkInfo.getType()) { 664306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen case ConnectivityManager.TYPE_WIFI: 665306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo(); 666306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (currentWifiInfo != null) { 667306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not 668306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // surrounded by double quotation marks (thus violating the Javadoc), but this 669306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // was changed to match the Javadoc in API 17. Since clients may have started 670306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // sanitizing the output of this method since API 17 was released, we should 671306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // not change it here as it would become impossible to tell whether the SSID is 672306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // simply being surrounded by quotes due to the API, or whether those quotes 673306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen // are actually part of the SSID. 674306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID()); 675306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID()); 676306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } else { 677306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (DBG) logw("network info is TYPE_WIFI but no ConnectionInfo found"); 678306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen return; 679306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 680306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen break; 681306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen case ConnectivityManager.TYPE_MOBILE: 682306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType()); 683306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen List<CellInfo> info = mTelephonyManager.getAllCellInfo(); 684306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (info == null) return; 685306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen int numRegisteredCellInfo = 0; 686306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen for (CellInfo cellInfo : info) { 687306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (cellInfo.isRegistered()) { 688306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen numRegisteredCellInfo++; 689306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (numRegisteredCellInfo > 1) { 690306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (DBG) log("more than one registered CellInfo. Can't " + 691306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen "tell which is active. Bailing."); 692306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen return; 693306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 694306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (cellInfo instanceof CellInfoCdma) { 695306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity(); 696306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); 697306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } else if (cellInfo instanceof CellInfoGsm) { 698306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity(); 699306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); 700306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } else if (cellInfo instanceof CellInfoLte) { 701306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity(); 702306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); 703306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } else if (cellInfo instanceof CellInfoWcdma) { 704306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); 705306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); 706306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } else { 707306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (DBG) logw("Registered cellinfo is unrecognized"); 708306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen return; 709306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 710306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 711306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 712306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen break; 713306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen default: 714306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen return; 715306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 716306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_CONNECTIVITY_TYPE, mNetworkAgentInfo.networkInfo.getType()); 717306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_RESPONSE_RECEIVED, responseReceived); 718306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs); 719306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen 720306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen if (responseReceived) { 721306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal); 722306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen latencyBroadcast.putExtra(EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs); 723306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 72455298581a0750505bc7da0539fe94255f95326a4Paul Jensen mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT, 72555298581a0750505bc7da0539fe94255f95326a4Paul Jensen PERMISSION_ACCESS_NETWORK_CONDITIONS); 726306f1a45c636e3721ae4b84b8797e6349ae6ff57Paul Jensen } 727ca8f16ad14819ba17f5ff3d2e2bf6fbc9bbaa9f7Paul Jensen} 728