1 /* 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import android.content.Context; 14 import android.os.Build; 15 import android.support.annotation.Nullable; 16 import java.util.ArrayList; 17 import java.util.List; 18 import org.webrtc.NetworkChangeDetector; 19 20 /** 21 * Borrowed from Chromium's 22 * src/net/android/java/src/org/chromium/net/NetworkChangeNotifier.java 23 * 24 * <p>Triggers updates to the underlying network state from OS networking events. 25 * 26 * <p>This class is thread-safe. 27 */ 28 public class NetworkMonitor { 29 /** 30 * Alerted when the connection type of the network changes. The alert is fired on the UI thread. 31 */ 32 public interface NetworkObserver { onConnectionTypeChanged(NetworkChangeDetector.ConnectionType connectionType)33 public void onConnectionTypeChanged(NetworkChangeDetector.ConnectionType connectionType); 34 } 35 36 private static final String TAG = "NetworkMonitor"; 37 38 // Lazy initialization holder class idiom for static fields. 39 private static class InstanceHolder { 40 // We are storing application context so it is okay. 41 static final NetworkMonitor instance = new NetworkMonitor(); 42 } 43 44 // Factory for creating NetworkChangeDetector. 45 private NetworkChangeDetectorFactory networkChangeDetectorFactory = 46 new NetworkChangeDetectorFactory() { 47 @Override 48 public NetworkChangeDetector create( 49 NetworkChangeDetector.Observer observer, Context context) { 50 return new NetworkMonitorAutoDetect(observer, context); 51 } 52 }; 53 54 // Native observers of the connection type changes. 55 private final ArrayList<Long> nativeNetworkObservers; 56 // Java observers of the connection type changes. 57 private final ArrayList<NetworkObserver> networkObservers; 58 59 private final Object networkChangeDetectorLock = new Object(); 60 // Object that detects the connection type changes and brings up mobile networks. 61 @Nullable private NetworkChangeDetector networkChangeDetector; 62 // Also guarded by autoDetectLock. 63 private int numObservers; 64 65 private volatile NetworkChangeDetector.ConnectionType currentConnectionType; 66 NetworkMonitor()67 private NetworkMonitor() { 68 nativeNetworkObservers = new ArrayList<Long>(); 69 networkObservers = new ArrayList<NetworkObserver>(); 70 numObservers = 0; 71 currentConnectionType = NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN; 72 } 73 74 /** 75 * Set the factory that will be used to create the network change detector. 76 * Needs to be called before the monitoring is starts. 77 */ setNetworkChangeDetectorFactory(NetworkChangeDetectorFactory factory)78 public void setNetworkChangeDetectorFactory(NetworkChangeDetectorFactory factory) { 79 assertIsTrue(numObservers == 0); 80 this.networkChangeDetectorFactory = factory; 81 } 82 83 // TODO(sakal): Remove once downstream dependencies have been updated. 84 @Deprecated init(Context context)85 public static void init(Context context) {} 86 87 /** Returns the singleton instance. This may be called from native or from Java code. */ 88 @CalledByNative getInstance()89 public static NetworkMonitor getInstance() { 90 return InstanceHolder.instance; 91 } 92 assertIsTrue(boolean condition)93 private static void assertIsTrue(boolean condition) { 94 if (!condition) { 95 throw new AssertionError("Expected to be true"); 96 } 97 } 98 99 /** 100 * Enables auto detection of the network state change and brings up mobile networks for using 101 * multi-networking. This requires the embedding app have the platform ACCESS_NETWORK_STATE and 102 * CHANGE_NETWORK_STATE permission. 103 */ startMonitoring(Context applicationContext)104 public void startMonitoring(Context applicationContext) { 105 synchronized (networkChangeDetectorLock) { 106 ++numObservers; 107 if (networkChangeDetector == null) { 108 networkChangeDetector = createNetworkChangeDetector(applicationContext); 109 } 110 currentConnectionType = networkChangeDetector.getCurrentConnectionType(); 111 } 112 } 113 114 /** Deprecated, pass in application context in startMonitoring instead. */ 115 @Deprecated startMonitoring()116 public void startMonitoring() { 117 startMonitoring(ContextUtils.getApplicationContext()); 118 } 119 120 /** 121 * Enables auto detection of the network state change and brings up mobile networks for using 122 * multi-networking. This requires the embedding app have the platform ACCESS_NETWORK_STATE and 123 * CHANGE_NETWORK_STATE permission. 124 */ 125 @CalledByNative startMonitoring(@ullable Context applicationContext, long nativeObserver)126 private void startMonitoring(@Nullable Context applicationContext, long nativeObserver) { 127 Logging.d(TAG, "Start monitoring with native observer " + nativeObserver); 128 129 startMonitoring( 130 applicationContext != null ? applicationContext : ContextUtils.getApplicationContext()); 131 // The native observers expect a network list update after they call startMonitoring. 132 synchronized (nativeNetworkObservers) { 133 nativeNetworkObservers.add(nativeObserver); 134 } 135 updateObserverActiveNetworkList(nativeObserver); 136 // currentConnectionType was updated in startMonitoring(). 137 // Need to notify the native observers here. 138 notifyObserversOfConnectionTypeChange(currentConnectionType); 139 } 140 141 /** 142 * Stop network monitoring. If no one is monitoring networks, destroy and reset 143 * networkChangeDetector. 144 */ stopMonitoring()145 public void stopMonitoring() { 146 synchronized (networkChangeDetectorLock) { 147 if (--numObservers == 0) { 148 networkChangeDetector.destroy(); 149 networkChangeDetector = null; 150 } 151 } 152 } 153 154 @CalledByNative stopMonitoring(long nativeObserver)155 private void stopMonitoring(long nativeObserver) { 156 Logging.d(TAG, "Stop monitoring with native observer " + nativeObserver); 157 stopMonitoring(); 158 synchronized (nativeNetworkObservers) { 159 nativeNetworkObservers.remove(nativeObserver); 160 } 161 } 162 163 // Returns true if network binding is supported on this platform. 164 @CalledByNative networkBindingSupported()165 private boolean networkBindingSupported() { 166 synchronized (networkChangeDetectorLock) { 167 return networkChangeDetector != null && networkChangeDetector.supportNetworkCallback(); 168 } 169 } 170 171 @CalledByNative androidSdkInt()172 private static int androidSdkInt() { 173 return Build.VERSION.SDK_INT; 174 } 175 getCurrentConnectionType()176 private NetworkChangeDetector.ConnectionType getCurrentConnectionType() { 177 return currentConnectionType; 178 } 179 createNetworkChangeDetector(Context appContext)180 private NetworkChangeDetector createNetworkChangeDetector(Context appContext) { 181 return networkChangeDetectorFactory.create(new NetworkChangeDetector.Observer() { 182 @Override 183 public void onConnectionTypeChanged(NetworkChangeDetector.ConnectionType newConnectionType) { 184 updateCurrentConnectionType(newConnectionType); 185 } 186 187 @Override 188 public void onNetworkConnect(NetworkChangeDetector.NetworkInformation networkInfo) { 189 notifyObserversOfNetworkConnect(networkInfo); 190 } 191 192 @Override 193 public void onNetworkDisconnect(long networkHandle) { 194 notifyObserversOfNetworkDisconnect(networkHandle); 195 } 196 197 @Override 198 public void onNetworkPreference( 199 List<NetworkChangeDetector.ConnectionType> types, int preference) { 200 notifyObserversOfNetworkPreference(types, preference); 201 } 202 }, appContext); 203 } 204 205 private void updateCurrentConnectionType(NetworkChangeDetector.ConnectionType newConnectionType) { 206 currentConnectionType = newConnectionType; 207 notifyObserversOfConnectionTypeChange(newConnectionType); 208 } 209 210 /** Alerts all observers of a connection change. */ 211 private void notifyObserversOfConnectionTypeChange( 212 NetworkChangeDetector.ConnectionType newConnectionType) { 213 List<Long> nativeObservers = getNativeNetworkObserversSync(); 214 for (Long nativeObserver : nativeObservers) { 215 nativeNotifyConnectionTypeChanged(nativeObserver); 216 } 217 // This avoids calling external methods while locking on an object. 218 List<NetworkObserver> javaObservers; 219 synchronized (networkObservers) { 220 javaObservers = new ArrayList<>(networkObservers); 221 } 222 for (NetworkObserver observer : javaObservers) { 223 observer.onConnectionTypeChanged(newConnectionType); 224 } 225 } 226 227 private void notifyObserversOfNetworkConnect( 228 NetworkChangeDetector.NetworkInformation networkInfo) { 229 List<Long> nativeObservers = getNativeNetworkObserversSync(); 230 for (Long nativeObserver : nativeObservers) { 231 nativeNotifyOfNetworkConnect(nativeObserver, networkInfo); 232 } 233 } 234 235 private void notifyObserversOfNetworkDisconnect(long networkHandle) { 236 List<Long> nativeObservers = getNativeNetworkObserversSync(); 237 for (Long nativeObserver : nativeObservers) { 238 nativeNotifyOfNetworkDisconnect(nativeObserver, networkHandle); 239 } 240 } 241 242 private void notifyObserversOfNetworkPreference( 243 List<NetworkChangeDetector.ConnectionType> types, int preference) { 244 List<Long> nativeObservers = getNativeNetworkObserversSync(); 245 for (NetworkChangeDetector.ConnectionType type : types) { 246 for (Long nativeObserver : nativeObservers) { 247 nativeNotifyOfNetworkPreference(nativeObserver, type, preference); 248 } 249 } 250 } 251 252 private void updateObserverActiveNetworkList(long nativeObserver) { 253 List<NetworkChangeDetector.NetworkInformation> networkInfoList; 254 synchronized (networkChangeDetectorLock) { 255 networkInfoList = 256 (networkChangeDetector == null) ? null : networkChangeDetector.getActiveNetworkList(); 257 } 258 if (networkInfoList == null || networkInfoList.size() == 0) { 259 return; 260 } 261 262 NetworkChangeDetector.NetworkInformation[] networkInfos = 263 new NetworkChangeDetector.NetworkInformation[networkInfoList.size()]; 264 networkInfos = networkInfoList.toArray(networkInfos); 265 nativeNotifyOfActiveNetworkList(nativeObserver, networkInfos); 266 } 267 268 private List<Long> getNativeNetworkObserversSync() { 269 synchronized (nativeNetworkObservers) { 270 return new ArrayList<>(nativeNetworkObservers); 271 } 272 } 273 274 /** 275 * Adds an observer for any connection type changes. 276 * 277 * @deprecated Use getInstance(appContext).addObserver instead. 278 */ 279 @Deprecated 280 public static void addNetworkObserver(NetworkObserver observer) { 281 getInstance().addObserver(observer); 282 } 283 284 public void addObserver(NetworkObserver observer) { 285 synchronized (networkObservers) { 286 networkObservers.add(observer); 287 } 288 } 289 290 /** 291 * Removes an observer for any connection type changes. 292 * 293 * @deprecated Use getInstance(appContext).removeObserver instead. 294 */ 295 @Deprecated 296 public static void removeNetworkObserver(NetworkObserver observer) { 297 getInstance().removeObserver(observer); 298 } 299 300 public void removeObserver(NetworkObserver observer) { 301 synchronized (networkObservers) { 302 networkObservers.remove(observer); 303 } 304 } 305 306 /** Checks if there currently is connectivity. */ 307 public static boolean isOnline() { 308 NetworkChangeDetector.ConnectionType connectionType = getInstance().getCurrentConnectionType(); 309 return connectionType != NetworkChangeDetector.ConnectionType.CONNECTION_NONE; 310 } 311 312 private native void nativeNotifyConnectionTypeChanged(long nativeAndroidNetworkMonitor); 313 314 private native void nativeNotifyOfNetworkConnect( 315 long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation networkInfo); 316 317 private native void nativeNotifyOfNetworkDisconnect( 318 long nativeAndroidNetworkMonitor, long networkHandle); 319 320 private native void nativeNotifyOfActiveNetworkList( 321 long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation[] networkInfos); 322 323 private native void nativeNotifyOfNetworkPreference( 324 long nativeAndroidNetworkMonitor, NetworkChangeDetector.ConnectionType type, int preference); 325 326 // For testing only. 327 @Nullable 328 NetworkChangeDetector getNetworkChangeDetector() { 329 synchronized (networkChangeDetectorLock) { 330 return networkChangeDetector; 331 } 332 } 333 334 // For testing only. 335 int getNumObservers() { 336 synchronized (networkChangeDetectorLock) { 337 return numObservers; 338 } 339 } 340 341 // For testing only. 342 static NetworkMonitorAutoDetect createAndSetAutoDetectForTest(Context context) { 343 NetworkMonitor networkMonitor = getInstance(); 344 NetworkChangeDetector networkChangeDetector = 345 networkMonitor.createNetworkChangeDetector(context); 346 networkMonitor.networkChangeDetector = networkChangeDetector; 347 return (NetworkMonitorAutoDetect) networkChangeDetector; 348 } 349 } 350