blob: 2e319d434eb7948b762aafc1db5719e92c5d4f26 [file] [log] [blame]
Irfan Sheriffda6da092012-08-16 12:49:23 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
Irfan Sheriff9538bdd2012-09-20 09:32:41 -070019import android.app.Activity;
Irfan Sheriffda6da092012-08-16 12:49:23 -070020import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.res.Resources;
Russell Brenner108da0c2013-02-12 10:03:14 -080028import android.database.ContentObserver;
Irfan Sheriffda6da092012-08-16 12:49:23 -070029import android.net.ConnectivityManager;
30import android.net.IConnectivityManager;
Brian Williammee1ed51622013-07-31 17:07:33 -070031import android.net.wifi.WifiInfo;
32import android.net.wifi.WifiManager;
Russell Brenner108da0c2013-02-12 10:03:14 -080033import android.os.Handler;
Irfan Sheriff9538bdd2012-09-20 09:32:41 -070034import android.os.UserHandle;
Irfan Sheriffda6da092012-08-16 12:49:23 -070035import android.os.Message;
36import android.os.RemoteException;
Brian Williammee1ed51622013-07-31 17:07:33 -070037import android.os.SystemClock;
Irfan Sheriffda6da092012-08-16 12:49:23 -070038import android.provider.Settings;
Brian Williammee1ed51622013-07-31 17:07:33 -070039import android.telephony.CellIdentityCdma;
40import android.telephony.CellIdentityGsm;
41import android.telephony.CellIdentityLte;
42import android.telephony.CellIdentityWcdma;
43import android.telephony.CellInfo;
44import android.telephony.CellInfoCdma;
45import android.telephony.CellInfoGsm;
46import android.telephony.CellInfoLte;
47import android.telephony.CellInfoWcdma;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -070048import android.telephony.TelephonyManager;
Irfan Sheriffda6da092012-08-16 12:49:23 -070049
Irfan Sheriff9538bdd2012-09-20 09:32:41 -070050import com.android.internal.util.State;
51import com.android.internal.util.StateMachine;
52
Irfan Sheriffda6da092012-08-16 12:49:23 -070053import java.io.IOException;
54import java.net.HttpURLConnection;
55import java.net.InetAddress;
56import java.net.Inet4Address;
Wink Savillebf341222013-08-02 11:25:23 -070057import java.net.SocketTimeoutException;
Irfan Sheriffda6da092012-08-16 12:49:23 -070058import java.net.URL;
59import java.net.UnknownHostException;
Brian Williammee1ed51622013-07-31 17:07:33 -070060import java.util.List;
Irfan Sheriffda6da092012-08-16 12:49:23 -070061
62import com.android.internal.R;
63
64/**
Irfan Sheriff9538bdd2012-09-20 09:32:41 -070065 * This class allows captive portal detection on a network.
Irfan Sheriffda6da092012-08-16 12:49:23 -070066 * @hide
67 */
Irfan Sheriff9538bdd2012-09-20 09:32:41 -070068public class CaptivePortalTracker extends StateMachine {
Wink Savillebf341222013-08-02 11:25:23 -070069 private static final boolean DBG = true;
Irfan Sheriffda6da092012-08-16 12:49:23 -070070 private static final String TAG = "CaptivePortalTracker";
71
72 private static final String DEFAULT_SERVER = "clients3.google.com";
73 private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
74
75 private static final int SOCKET_TIMEOUT_MS = 10000;
76
Brian Williammee1ed51622013-07-31 17:07:33 -070077 public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
78 "android.net.conn.NETWORK_CONDITIONS_MEASURED";
79 public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
80 public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
81 public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
82 public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
83 public static final String EXTRA_CELL_ID = "extra_cellid";
84 public static final String EXTRA_SSID = "extra_ssid";
85 public static final String EXTRA_BSSID = "extra_bssid";
86 /** real time since boot */
87 public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
88 public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
89
90 private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
91 "android.permission.ACCESS_NETWORK_CONDITIONS";
92
Irfan Sheriffda6da092012-08-16 12:49:23 -070093 private String mServer;
94 private String mUrl;
95 private boolean mNotificationShown = false;
96 private boolean mIsCaptivePortalCheckEnabled = false;
Irfan Sheriffda6da092012-08-16 12:49:23 -070097 private IConnectivityManager mConnService;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -070098 private TelephonyManager mTelephonyManager;
Brian Williammee1ed51622013-07-31 17:07:33 -070099 private WifiManager mWifiManager;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700100 private Context mContext;
101 private NetworkInfo mNetworkInfo;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700102
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700103 private static final int CMD_DETECT_PORTAL = 0;
104 private static final int CMD_CONNECTIVITY_CHANGE = 1;
105 private static final int CMD_DELAYED_CAPTIVE_CHECK = 2;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700106
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700107 /* This delay happens every time before we do a captive check on a network */
108 private static final int DELAYED_CHECK_INTERVAL_MS = 10000;
109 private int mDelayedCheckToken = 0;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700110
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700111 private State mDefaultState = new DefaultState();
112 private State mNoActiveNetworkState = new NoActiveNetworkState();
113 private State mActiveNetworkState = new ActiveNetworkState();
114 private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState();
115
Russell Brenner108da0c2013-02-12 10:03:14 -0800116 private static final String SETUP_WIZARD_PACKAGE = "com.google.android.setupwizard";
117 private boolean mDeviceProvisioned = false;
118 private ProvisioningObserver mProvisioningObserver;
119
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700120 private CaptivePortalTracker(Context context, IConnectivityManager cs) {
121 super(TAG);
122
Irfan Sheriffda6da092012-08-16 12:49:23 -0700123 mContext = context;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700124 mConnService = cs;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -0700125 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
Brian Williammee1ed51622013-07-31 17:07:33 -0700126 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Russell Brenner108da0c2013-02-12 10:03:14 -0800127 mProvisioningObserver = new ProvisioningObserver();
Irfan Sheriffda6da092012-08-16 12:49:23 -0700128
Irfan Sheriffda6da092012-08-16 12:49:23 -0700129 IntentFilter filter = new IntentFilter();
Irfan Sheriffda6da092012-08-16 12:49:23 -0700130 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
Russell Brenner108da0c2013-02-12 10:03:14 -0800131 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
Irfan Sheriffda6da092012-08-16 12:49:23 -0700132 mContext.registerReceiver(mReceiver, filter);
133
Jeff Sharkey625239a2012-09-26 22:03:49 -0700134 mServer = Settings.Global.getString(mContext.getContentResolver(),
135 Settings.Global.CAPTIVE_PORTAL_SERVER);
Irfan Sheriffda6da092012-08-16 12:49:23 -0700136 if (mServer == null) mServer = DEFAULT_SERVER;
137
Jeff Sharkey625239a2012-09-26 22:03:49 -0700138 mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
139 Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700140
141 addState(mDefaultState);
142 addState(mNoActiveNetworkState, mDefaultState);
143 addState(mActiveNetworkState, mDefaultState);
144 addState(mDelayedCaptiveCheckState, mActiveNetworkState);
145 setInitialState(mNoActiveNetworkState);
Irfan Sheriffda6da092012-08-16 12:49:23 -0700146 }
147
Russell Brenner108da0c2013-02-12 10:03:14 -0800148 private class ProvisioningObserver extends ContentObserver {
149 ProvisioningObserver() {
150 super(new Handler());
151 mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
152 Settings.Global.DEVICE_PROVISIONED), false, this);
153 onChange(false); // load initial value
154 }
155
156 @Override
157 public void onChange(boolean selfChange) {
158 mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
159 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
160 }
161 }
162
Irfan Sheriffda6da092012-08-16 12:49:23 -0700163 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
164 @Override
165 public void onReceive(Context context, Intent intent) {
166 String action = intent.getAction();
Russell Brenner108da0c2013-02-12 10:03:14 -0800167 // Normally, we respond to CONNECTIVITY_ACTION, allowing time for the change in
168 // connectivity to stabilize, but if the device is not yet provisioned, respond
169 // immediately to speed up transit through the setup wizard.
170 if ((mDeviceProvisioned && action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
171 || (!mDeviceProvisioned
172 && action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE))) {
Irfan Sheriffda6da092012-08-16 12:49:23 -0700173 NetworkInfo info = intent.getParcelableExtra(
174 ConnectivityManager.EXTRA_NETWORK_INFO);
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700175 sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info));
Irfan Sheriffda6da092012-08-16 12:49:23 -0700176 }
177 }
178 };
179
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700180 public static CaptivePortalTracker makeCaptivePortalTracker(Context context,
Irfan Sheriffda6da092012-08-16 12:49:23 -0700181 IConnectivityManager cs) {
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700182 CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, cs);
183 captivePortal.start();
Irfan Sheriffda6da092012-08-16 12:49:23 -0700184 return captivePortal;
185 }
186
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700187 public void detectCaptivePortal(NetworkInfo info) {
188 sendMessage(obtainMessage(CMD_DETECT_PORTAL, info));
189 }
190
191 private class DefaultState extends State {
192 @Override
193 public void enter() {
194 if (DBG) log(getName() + "\n");
Irfan Sheriffda6da092012-08-16 12:49:23 -0700195 }
196
197 @Override
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700198 public boolean processMessage(Message message) {
199 if (DBG) log(getName() + message.toString() + "\n");
200 switch (message.what) {
201 case CMD_DETECT_PORTAL:
202 NetworkInfo info = (NetworkInfo) message.obj;
203 // Checking on a secondary connection is not supported
204 // yet
205 notifyPortalCheckComplete(info);
Irfan Sheriffda6da092012-08-16 12:49:23 -0700206 break;
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700207 case CMD_CONNECTIVITY_CHANGE:
208 case CMD_DELAYED_CAPTIVE_CHECK:
209 break;
210 default:
211 loge("Ignoring " + message);
212 break;
213 }
214 return HANDLED;
215 }
216 }
Irfan Sheriffda6da092012-08-16 12:49:23 -0700217
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700218 private class NoActiveNetworkState extends State {
219 @Override
220 public void enter() {
221 if (DBG) log(getName() + "\n");
222 mNetworkInfo = null;
223 /* Clear any previous notification */
224 setNotificationVisible(false);
225 }
Irfan Sheriffda6da092012-08-16 12:49:23 -0700226
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700227 @Override
228 public boolean processMessage(Message message) {
229 if (DBG) log(getName() + message.toString() + "\n");
230 InetAddress server;
231 NetworkInfo info;
232 switch (message.what) {
233 case CMD_CONNECTIVITY_CHANGE:
234 info = (NetworkInfo) message.obj;
235 if (info.isConnected() && isActiveNetwork(info)) {
236 mNetworkInfo = info;
237 transitionTo(mDelayedCaptiveCheckState);
Irfan Sheriffda6da092012-08-16 12:49:23 -0700238 }
239 break;
240 default:
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700241 return NOT_HANDLED;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700242 }
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700243 return HANDLED;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700244 }
245 }
246
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700247 private class ActiveNetworkState extends State {
248 @Override
249 public void enter() {
250 if (DBG) log(getName() + "\n");
251 }
252
253 @Override
254 public boolean processMessage(Message message) {
255 NetworkInfo info;
256 switch (message.what) {
257 case CMD_CONNECTIVITY_CHANGE:
258 info = (NetworkInfo) message.obj;
259 if (!info.isConnected()
260 && info.getType() == mNetworkInfo.getType()) {
261 if (DBG) log("Disconnected from active network " + info);
262 transitionTo(mNoActiveNetworkState);
263 } else if (info.getType() != mNetworkInfo.getType() &&
264 info.isConnected() &&
265 isActiveNetwork(info)) {
266 if (DBG) log("Active network switched " + info);
267 deferMessage(message);
268 transitionTo(mNoActiveNetworkState);
269 }
270 break;
271 default:
272 return NOT_HANDLED;
273 }
274 return HANDLED;
275 }
Irfan Sheriffda6da092012-08-16 12:49:23 -0700276 }
277
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700278
279
280 private class DelayedCaptiveCheckState extends State {
281 @Override
282 public void enter() {
283 if (DBG) log(getName() + "\n");
Russell Brenner108da0c2013-02-12 10:03:14 -0800284 Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
285 if (mDeviceProvisioned) {
286 sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
287 } else {
288 sendMessage(message);
289 }
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700290 }
291
292 @Override
293 public boolean processMessage(Message message) {
294 if (DBG) log(getName() + message.toString() + "\n");
295 switch (message.what) {
296 case CMD_DELAYED_CAPTIVE_CHECK:
297 if (message.arg1 == mDelayedCheckToken) {
298 InetAddress server = lookupHost(mServer);
Russell Brenner108da0c2013-02-12 10:03:14 -0800299 boolean captive = server != null && isCaptivePortal(server);
300 if (captive) {
301 if (DBG) log("Captive network " + mNetworkInfo);
302 } else {
303 if (DBG) log("Not captive network " + mNetworkInfo);
304 }
305 if (mDeviceProvisioned) {
306 if (captive) {
307 // Setup Wizard will assist the user in connecting to a captive
308 // portal, so make the notification visible unless during setup
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700309 setNotificationVisible(true);
310 }
Russell Brenner108da0c2013-02-12 10:03:14 -0800311 } else {
312 Intent intent = new Intent(
313 ConnectivityManager.ACTION_CAPTIVE_PORTAL_TEST_COMPLETED);
314 intent.putExtra(ConnectivityManager.EXTRA_IS_CAPTIVE_PORTAL, captive);
315 intent.setPackage(SETUP_WIZARD_PACKAGE);
316 mContext.sendBroadcast(intent);
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700317 }
Russell Brenner108da0c2013-02-12 10:03:14 -0800318
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700319 transitionTo(mActiveNetworkState);
320 }
321 break;
322 default:
323 return NOT_HANDLED;
324 }
325 return HANDLED;
326 }
327 }
328
329 private void notifyPortalCheckComplete(NetworkInfo info) {
330 if (info == null) {
331 loge("notifyPortalCheckComplete on null");
332 return;
333 }
Irfan Sheriffda6da092012-08-16 12:49:23 -0700334 try {
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700335 mConnService.captivePortalCheckComplete(info);
Irfan Sheriffda6da092012-08-16 12:49:23 -0700336 } catch(RemoteException e) {
337 e.printStackTrace();
338 }
339 }
340
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700341 private boolean isActiveNetwork(NetworkInfo info) {
Irfan Sheriffda6da092012-08-16 12:49:23 -0700342 try {
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700343 NetworkInfo active = mConnService.getActiveNetworkInfo();
344 if (active != null && active.getType() == info.getType()) {
345 return true;
346 }
Irfan Sheriffda6da092012-08-16 12:49:23 -0700347 } catch (RemoteException e) {
348 e.printStackTrace();
349 }
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700350 return false;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700351 }
352
353 /**
Brian Williammee1ed51622013-07-31 17:07:33 -0700354 * Do a URL fetch on a known server to see if we get the data we expect.
355 * Measure the response time and broadcast that.
Irfan Sheriffda6da092012-08-16 12:49:23 -0700356 */
357 private boolean isCaptivePortal(InetAddress server) {
358 HttpURLConnection urlConnection = null;
359 if (!mIsCaptivePortalCheckEnabled) return false;
360
361 mUrl = "http://" + server.getHostAddress() + "/generate_204";
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700362 if (DBG) log("Checking " + mUrl);
Brian Williammee1ed51622013-07-31 17:07:33 -0700363 long requestTimestamp = -1;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700364 try {
365 URL url = new URL(mUrl);
366 urlConnection = (HttpURLConnection) url.openConnection();
367 urlConnection.setInstanceFollowRedirects(false);
368 urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
369 urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
370 urlConnection.setUseCaches(false);
Brian Williammee1ed51622013-07-31 17:07:33 -0700371
372 // Time how long it takes to get a response to our request
373 requestTimestamp = SystemClock.elapsedRealtime();
374
Irfan Sheriffda6da092012-08-16 12:49:23 -0700375 urlConnection.getInputStream();
Brian Williammee1ed51622013-07-31 17:07:33 -0700376
377 // Time how long it takes to get a response to our request
378 long responseTimestamp = SystemClock.elapsedRealtime();
379
Irfan Sheriffda6da092012-08-16 12:49:23 -0700380 // we got a valid response, but not from the real google
Brian Williammee1ed51622013-07-31 17:07:33 -0700381 boolean isCaptivePortal = urlConnection.getResponseCode() != 204;
382
383 sendNetworkConditionsBroadcast(true /* response received */, isCaptivePortal,
384 requestTimestamp, responseTimestamp);
385 return isCaptivePortal;
Wink Savillebf341222013-08-02 11:25:23 -0700386 } catch (SocketTimeoutException e) {
387 if (DBG) log("Probably a portal: exception " + e);
Wink Savillef35f3202013-08-08 16:51:46 -0700388 if (requestTimestamp != -1) {
389 sendFailedCaptivePortalCheckBroadcast(requestTimestamp);
390 } // else something went wrong with setting up the urlConnection
Wink Savillebf341222013-08-02 11:25:23 -0700391 return true;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700392 } catch (IOException e) {
393 if (DBG) log("Probably not a portal: exception " + e);
Brian Williammee1ed51622013-07-31 17:07:33 -0700394 if (requestTimestamp != -1) {
395 sendFailedCaptivePortalCheckBroadcast(requestTimestamp);
396 } // else something went wrong with setting up the urlConnection
Irfan Sheriffda6da092012-08-16 12:49:23 -0700397 return false;
398 } finally {
399 if (urlConnection != null) {
400 urlConnection.disconnect();
401 }
402 }
403 }
404
405 private InetAddress lookupHost(String hostname) {
406 InetAddress inetAddress[];
407 try {
408 inetAddress = InetAddress.getAllByName(hostname);
409 } catch (UnknownHostException e) {
Brian Williammee1ed51622013-07-31 17:07:33 -0700410 sendFailedCaptivePortalCheckBroadcast(SystemClock.elapsedRealtime());
Irfan Sheriffda6da092012-08-16 12:49:23 -0700411 return null;
412 }
413
414 for (InetAddress a : inetAddress) {
415 if (a instanceof Inet4Address) return a;
416 }
Brian Williammee1ed51622013-07-31 17:07:33 -0700417
418 sendFailedCaptivePortalCheckBroadcast(SystemClock.elapsedRealtime());
Irfan Sheriffda6da092012-08-16 12:49:23 -0700419 return null;
420 }
421
422 private void setNotificationVisible(boolean visible) {
423 // if it should be hidden and it is already hidden, then noop
424 if (!visible && !mNotificationShown) {
425 return;
426 }
427
428 Resources r = Resources.getSystem();
429 NotificationManager notificationManager = (NotificationManager) mContext
430 .getSystemService(Context.NOTIFICATION_SERVICE);
431
432 if (visible) {
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700433 CharSequence title;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -0700434 CharSequence details;
Irfan Sheriffebb8f412012-10-02 16:12:05 -0700435 int icon;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -0700436 switch (mNetworkInfo.getType()) {
437 case ConnectivityManager.TYPE_WIFI:
438 title = r.getString(R.string.wifi_available_sign_in, 0);
439 details = r.getString(R.string.network_available_sign_in_detailed,
440 mNetworkInfo.getExtraInfo());
Irfan Sheriffebb8f412012-10-02 16:12:05 -0700441 icon = R.drawable.stat_notify_wifi_in_range;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -0700442 break;
443 case ConnectivityManager.TYPE_MOBILE:
444 title = r.getString(R.string.network_available_sign_in, 0);
445 // TODO: Change this to pull from NetworkInfo once a printable
446 // name has been added to it
447 details = mTelephonyManager.getNetworkOperatorName();
Irfan Sheriffebb8f412012-10-02 16:12:05 -0700448 icon = R.drawable.stat_notify_rssi_in_range;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -0700449 break;
450 default:
451 title = r.getString(R.string.network_available_sign_in, 0);
452 details = r.getString(R.string.network_available_sign_in_detailed,
453 mNetworkInfo.getExtraInfo());
Irfan Sheriffebb8f412012-10-02 16:12:05 -0700454 icon = R.drawable.stat_notify_rssi_in_range;
Irfan Sheriffb8aad91f2012-10-02 14:01:28 -0700455 break;
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700456 }
Irfan Sheriffda6da092012-08-16 12:49:23 -0700457
458 Notification notification = new Notification();
459 notification.when = 0;
Irfan Sheriffebb8f412012-10-02 16:12:05 -0700460 notification.icon = icon;
Irfan Sheriffda6da092012-08-16 12:49:23 -0700461 notification.flags = Notification.FLAG_AUTO_CANCEL;
Irfan Sheriff9538bdd2012-09-20 09:32:41 -0700462 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl));
463 intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
464 Intent.FLAG_ACTIVITY_NEW_TASK);
465 notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Irfan Sheriffda6da092012-08-16 12:49:23 -0700466 notification.tickerText = title;
467 notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
468
469 notificationManager.notify(NOTIFICATION_ID, 1, notification);
470 } else {
471 notificationManager.cancel(NOTIFICATION_ID, 1);
472 }
473 mNotificationShown = visible;
474 }
Brian Williammee1ed51622013-07-31 17:07:33 -0700475
476 private void sendFailedCaptivePortalCheckBroadcast(long requestTimestampMs) {
477 sendNetworkConditionsBroadcast(false /* response received */, false /* ignored */,
478 requestTimestampMs, 0 /* ignored */);
479 }
480
481 /**
482 * @param responseReceived - whether or not we received a valid HTTP response to our request.
483 * If false, isCaptivePortal and responseTimestampMs are ignored
484 */
485 private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
486 long requestTimestampMs, long responseTimestampMs) {
487 if (Settings.Global.getInt(mContext.getContentResolver(),
488 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0) {
489 if (DBG) log("Don't send network conditions - lacking user consent.");
490 return;
491 }
492
493 Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED);
494 switch (mNetworkInfo.getType()) {
495 case ConnectivityManager.TYPE_WIFI:
496 WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
497 if (currentWifiInfo != null) {
498 latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID());
499 latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID());
500 } else {
501 if (DBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
502 return;
503 }
504 break;
505 case ConnectivityManager.TYPE_MOBILE:
506 latencyBroadcast.putExtra(EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType());
507 List<CellInfo> info = mTelephonyManager.getAllCellInfo();
508 if (info == null) return;
509 StringBuffer uniqueCellId = new StringBuffer();
510 int numRegisteredCellInfo = 0;
511 for (CellInfo cellInfo : info) {
512 if (cellInfo.isRegistered()) {
513 numRegisteredCellInfo++;
514 if (numRegisteredCellInfo > 1) {
515 if (DBG) log("more than one registered CellInfo. Can't " +
516 "tell which is active. Bailing.");
517 return;
518 }
519 if (cellInfo instanceof CellInfoCdma) {
520 CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
521 latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
522 } else if (cellInfo instanceof CellInfoGsm) {
523 CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
524 latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
525 } else if (cellInfo instanceof CellInfoLte) {
526 CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
527 latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
528 } else if (cellInfo instanceof CellInfoWcdma) {
529 CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
530 latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
531 } else {
532 if (DBG) logw("Registered cellinfo is unrecognized");
533 return;
534 }
535 }
536 }
537 break;
538 default:
539 return;
540 }
541 latencyBroadcast.putExtra(EXTRA_CONNECTIVITY_TYPE, mNetworkInfo.getType());
542 latencyBroadcast.putExtra(EXTRA_RESPONSE_RECEIVED, responseReceived);
543 latencyBroadcast.putExtra(EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs);
544
545 if (responseReceived) {
546 latencyBroadcast.putExtra(EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal);
547 latencyBroadcast.putExtra(EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs);
548 }
549 mContext.sendBroadcast(latencyBroadcast, PERMISSION_ACCESS_NETWORK_CONDITIONS);
550 }
Irfan Sheriffda6da092012-08-16 12:49:23 -0700551}