• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net;
6 
7 import android.annotation.SuppressLint;
8 
9 import androidx.annotation.VisibleForTesting;
10 
11 import org.chromium.base.ObserverList;
12 import org.chromium.base.annotations.CalledByNative;
13 import org.chromium.base.annotations.JNINamespace;
14 import org.chromium.base.annotations.NativeClassQualifiedName;
15 import org.chromium.base.annotations.NativeMethods;
16 
17 import java.util.ArrayList;
18 
19 /**
20  * Triggers updates to the underlying network state in Chrome.
21  *
22  * By default, connectivity is assumed and changes must be pushed from the embedder via the
23  * forceConnectivityState function.
24  * Embedders may choose to have this class auto-detect changes in network connectivity by invoking
25  * the setAutoDetectConnectivityState function.
26  *
27  * WARNING: This class is not thread-safe.
28  */
29 @JNINamespace("net")
30 public class NetworkChangeNotifier {
31     /**
32      * Alerted when the connection type of the network changes.
33      * The alert is fired on the UI thread.
34      */
35     public interface ConnectionTypeObserver {
onConnectionTypeChanged(int connectionType)36         public void onConnectionTypeChanged(int connectionType);
37     }
38 
39     private final ArrayList<Long> mNativeChangeNotifiers;
40     private final ObserverList<ConnectionTypeObserver> mConnectionTypeObservers;
41     private NetworkChangeNotifierAutoDetect mAutoDetector;
42     // Last value broadcast via ConnectionTypeChange signal.
43     private int mCurrentConnectionType = ConnectionType.CONNECTION_UNKNOWN;
44     // Last value broadcast via ConnectionCostChange signal.
45     private int mCurrentConnectionCost = ConnectionCost.UNKNOWN;
46 
47     @SuppressLint("StaticFieldLeak")
48     private static NetworkChangeNotifier sInstance;
49 
50     @VisibleForTesting
NetworkChangeNotifier()51     protected NetworkChangeNotifier() {
52         mNativeChangeNotifiers = new ArrayList<Long>();
53         mConnectionTypeObservers = new ObserverList<ConnectionTypeObserver>();
54     }
55 
56     /**
57      * Initializes the singleton once.
58      */
59     @CalledByNative
init()60     public static NetworkChangeNotifier init() {
61         if (sInstance == null) {
62             sInstance = new NetworkChangeNotifier();
63         }
64         return sInstance;
65     }
66 
isInitialized()67     public static boolean isInitialized() {
68         return sInstance != null;
69     }
70 
71     @VisibleForTesting
resetInstanceForTests()72     public static void resetInstanceForTests() {
73         sInstance = new NetworkChangeNotifier();
74     }
75 
76     @VisibleForTesting
resetInstanceForTests(NetworkChangeNotifier notifier)77     public static void resetInstanceForTests(NetworkChangeNotifier notifier) {
78         sInstance = notifier;
79     }
80 
81     @CalledByNative
getCurrentConnectionType()82     public int getCurrentConnectionType() {
83         return mCurrentConnectionType;
84     }
85 
86     @CalledByNative
getCurrentConnectionSubtype()87     public int getCurrentConnectionSubtype() {
88         return mAutoDetector == null
89                 ? ConnectionSubtype.SUBTYPE_UNKNOWN
90                 : mAutoDetector.getCurrentNetworkState().getConnectionSubtype();
91     }
92 
93     @CalledByNative
getCurrentConnectionCost()94     public int getCurrentConnectionCost() {
95         return mCurrentConnectionCost;
96     }
97 
98     /**
99      * Returns NetID of device's current default connected network used for
100      * communication. Only available on Lollipop and newer releases and when
101      * auto-detection has been enabled, returns NetId.INVALID otherwise.
102      */
103     @CalledByNative
getCurrentDefaultNetId()104     public long getCurrentDefaultNetId() {
105         return mAutoDetector == null ? NetId.INVALID : mAutoDetector.getDefaultNetId();
106     }
107 
108     /**
109      * Returns an array of all of the device's currently connected
110      * networks and ConnectionTypes. Array elements are a repeated sequence of:
111      *   NetID of network
112      *   ConnectionType of network
113      * Only available on Lollipop and newer releases and when auto-detection has
114      * been enabled.
115      */
116     @CalledByNative
getCurrentNetworksAndTypes()117     public long[] getCurrentNetworksAndTypes() {
118         return mAutoDetector == null ? new long[0] : mAutoDetector.getNetworksAndTypes();
119     }
120 
121     /**
122      * Adds a native-side observer.
123      */
124     @CalledByNative
addNativeObserver(long nativeChangeNotifier)125     public void addNativeObserver(long nativeChangeNotifier) {
126         mNativeChangeNotifiers.add(nativeChangeNotifier);
127     }
128 
129     /**
130      * Removes a native-side observer.
131      */
132     @CalledByNative
removeNativeObserver(long nativeChangeNotifier)133     public void removeNativeObserver(long nativeChangeNotifier) {
134         mNativeChangeNotifiers.remove(nativeChangeNotifier);
135     }
136 
137     /**
138      * Returns {@code true} if NetworkCallback failed to register, indicating that network-specific
139      * callbacks will not be issued.
140      */
141     @CalledByNative
registerNetworkCallbackFailed()142     public boolean registerNetworkCallbackFailed() {
143         return mAutoDetector == null ? false : mAutoDetector.registerNetworkCallbackFailed();
144     }
145 
146     /**
147      * Returns the singleton instance.
148      */
getInstance()149     public static NetworkChangeNotifier getInstance() {
150         assert sInstance != null;
151         return sInstance;
152     }
153 
154     /**
155      * Enables auto detection of the current network state based on notifications from the system.
156      * Note that passing true here requires the embedding app have the platform ACCESS_NETWORK_STATE
157      * permission. Also note that in this case the auto detection is enabled based on the status of
158      * the application (@see ApplicationStatus).
159      * Declare @CalledByNative only for testing.
160      *
161      * @param shouldAutoDetect true if the NetworkChangeNotifier should listen for system changes in
162      *    network connectivity.
163      */
164     @CalledByNative
setAutoDetectConnectivityState(boolean shouldAutoDetect)165     public static void setAutoDetectConnectivityState(boolean shouldAutoDetect) {
166         getInstance().setAutoDetectConnectivityStateInternal(
167                 shouldAutoDetect, new RegistrationPolicyApplicationStatus());
168     }
169 
170     /**
171      * Registers to always receive network change notifications no matter if
172      * the app is in the background or foreground.
173      * Note that in normal circumstances, chrome embedders should use
174      * {@code setAutoDetectConnectivityState} to listen to network changes only
175      * when the app is in the foreground, because network change observers
176      * might perform expensive work depending on the network connectivity.
177      */
registerToReceiveNotificationsAlways()178     public static void registerToReceiveNotificationsAlways() {
179         getInstance().setAutoDetectConnectivityStateInternal(
180                 true, new RegistrationPolicyAlwaysRegister());
181     }
182 
183     /**
184      * Registers to receive network change notification based on the provided registration policy.
185      */
setAutoDetectConnectivityState( NetworkChangeNotifierAutoDetect.RegistrationPolicy policy)186     public static void setAutoDetectConnectivityState(
187             NetworkChangeNotifierAutoDetect.RegistrationPolicy policy) {
188         getInstance().setAutoDetectConnectivityStateInternal(true, policy);
189     }
190 
destroyAutoDetector()191     private void destroyAutoDetector() {
192         if (mAutoDetector != null) {
193             mAutoDetector.destroy();
194             mAutoDetector = null;
195         }
196     }
197 
setAutoDetectConnectivityStateInternal( boolean shouldAutoDetect, NetworkChangeNotifierAutoDetect.RegistrationPolicy policy)198     private void setAutoDetectConnectivityStateInternal(
199             boolean shouldAutoDetect, NetworkChangeNotifierAutoDetect.RegistrationPolicy policy) {
200         if (shouldAutoDetect) {
201             if (mAutoDetector == null) {
202                 mAutoDetector = new NetworkChangeNotifierAutoDetect(
203                         new NetworkChangeNotifierAutoDetect.Observer() {
204                             @Override
205                             public void onConnectionTypeChanged(int newConnectionType) {
206                                 updateCurrentConnectionType(newConnectionType);
207                             }
208                             @Override
209                             public void onConnectionCostChanged(int newConnectionCost) {
210                                 notifyObserversOfConnectionCostChange(newConnectionCost);
211                             }
212                             @Override
213                             public void onConnectionSubtypeChanged(int newConnectionSubtype) {
214                                 notifyObserversOfConnectionSubtypeChange(newConnectionSubtype);
215                             }
216                             @Override
217                             public void onNetworkConnect(long netId, int connectionType) {
218                                 notifyObserversOfNetworkConnect(netId, connectionType);
219                             }
220                             @Override
221                             public void onNetworkSoonToDisconnect(long netId) {
222                                 notifyObserversOfNetworkSoonToDisconnect(netId);
223                             }
224                             @Override
225                             public void onNetworkDisconnect(long netId) {
226                                 notifyObserversOfNetworkDisconnect(netId);
227                             }
228                             @Override
229                             public void purgeActiveNetworkList(long[] activeNetIds) {
230                                 notifyObserversToPurgeActiveNetworkList(activeNetIds);
231                             }
232                         },
233                         policy);
234                 final NetworkChangeNotifierAutoDetect.NetworkState networkState =
235                         mAutoDetector.getCurrentNetworkState();
236                 updateCurrentConnectionType(networkState.getConnectionType());
237                 updateCurrentConnectionCost(networkState.getConnectionCost());
238                 notifyObserversOfConnectionSubtypeChange(networkState.getConnectionSubtype());
239             }
240         } else {
241             destroyAutoDetector();
242         }
243     }
244 
245     /**
246      * For testing, updates the perceived network state when not auto-detecting changes to
247      * connectivity.
248      *
249      * @param networkAvailable True if the NetworkChangeNotifier should perceive a "connected"
250      *    state, false implies "disconnected".
251      */
252     @CalledByNative
forceConnectivityState(boolean networkAvailable)253     public static void forceConnectivityState(boolean networkAvailable) {
254         setAutoDetectConnectivityState(false);
255         getInstance().forceConnectivityStateInternal(networkAvailable);
256     }
257 
forceConnectivityStateInternal(boolean forceOnline)258     private void forceConnectivityStateInternal(boolean forceOnline) {
259         boolean connectionCurrentlyExists =
260                 mCurrentConnectionType != ConnectionType.CONNECTION_NONE;
261         if (connectionCurrentlyExists != forceOnline) {
262             updateCurrentConnectionType(forceOnline ? ConnectionType.CONNECTION_UNKNOWN
263                                                     : ConnectionType.CONNECTION_NONE);
264             notifyObserversOfConnectionSubtypeChange(forceOnline ? ConnectionSubtype.SUBTYPE_UNKNOWN
265                                                                  : ConnectionSubtype.SUBTYPE_NONE);
266         }
267     }
268 
269     // For testing, pretend a network connected.
270     @CalledByNative
fakeNetworkConnected(long netId, int connectionType)271     public static void fakeNetworkConnected(long netId, int connectionType) {
272         setAutoDetectConnectivityState(false);
273         getInstance().notifyObserversOfNetworkConnect(netId, connectionType);
274     }
275 
276     // For testing, pretend a network will soon disconnect.
277     @CalledByNative
fakeNetworkSoonToBeDisconnected(long netId)278     public static void fakeNetworkSoonToBeDisconnected(long netId) {
279         setAutoDetectConnectivityState(false);
280         getInstance().notifyObserversOfNetworkSoonToDisconnect(netId);
281     }
282 
283     // For testing, pretend a network disconnected.
284     @CalledByNative
fakeNetworkDisconnected(long netId)285     public static void fakeNetworkDisconnected(long netId) {
286         setAutoDetectConnectivityState(false);
287         getInstance().notifyObserversOfNetworkDisconnect(netId);
288     }
289 
290     // For testing, pretend a network lists should be purged.
291     @CalledByNative
fakePurgeActiveNetworkList(long[] activeNetIds)292     public static void fakePurgeActiveNetworkList(long[] activeNetIds) {
293         setAutoDetectConnectivityState(false);
294         getInstance().notifyObserversToPurgeActiveNetworkList(activeNetIds);
295     }
296 
297     // For testing, pretend a default network changed.
298     @CalledByNative
fakeDefaultNetwork(long netId, int connectionType)299     public static void fakeDefaultNetwork(long netId, int connectionType) {
300         setAutoDetectConnectivityState(false);
301         getInstance().notifyObserversOfConnectionTypeChange(connectionType, netId);
302     }
303 
304     // For testing, pretend the connection cost has changed.
305     @CalledByNative
306     @VisibleForTesting
fakeConnectionCostChanged(int connectionCost)307     public static void fakeConnectionCostChanged(int connectionCost) {
308         setAutoDetectConnectivityState(false);
309         getInstance().notifyObserversOfConnectionCostChange(connectionCost);
310     }
311 
312     // For testing, pretend the connection subtype has changed.
313     @CalledByNative
fakeConnectionSubtypeChanged(int connectionSubtype)314     public static void fakeConnectionSubtypeChanged(int connectionSubtype) {
315         setAutoDetectConnectivityState(false);
316         getInstance().notifyObserversOfConnectionSubtypeChange(connectionSubtype);
317     }
318 
updateCurrentConnectionType(int newConnectionType)319     private void updateCurrentConnectionType(int newConnectionType) {
320         mCurrentConnectionType = newConnectionType;
321         notifyObserversOfConnectionTypeChange(newConnectionType);
322     }
323 
324     /**
325      * Alerts all observers of a connection change.
326      */
notifyObserversOfConnectionTypeChange(int newConnectionType)327     void notifyObserversOfConnectionTypeChange(int newConnectionType) {
328         notifyObserversOfConnectionTypeChange(newConnectionType, getCurrentDefaultNetId());
329     }
330 
notifyObserversOfConnectionTypeChange(int newConnectionType, long defaultNetId)331     private void notifyObserversOfConnectionTypeChange(int newConnectionType, long defaultNetId) {
332         for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
333             NetworkChangeNotifierJni.get().notifyConnectionTypeChanged(nativeChangeNotifier,
334                     NetworkChangeNotifier.this, newConnectionType, defaultNetId);
335         }
336         for (ConnectionTypeObserver observer : mConnectionTypeObservers) {
337             observer.onConnectionTypeChanged(newConnectionType);
338         }
339     }
340 
updateCurrentConnectionCost(int newConnectionCost)341     private void updateCurrentConnectionCost(int newConnectionCost) {
342         mCurrentConnectionCost = newConnectionCost;
343         notifyObserversOfConnectionCostChange(newConnectionCost);
344     }
345 
346     /**
347      * Alerts all observers of a connection cost change.
348      */
notifyObserversOfConnectionCostChange(int newConnectionCost)349     void notifyObserversOfConnectionCostChange(int newConnectionCost) {
350         for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
351             NetworkChangeNotifierJni.get().notifyConnectionCostChanged(
352                     nativeChangeNotifier, NetworkChangeNotifier.this, newConnectionCost);
353         }
354     }
355 
356     /**
357      * Alerts all observers of a bandwidth change.
358      */
notifyObserversOfConnectionSubtypeChange(int connectionSubtype)359     void notifyObserversOfConnectionSubtypeChange(int connectionSubtype) {
360         for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
361             NetworkChangeNotifierJni.get().notifyMaxBandwidthChanged(
362                     nativeChangeNotifier, NetworkChangeNotifier.this, connectionSubtype);
363         }
364     }
365 
366     /**
367      * Alerts all observers of a network connect.
368      */
notifyObserversOfNetworkConnect(long netId, int connectionType)369     void notifyObserversOfNetworkConnect(long netId, int connectionType) {
370         for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
371             NetworkChangeNotifierJni.get().notifyOfNetworkConnect(
372                     nativeChangeNotifier, NetworkChangeNotifier.this, netId, connectionType);
373         }
374     }
375 
376     /**
377      * Alerts all observers of a network soon to be disconnected.
378      */
notifyObserversOfNetworkSoonToDisconnect(long netId)379     void notifyObserversOfNetworkSoonToDisconnect(long netId) {
380         for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
381             NetworkChangeNotifierJni.get().notifyOfNetworkSoonToDisconnect(
382                     nativeChangeNotifier, NetworkChangeNotifier.this, netId);
383         }
384     }
385 
386     /**
387      * Alerts all observers of a network disconnect.
388      */
notifyObserversOfNetworkDisconnect(long netId)389     void notifyObserversOfNetworkDisconnect(long netId) {
390         for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
391             NetworkChangeNotifierJni.get().notifyOfNetworkDisconnect(
392                     nativeChangeNotifier, NetworkChangeNotifier.this, netId);
393         }
394     }
395 
396     /**
397      * Alerts all observers to purge cached lists of active networks, of any
398      * networks not in the accompanying list of active networks. This is
399      * issued if a period elapsed where disconnected notifications may have
400      * been missed, and acts to keep cached lists of active networks accurate.
401      */
notifyObserversToPurgeActiveNetworkList(long[] activeNetIds)402     void notifyObserversToPurgeActiveNetworkList(long[] activeNetIds) {
403         for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
404             NetworkChangeNotifierJni.get().notifyPurgeActiveNetworkList(
405                     nativeChangeNotifier, NetworkChangeNotifier.this, activeNetIds);
406         }
407     }
408 
409     /**
410      * Adds an observer for any connection type changes.
411      */
addConnectionTypeObserver(ConnectionTypeObserver observer)412     public static void addConnectionTypeObserver(ConnectionTypeObserver observer) {
413         getInstance().addConnectionTypeObserverInternal(observer);
414     }
415 
addConnectionTypeObserverInternal(ConnectionTypeObserver observer)416     private void addConnectionTypeObserverInternal(ConnectionTypeObserver observer) {
417         mConnectionTypeObservers.addObserver(observer);
418     }
419 
420     /**
421      * Removes an observer for any connection type changes.
422      */
removeConnectionTypeObserver(ConnectionTypeObserver observer)423     public static void removeConnectionTypeObserver(ConnectionTypeObserver observer) {
424         getInstance().removeConnectionTypeObserverInternal(observer);
425     }
426 
removeConnectionTypeObserverInternal(ConnectionTypeObserver observer)427     private void removeConnectionTypeObserverInternal(ConnectionTypeObserver observer) {
428         mConnectionTypeObservers.removeObserver(observer);
429     }
430 
431     // For testing only.
getAutoDetectorForTest()432     public static NetworkChangeNotifierAutoDetect getAutoDetectorForTest() {
433         return getInstance().mAutoDetector;
434     }
435 
436     /**
437      * Checks if there currently is connectivity.
438      */
isOnline()439     public static boolean isOnline() {
440         int connectionType = getInstance().getCurrentConnectionType();
441         return connectionType != ConnectionType.CONNECTION_NONE;
442     }
443 
444     @NativeMethods
445     interface Natives {
446         @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
notifyConnectionTypeChanged(long nativePtr, NetworkChangeNotifier caller, int newConnectionType, long defaultNetId)447         void notifyConnectionTypeChanged(long nativePtr, NetworkChangeNotifier caller,
448                 int newConnectionType, long defaultNetId);
449 
450         @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
notifyConnectionCostChanged( long nativePtr, NetworkChangeNotifier caller, int newConnectionCost)451         void notifyConnectionCostChanged(
452                 long nativePtr, NetworkChangeNotifier caller, int newConnectionCost);
453 
454         @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
notifyMaxBandwidthChanged(long nativePtr, NetworkChangeNotifier caller, int subType)455         void notifyMaxBandwidthChanged(long nativePtr, NetworkChangeNotifier caller, int subType);
456 
457         @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
notifyOfNetworkConnect( long nativePtr, NetworkChangeNotifier caller, long netId, int connectionType)458         void notifyOfNetworkConnect(
459                 long nativePtr, NetworkChangeNotifier caller, long netId, int connectionType);
460 
461         @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
notifyOfNetworkSoonToDisconnect( long nativePtr, NetworkChangeNotifier caller, long netId)462         void notifyOfNetworkSoonToDisconnect(
463                 long nativePtr, NetworkChangeNotifier caller, long netId);
464 
465         @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
notifyOfNetworkDisconnect(long nativePtr, NetworkChangeNotifier caller, long netId)466         void notifyOfNetworkDisconnect(long nativePtr, NetworkChangeNotifier caller, long netId);
467 
468         @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
notifyPurgeActiveNetworkList( long nativePtr, NetworkChangeNotifier caller, long[] activeNetIds)469         void notifyPurgeActiveNetworkList(
470                 long nativePtr, NetworkChangeNotifier caller, long[] activeNetIds);
471     }
472 }
473