xref: /aosp_15_r20/external/webrtc/sdk/android/api/org/webrtc/NetworkMonitor.java (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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