|  | /* | 
|  | *  Copyright 2015 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | package org.webrtc; | 
|  |  | 
|  | import android.content.Context; | 
|  | import android.os.Build; | 
|  | import androidx.annotation.Nullable; | 
|  | import java.util.ArrayList; | 
|  | import java.util.List; | 
|  | import org.webrtc.NetworkChangeDetector; | 
|  |  | 
|  | /** | 
|  | * Borrowed from Chromium's | 
|  | * src/net/android/java/src/org/chromium/net/NetworkChangeNotifier.java | 
|  | * | 
|  | * <p>Triggers updates to the underlying network state from OS networking events. | 
|  | * | 
|  | * <p>This class is thread-safe. | 
|  | */ | 
|  | public class NetworkMonitor { | 
|  | /** | 
|  | * Alerted when the connection type of the network changes. The alert is fired on the UI thread. | 
|  | */ | 
|  | public interface NetworkObserver { | 
|  | public void onConnectionTypeChanged(NetworkChangeDetector.ConnectionType connectionType); | 
|  | } | 
|  |  | 
|  | private static final String TAG = "NetworkMonitor"; | 
|  |  | 
|  | // Lazy initialization holder class idiom for static fields. | 
|  | private static class InstanceHolder { | 
|  | // We are storing application context so it is okay. | 
|  | static final NetworkMonitor instance = new NetworkMonitor(); | 
|  | } | 
|  |  | 
|  | // Factory for creating NetworkChangeDetector. | 
|  | private NetworkChangeDetectorFactory networkChangeDetectorFactory = | 
|  | new NetworkChangeDetectorFactory() { | 
|  | @Override | 
|  | public NetworkChangeDetector create( | 
|  | NetworkChangeDetector.Observer observer, Context context) { | 
|  | return new NetworkMonitorAutoDetect(observer, context); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Native observers of the connection type changes. | 
|  | private final ArrayList<Long> nativeNetworkObservers; | 
|  | // Java observers of the connection type changes. | 
|  | private final ArrayList<NetworkObserver> networkObservers; | 
|  |  | 
|  | private final Object networkChangeDetectorLock = new Object(); | 
|  | // Object that detects the connection type changes and brings up mobile networks. | 
|  | @Nullable private NetworkChangeDetector networkChangeDetector; | 
|  | // Also guarded by autoDetectLock. | 
|  | private int numObservers; | 
|  |  | 
|  | private volatile NetworkChangeDetector.ConnectionType currentConnectionType; | 
|  |  | 
|  | private NetworkMonitor() { | 
|  | nativeNetworkObservers = new ArrayList<Long>(); | 
|  | networkObservers = new ArrayList<NetworkObserver>(); | 
|  | numObservers = 0; | 
|  | currentConnectionType = NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Set the factory that will be used to create the network change detector. | 
|  | * Needs to be called before the monitoring is starts. | 
|  | */ | 
|  | public void setNetworkChangeDetectorFactory(NetworkChangeDetectorFactory factory) { | 
|  | assertIsTrue(numObservers == 0); | 
|  | this.networkChangeDetectorFactory = factory; | 
|  | } | 
|  |  | 
|  | // TODO(sakal): Remove once downstream dependencies have been updated. | 
|  | @Deprecated | 
|  | public static void init(Context context) {} | 
|  |  | 
|  | /** Returns the singleton instance. This may be called from native or from Java code. */ | 
|  | @CalledByNative | 
|  | public static NetworkMonitor getInstance() { | 
|  | return InstanceHolder.instance; | 
|  | } | 
|  |  | 
|  | private static void assertIsTrue(boolean condition) { | 
|  | if (!condition) { | 
|  | throw new AssertionError("Expected to be true"); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Enables auto detection of the network state change and brings up mobile networks for using | 
|  | * multi-networking. This requires the embedding app have the platform ACCESS_NETWORK_STATE and | 
|  | * CHANGE_NETWORK_STATE permission. | 
|  | */ | 
|  | public void startMonitoring(Context applicationContext, String fieldTrialsString) { | 
|  | synchronized (networkChangeDetectorLock) { | 
|  | ++numObservers; | 
|  | if (networkChangeDetector == null) { | 
|  | networkChangeDetector = createNetworkChangeDetector(applicationContext, fieldTrialsString); | 
|  | } | 
|  | currentConnectionType = networkChangeDetector.getCurrentConnectionType(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Deprecated, use startMonitoring with fieldTrialsStringString argument. */ | 
|  | @Deprecated | 
|  | public void startMonitoring(Context applicationContext) { | 
|  | startMonitoring(applicationContext, ""); | 
|  | } | 
|  |  | 
|  | /** Deprecated, pass in application context in startMonitoring instead. */ | 
|  | @Deprecated | 
|  | public void startMonitoring() { | 
|  | startMonitoring(ContextUtils.getApplicationContext(), ""); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Enables auto detection of the network state change and brings up mobile networks for using | 
|  | * multi-networking. This requires the embedding app have the platform ACCESS_NETWORK_STATE and | 
|  | * CHANGE_NETWORK_STATE permission. | 
|  | */ | 
|  | @CalledByNative | 
|  | private void startMonitoring( | 
|  | @Nullable Context applicationContext, long nativeObserver, String fieldTrialsString) { | 
|  | Logging.d(TAG, | 
|  | "Start monitoring with native observer " + nativeObserver | 
|  | + " fieldTrialsString: " + fieldTrialsString); | 
|  |  | 
|  | startMonitoring( | 
|  | applicationContext != null ? applicationContext : ContextUtils.getApplicationContext(), | 
|  | fieldTrialsString); | 
|  |  | 
|  | synchronized (nativeNetworkObservers) { | 
|  | nativeNetworkObservers.add(nativeObserver); | 
|  | } | 
|  | // The native observer expects a network list update after startMonitoring. | 
|  | updateObserverActiveNetworkList(nativeObserver); | 
|  | // currentConnectionType was updated in startMonitoring(). | 
|  | // Need to notify the native observers here. | 
|  | notifyObserversOfConnectionTypeChange(currentConnectionType); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Stop network monitoring. If no one is monitoring networks, destroy and reset | 
|  | * networkChangeDetector. | 
|  | */ | 
|  | public void stopMonitoring() { | 
|  | synchronized (networkChangeDetectorLock) { | 
|  | if (--numObservers == 0) { | 
|  | networkChangeDetector.destroy(); | 
|  | networkChangeDetector = null; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @CalledByNative | 
|  | private void stopMonitoring(long nativeObserver) { | 
|  | Logging.d(TAG, "Stop monitoring with native observer " + nativeObserver); | 
|  | stopMonitoring(); | 
|  | synchronized (nativeNetworkObservers) { | 
|  | nativeNetworkObservers.remove(nativeObserver); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Returns true if network binding is supported on this platform. | 
|  | @CalledByNative | 
|  | private boolean networkBindingSupported() { | 
|  | synchronized (networkChangeDetectorLock) { | 
|  | return networkChangeDetector != null && networkChangeDetector.supportNetworkCallback(); | 
|  | } | 
|  | } | 
|  |  | 
|  | @CalledByNative | 
|  | private static int androidSdkInt() { | 
|  | return Build.VERSION.SDK_INT; | 
|  | } | 
|  |  | 
|  | private NetworkChangeDetector.ConnectionType getCurrentConnectionType() { | 
|  | return currentConnectionType; | 
|  | } | 
|  |  | 
|  | private NetworkChangeDetector createNetworkChangeDetector( | 
|  | Context appContext, String fieldTrialsString) { | 
|  | return networkChangeDetectorFactory.create(new NetworkChangeDetector.Observer() { | 
|  | @Override | 
|  | public void onConnectionTypeChanged(NetworkChangeDetector.ConnectionType newConnectionType) { | 
|  | updateCurrentConnectionType(newConnectionType); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNetworkConnect(NetworkChangeDetector.NetworkInformation networkInfo) { | 
|  | notifyObserversOfNetworkConnect(networkInfo); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNetworkDisconnect(long networkHandle) { | 
|  | notifyObserversOfNetworkDisconnect(networkHandle); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onNetworkPreference( | 
|  | List<NetworkChangeDetector.ConnectionType> types, int preference) { | 
|  | notifyObserversOfNetworkPreference(types, preference); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String getFieldTrialsString() { | 
|  | return fieldTrialsString; | 
|  | } | 
|  | }, appContext); | 
|  | } | 
|  |  | 
|  | private void updateCurrentConnectionType(NetworkChangeDetector.ConnectionType newConnectionType) { | 
|  | currentConnectionType = newConnectionType; | 
|  | notifyObserversOfConnectionTypeChange(newConnectionType); | 
|  | } | 
|  |  | 
|  | /** Alerts all observers of a connection change. */ | 
|  | private void notifyObserversOfConnectionTypeChange( | 
|  | NetworkChangeDetector.ConnectionType newConnectionType) { | 
|  |  | 
|  | synchronized (nativeNetworkObservers) { | 
|  | for (Long nativeObserver : nativeNetworkObservers) { | 
|  | nativeNotifyConnectionTypeChanged(nativeObserver); | 
|  | } | 
|  | } | 
|  |  | 
|  | // This avoids calling external methods while locking on an object. | 
|  | List<NetworkObserver> javaObservers; | 
|  | synchronized (networkObservers) { | 
|  | javaObservers = new ArrayList<>(networkObservers); | 
|  | } | 
|  | for (NetworkObserver observer : javaObservers) { | 
|  | observer.onConnectionTypeChanged(newConnectionType); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void notifyObserversOfNetworkConnect( | 
|  | NetworkChangeDetector.NetworkInformation networkInfo) { | 
|  | synchronized (nativeNetworkObservers) { | 
|  | for (Long nativeObserver : nativeNetworkObservers) { | 
|  | nativeNotifyOfNetworkConnect(nativeObserver, networkInfo); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void notifyObserversOfNetworkDisconnect(long networkHandle) { | 
|  | synchronized (nativeNetworkObservers) { | 
|  | for (Long nativeObserver : nativeNetworkObservers) { | 
|  | nativeNotifyOfNetworkDisconnect(nativeObserver, networkHandle); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void notifyObserversOfNetworkPreference( | 
|  | List<NetworkChangeDetector.ConnectionType> types, int preference) { | 
|  | synchronized(nativeNetworkObservers) { | 
|  | for (NetworkChangeDetector.ConnectionType type : types) { | 
|  | for (Long nativeObserver : nativeNetworkObservers) { | 
|  | nativeNotifyOfNetworkPreference(nativeObserver, type, preference); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void updateObserverActiveNetworkList(long nativeObserver) { | 
|  | List<NetworkChangeDetector.NetworkInformation> networkInfoList; | 
|  | synchronized (networkChangeDetectorLock) { | 
|  | networkInfoList = | 
|  | (networkChangeDetector == null) ? null : networkChangeDetector.getActiveNetworkList(); | 
|  | } | 
|  | if (networkInfoList == null) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | NetworkChangeDetector.NetworkInformation[] networkInfos = | 
|  | new NetworkChangeDetector.NetworkInformation[networkInfoList.size()]; | 
|  | networkInfos = networkInfoList.toArray(networkInfos); | 
|  | nativeNotifyOfActiveNetworkList(nativeObserver, networkInfos); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Adds an observer for any connection type changes. | 
|  | * | 
|  | * @deprecated Use getInstance(appContext).addObserver instead. | 
|  | */ | 
|  | @Deprecated | 
|  | public static void addNetworkObserver(NetworkObserver observer) { | 
|  | getInstance().addObserver(observer); | 
|  | } | 
|  |  | 
|  | public void addObserver(NetworkObserver observer) { | 
|  | synchronized (networkObservers) { | 
|  | networkObservers.add(observer); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Removes an observer for any connection type changes. | 
|  | * | 
|  | * @deprecated Use getInstance(appContext).removeObserver instead. | 
|  | */ | 
|  | @Deprecated | 
|  | public static void removeNetworkObserver(NetworkObserver observer) { | 
|  | getInstance().removeObserver(observer); | 
|  | } | 
|  |  | 
|  | public void removeObserver(NetworkObserver observer) { | 
|  | synchronized (networkObservers) { | 
|  | networkObservers.remove(observer); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Checks if there currently is connectivity. */ | 
|  | public static boolean isOnline() { | 
|  | NetworkChangeDetector.ConnectionType connectionType = getInstance().getCurrentConnectionType(); | 
|  | return connectionType != NetworkChangeDetector.ConnectionType.CONNECTION_NONE; | 
|  | } | 
|  |  | 
|  | private native void nativeNotifyConnectionTypeChanged(long nativeAndroidNetworkMonitor); | 
|  |  | 
|  | private native void nativeNotifyOfNetworkConnect( | 
|  | long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation networkInfo); | 
|  |  | 
|  | private native void nativeNotifyOfNetworkDisconnect( | 
|  | long nativeAndroidNetworkMonitor, long networkHandle); | 
|  |  | 
|  | private native void nativeNotifyOfActiveNetworkList( | 
|  | long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation[] networkInfos); | 
|  |  | 
|  | private native void nativeNotifyOfNetworkPreference( | 
|  | long nativeAndroidNetworkMonitor, NetworkChangeDetector.ConnectionType type, int preference); | 
|  |  | 
|  | // For testing only. | 
|  | @Nullable | 
|  | NetworkChangeDetector getNetworkChangeDetector() { | 
|  | synchronized (networkChangeDetectorLock) { | 
|  | return networkChangeDetector; | 
|  | } | 
|  | } | 
|  |  | 
|  | // For testing only. | 
|  | int getNumObservers() { | 
|  | synchronized (networkChangeDetectorLock) { | 
|  | return numObservers; | 
|  | } | 
|  | } | 
|  |  | 
|  | // For testing only. | 
|  | static NetworkMonitorAutoDetect createAndSetAutoDetectForTest( | 
|  | Context context, String fieldTrialsString) { | 
|  | NetworkMonitor networkMonitor = getInstance(); | 
|  | NetworkChangeDetector networkChangeDetector = | 
|  | networkMonitor.createNetworkChangeDetector(context, fieldTrialsString); | 
|  | networkMonitor.networkChangeDetector = networkChangeDetector; | 
|  | return (NetworkMonitorAutoDetect) networkChangeDetector; | 
|  | } | 
|  | } |