Commit 1088603e authored by Jian Li's avatar Jian Li Committed by Commit Bot

Move connectivity detection code out of OfflineIndicatorController

Bug: 852577
Change-Id: Ibad70134b5f43877a39ef48e4a6fc99546d7ca3d
Reviewed-on: https://chromium-review.googlesource.com/1182288
Commit-Queue: Jian Li <jianli@chromium.org>
Reviewed-by: default avatarPeter Williamson <petewil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585284}
parent e7f35a93
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.offlinepages.indicator;
import android.annotation.TargetApi;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Build;
import android.support.annotation.IntDef;
import org.chromium.base.ContextUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.net.ConnectionType;
import org.chromium.net.NetworkChangeNotifier;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Class that detects the network connectivity.
*/
public class ConnectivityDetector implements NetworkChangeNotifier.ConnectionTypeObserver {
// Denotes the connection state.
@Retention(RetentionPolicy.SOURCE)
@IntDef({ConnectionState.NONE, ConnectionState.DISCONNECTED, ConnectionState.NO_INTERNET,
ConnectionState.CAPTIVE_PORTAL, ConnectionState.VALIDATED})
public @interface ConnectionState {
// Initial state or connection state can't be evaluated.
int NONE = 0;
// The network is disconnected.
int DISCONNECTED = 1;
// The network is connected, but it can't reach the Internet, i.e. connecting to a hotspot
// that is not conencted to Internet.
int NO_INTERNET = 2;
// The network is connected, but capitive portal is detected and the user has not signed
// into it.
int CAPTIVE_PORTAL = 3;
// The network is connected to Internet and validated. If the connected network imposes
// capitive portal, the user has already signed into it.
int VALIDATED = 4;
}
/**
* Interface for observing network connectivity changes.
*/
public interface Observer {
/**
* Called when the network connection state changes.
* @param connectionState Current connection state.
*/
void onConnectionStateChanged(@ConnectionState int connectionState);
}
private static boolean sSkipSystemCheckForTesting = false;
private Observer mObserver;
private @ConnectionType int mConnectionType = ConnectionType.CONNECTION_UNKNOWN;
private @ConnectionState int mConnectionState = ConnectionState.NONE;
public ConnectivityDetector(Observer observer) {
mObserver = observer;
NetworkChangeNotifier.addConnectionTypeObserver(this);
detect();
}
public void detect() {
onConnectionTypeChanged(NetworkChangeNotifier.getInstance().getCurrentConnectionType());
}
@Override
public void onConnectionTypeChanged(@ConnectionType int connectionType) {
// This method may be called multiple times with same |connectionType|.
if (mConnectionType == connectionType) {
return;
}
mConnectionType = connectionType;
// If not connected at all, no further check is needed.
if (connectionType == ConnectionType.CONNECTION_NONE) {
mConnectionState = ConnectionState.DISCONNECTED;
notifyConnectionStateChanged();
return;
}
// Check the Android system to determine the network connectivity. If unavailable, as in
// Android version below Marshmallow, we will kick off our own probes.
@ConnectionState
int connectionState = getConnectionStateFromSystem();
if (connectionState != ConnectionState.NONE) {
mConnectionState = connectionState;
notifyConnectionStateChanged();
return;
}
// TODO(jianli): Do manual check via sending HTTP probes to server.
mConnectionState = ConnectionState.VALIDATED;
notifyConnectionStateChanged();
}
/**
* Consults with the Android connection manager to find out the network state.
*/
@TargetApi(Build.VERSION_CODES.M)
private static @ConnectionState int getConnectionStateFromSystem() {
if (sSkipSystemCheckForTesting) return ConnectionState.NONE;
// NET_CAPABILITY_VALIDATED and NET_CAPABILITY_CAPTIVE_PORTAL are only available on
// Marshmallow and later versions.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return ConnectionState.NONE;
ConnectivityManager connectivityManager =
(ConnectivityManager) ContextUtils.getApplicationContext().getSystemService(
Context.CONNECTIVITY_SERVICE);
if (connectivityManager == null) return ConnectionState.NONE;
boolean isCapitivePortal = false;
Network[] networks = connectivityManager.getAllNetworks();
if (networks.length == 0) return ConnectionState.DISCONNECTED;
for (Network network : networks) {
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
if (capabilities == null) continue;
if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
&& capabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
return ConnectionState.VALIDATED;
}
if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)) {
isCapitivePortal = true;
}
}
return isCapitivePortal ? ConnectionState.CAPTIVE_PORTAL : ConnectionState.NO_INTERNET;
}
private void notifyConnectionStateChanged() {
mObserver.onConnectionStateChanged(mConnectionState);
}
@VisibleForTesting
static void skipSystemCheckForTesting() {
sSkipSystemCheckForTesting = true;
}
}
......@@ -5,19 +5,12 @@
package org.chromium.chrome.browser.offlinepages.indicator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Build;
import android.support.v7.content.res.AppCompatResources;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
......@@ -30,14 +23,12 @@ import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.net.ConnectionType;
import org.chromium.net.NetworkChangeNotifier;
/**
* Class that controls when to show the offline indicator.
*/
public class OfflineIndicatorController
implements NetworkChangeNotifier.ConnectionTypeObserver, SnackbarController {
implements ConnectivityDetector.Observer, SnackbarController {
// OfflineIndicatorCTREvent defined in tools/metrics/histograms/enums.xml.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
......@@ -47,17 +38,14 @@ public class OfflineIndicatorController
private static final int DURATION_MS = 10000;
private static boolean sSkipSystemCheckForTesting = false;
@SuppressLint("StaticFieldLeak")
private static OfflineIndicatorController sInstance;
private boolean mIsOffline = false;
private boolean mIsShowingOfflineIndicator = false;
private ConnectivityDetector mConnectivityDetector;
private OfflineIndicatorController() {
NetworkChangeNotifier.addConnectionTypeObserver(this);
updateConnectionType();
mConnectivityDetector = new ConnectivityDetector(this);
}
/**
......@@ -86,20 +74,13 @@ public class OfflineIndicatorController
*/
public static void onUpdate() {
if (sInstance == null) return;
sInstance.updateConnectionType();
sInstance.mConnectivityDetector.detect();
}
@Override
public void onConnectionTypeChanged(int connectionType) {
if (connectionType == ConnectionType.CONNECTION_NONE) {
mIsOffline = true;
} else {
if (!performSystemCheckForValidatedNetwork()) {
mIsOffline = false;
}
}
updateOfflineIndicator();
public void onConnectionStateChanged(
@ConnectivityDetector.ConnectionState int connectionState) {
updateOfflineIndicator(connectionState == ConnectivityDetector.ConnectionState.VALIDATED);
}
@Override
......@@ -115,52 +96,16 @@ public class OfflineIndicatorController
mIsShowingOfflineIndicator = false;
}
/**
* Consults with the Android connection manager to find out if there is a validated network.
* Returns true if the check is performed. A validated network is one of the following:
* 1) a functioning network providing Internet access
* 2) a captive portal and the user decided to use it as is
*/
@TargetApi(Build.VERSION_CODES.M)
private boolean performSystemCheckForValidatedNetwork() {
if (sSkipSystemCheckForTesting) return false;
// NetworkCapabilities.NET_CAPABILITY_VALIDATED is only available on Marshmallow and
// later versions.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false;
ConnectivityManager connectivityManager =
(ConnectivityManager) ContextUtils.getApplicationContext().getSystemService(
Context.CONNECTIVITY_SERVICE);
if (connectivityManager == null) return false;
Network[] networks = connectivityManager.getAllNetworks();
for (Network network : networks) {
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
if (capabilities != null
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
mIsOffline = false;
return true;
}
}
mIsOffline = true;
return true;
}
private void updateConnectionType() {
onConnectionTypeChanged(NetworkChangeNotifier.getInstance().getCurrentConnectionType());
}
private void updateOfflineIndicator() {
private void updateOfflineIndicator(boolean isOnline) {
Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
if (!(activity instanceof SnackbarManageable)) return;
SnackbarManager snackbarManager = ((SnackbarManageable) activity).getSnackbarManager();
if (snackbarManager == null) return;
if (mIsOffline) {
showOfflineIndicator(activity, snackbarManager);
} else {
if (isOnline) {
hideOfflineIndicator(snackbarManager);
} else {
showOfflineIndicator(activity, snackbarManager);
}
}
......@@ -207,7 +152,7 @@ public class OfflineIndicatorController
}
@VisibleForTesting
static void skipSystemCheckForTesting() {
sSkipSystemCheckForTesting = true;
public ConnectivityDetector getConnectivityDetectorForTesting() {
return mConnectivityDetector;
}
}
......@@ -954,6 +954,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/offlinepages/TaskExtrasPacker.java",
"java/src/org/chromium/chrome/browser/offlinepages/TriggerConditions.java",
"java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java",
"java/src/org/chromium/chrome/browser/offlinepages/indicator/ConnectivityDetector.java",
"java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java",
"java/src/org/chromium/chrome/browser/offlinepages/prefetch/OfflineNotificationBackgroundTask.java",
"java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java",
......
......@@ -48,7 +48,7 @@ public class OfflineIndicatorControllerTest {
@Before
public void setUp() throws Exception {
OfflineIndicatorController.skipSystemCheckForTesting();
ConnectivityDetector.skipSystemCheckForTesting();
mActivityTestRule.startMainActivityOnBlankPage();
ThreadUtils.runOnUiThreadBlocking(() -> {
if (!NetworkChangeNotifier.isInitialized()) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment