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