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 androidx.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, String fieldTrialsString)104 public void startMonitoring(Context applicationContext, String fieldTrialsString) { 105 synchronized (networkChangeDetectorLock) { 106 ++numObservers; 107 if (networkChangeDetector == null) { 108 networkChangeDetector = createNetworkChangeDetector(applicationContext, fieldTrialsString); 109 } 110 currentConnectionType = networkChangeDetector.getCurrentConnectionType(); 111 } 112 } 113 114 /** Deprecated, use startMonitoring with fieldTrialsStringString argument. */ 115 @Deprecated startMonitoring(Context applicationContext)116 public void startMonitoring(Context applicationContext) { 117 startMonitoring(applicationContext, ""); 118 } 119 120 /** Deprecated, pass in application context in startMonitoring instead. */ 121 @Deprecated startMonitoring()122 public void startMonitoring() { 123 startMonitoring(ContextUtils.getApplicationContext(), ""); 124 } 125 126 /** 127 * Enables auto detection of the network state change and brings up mobile networks for using 128 * multi-networking. This requires the embedding app have the platform ACCESS_NETWORK_STATE and 129 * CHANGE_NETWORK_STATE permission. 130 */ 131 @CalledByNative startMonitoring( @ullable Context applicationContext, long nativeObserver, String fieldTrialsString)132 private void startMonitoring( 133 @Nullable Context applicationContext, long nativeObserver, String fieldTrialsString) { 134 Logging.d(TAG, 135 "Start monitoring with native observer " + nativeObserver 136 + " fieldTrialsString: " + fieldTrialsString); 137 138 startMonitoring( 139 applicationContext != null ? applicationContext : ContextUtils.getApplicationContext(), 140 fieldTrialsString); 141 142 synchronized (nativeNetworkObservers) { 143 nativeNetworkObservers.add(nativeObserver); 144 } 145 // The native observer expects a network list update after startMonitoring. 146 updateObserverActiveNetworkList(nativeObserver); 147 // currentConnectionType was updated in startMonitoring(). 148 // Need to notify the native observers here. 149 notifyObserversOfConnectionTypeChange(currentConnectionType); 150 } 151 152 /** 153 * Stop network monitoring. If no one is monitoring networks, destroy and reset 154 * networkChangeDetector. 155 */ stopMonitoring()156 public void stopMonitoring() { 157 synchronized (networkChangeDetectorLock) { 158 if (--numObservers == 0) { 159 networkChangeDetector.destroy(); 160 networkChangeDetector = null; 161 } 162 } 163 } 164 165 @CalledByNative stopMonitoring(long nativeObserver)166 private void stopMonitoring(long nativeObserver) { 167 Logging.d(TAG, "Stop monitoring with native observer " + nativeObserver); 168 stopMonitoring(); 169 synchronized (nativeNetworkObservers) { 170 nativeNetworkObservers.remove(nativeObserver); 171 } 172 } 173 174 // Returns true if network binding is supported on this platform. 175 @CalledByNative networkBindingSupported()176 private boolean networkBindingSupported() { 177 synchronized (networkChangeDetectorLock) { 178 return networkChangeDetector != null && networkChangeDetector.supportNetworkCallback(); 179 } 180 } 181 182 @CalledByNative androidSdkInt()183 private static int androidSdkInt() { 184 return Build.VERSION.SDK_INT; 185 } 186 getCurrentConnectionType()187 private NetworkChangeDetector.ConnectionType getCurrentConnectionType() { 188 return currentConnectionType; 189 } 190 createNetworkChangeDetector( Context appContext, String fieldTrialsString)191 private NetworkChangeDetector createNetworkChangeDetector( 192 Context appContext, String fieldTrialsString) { 193 return networkChangeDetectorFactory.create(new NetworkChangeDetector.Observer() { 194 @Override 195 public void onConnectionTypeChanged(NetworkChangeDetector.ConnectionType newConnectionType) { 196 updateCurrentConnectionType(newConnectionType); 197 } 198 199 @Override 200 public void onNetworkConnect(NetworkChangeDetector.NetworkInformation networkInfo) { 201 notifyObserversOfNetworkConnect(networkInfo); 202 } 203 204 @Override 205 public void onNetworkDisconnect(long networkHandle) { 206 notifyObserversOfNetworkDisconnect(networkHandle); 207 } 208 209 @Override 210 public void onNetworkPreference( 211 List<NetworkChangeDetector.ConnectionType> types, int preference) { 212 notifyObserversOfNetworkPreference(types, preference); 213 } 214 215 @Override 216 public String getFieldTrialsString() { 217 return fieldTrialsString; 218 } 219 }, appContext); 220 } 221 222 private void updateCurrentConnectionType(NetworkChangeDetector.ConnectionType newConnectionType) { 223 currentConnectionType = newConnectionType; 224 notifyObserversOfConnectionTypeChange(newConnectionType); 225 } 226 227 /** Alerts all observers of a connection change. */ 228 private void notifyObserversOfConnectionTypeChange( 229 NetworkChangeDetector.ConnectionType newConnectionType) { 230 List<Long> nativeObservers = getNativeNetworkObserversSync(); 231 for (Long nativeObserver : nativeObservers) { 232 nativeNotifyConnectionTypeChanged(nativeObserver); 233 } 234 // This avoids calling external methods while locking on an object. 235 List<NetworkObserver> javaObservers; 236 synchronized (networkObservers) { 237 javaObservers = new ArrayList<>(networkObservers); 238 } 239 for (NetworkObserver observer : javaObservers) { 240 observer.onConnectionTypeChanged(newConnectionType); 241 } 242 } 243 244 private void notifyObserversOfNetworkConnect( 245 NetworkChangeDetector.NetworkInformation networkInfo) { 246 List<Long> nativeObservers = getNativeNetworkObserversSync(); 247 for (Long nativeObserver : nativeObservers) { 248 nativeNotifyOfNetworkConnect(nativeObserver, networkInfo); 249 } 250 } 251 252 private void notifyObserversOfNetworkDisconnect(long networkHandle) { 253 List<Long> nativeObservers = getNativeNetworkObserversSync(); 254 for (Long nativeObserver : nativeObservers) { 255 nativeNotifyOfNetworkDisconnect(nativeObserver, networkHandle); 256 } 257 } 258 259 private void notifyObserversOfNetworkPreference( 260 List<NetworkChangeDetector.ConnectionType> types, int preference) { 261 List<Long> nativeObservers = getNativeNetworkObserversSync(); 262 for (NetworkChangeDetector.ConnectionType type : types) { 263 for (Long nativeObserver : nativeObservers) { 264 nativeNotifyOfNetworkPreference(nativeObserver, type, preference); 265 } 266 } 267 } 268 269 private void updateObserverActiveNetworkList(long nativeObserver) { 270 List<NetworkChangeDetector.NetworkInformation> networkInfoList; 271 synchronized (networkChangeDetectorLock) { 272 networkInfoList = 273 (networkChangeDetector == null) ? null : networkChangeDetector.getActiveNetworkList(); 274 } 275 if (networkInfoList == null) { 276 return; 277 } 278 279 NetworkChangeDetector.NetworkInformation[] networkInfos = 280 new NetworkChangeDetector.NetworkInformation[networkInfoList.size()]; 281 networkInfos = networkInfoList.toArray(networkInfos); 282 nativeNotifyOfActiveNetworkList(nativeObserver, networkInfos); 283 } 284 285 private List<Long> getNativeNetworkObserversSync() { 286 synchronized (nativeNetworkObservers) { 287 return new ArrayList<>(nativeNetworkObservers); 288 } 289 } 290 291 /** 292 * Adds an observer for any connection type changes. 293 * 294 * @deprecated Use getInstance(appContext).addObserver instead. 295 */ 296 @Deprecated 297 public static void addNetworkObserver(NetworkObserver observer) { 298 getInstance().addObserver(observer); 299 } 300 301 public void addObserver(NetworkObserver observer) { 302 synchronized (networkObservers) { 303 networkObservers.add(observer); 304 } 305 } 306 307 /** 308 * Removes an observer for any connection type changes. 309 * 310 * @deprecated Use getInstance(appContext).removeObserver instead. 311 */ 312 @Deprecated 313 public static void removeNetworkObserver(NetworkObserver observer) { 314 getInstance().removeObserver(observer); 315 } 316 317 public void removeObserver(NetworkObserver observer) { 318 synchronized (networkObservers) { 319 networkObservers.remove(observer); 320 } 321 } 322 323 /** Checks if there currently is connectivity. */ 324 public static boolean isOnline() { 325 NetworkChangeDetector.ConnectionType connectionType = getInstance().getCurrentConnectionType(); 326 return connectionType != NetworkChangeDetector.ConnectionType.CONNECTION_NONE; 327 } 328 329 private native void nativeNotifyConnectionTypeChanged(long nativeAndroidNetworkMonitor); 330 331 private native void nativeNotifyOfNetworkConnect( 332 long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation networkInfo); 333 334 private native void nativeNotifyOfNetworkDisconnect( 335 long nativeAndroidNetworkMonitor, long networkHandle); 336 337 private native void nativeNotifyOfActiveNetworkList( 338 long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation[] networkInfos); 339 340 private native void nativeNotifyOfNetworkPreference( 341 long nativeAndroidNetworkMonitor, NetworkChangeDetector.ConnectionType type, int preference); 342 343 // For testing only. 344 @Nullable 345 NetworkChangeDetector getNetworkChangeDetector() { 346 synchronized (networkChangeDetectorLock) { 347 return networkChangeDetector; 348 } 349 } 350 351 // For testing only. 352 int getNumObservers() { 353 synchronized (networkChangeDetectorLock) { 354 return numObservers; 355 } 356 } 357 358 // For testing only. 359 static NetworkMonitorAutoDetect createAndSetAutoDetectForTest( 360 Context context, String fieldTrialsString) { 361 NetworkMonitor networkMonitor = getInstance(); 362 NetworkChangeDetector networkChangeDetector = 363 networkMonitor.createNetworkChangeDetector(context, fieldTrialsString); 364 networkMonitor.networkChangeDetector = networkChangeDetector; 365 return (NetworkMonitorAutoDetect) networkChangeDetector; 366 } 367 } 368