1 // Copyright 2012 The Chromium Authors. All rights reserved. 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.content.Context; 8 9 import org.chromium.base.CalledByNative; 10 import org.chromium.base.JNINamespace; 11 import org.chromium.base.NativeClassQualifiedName; 12 import org.chromium.base.ObserverList; 13 14 import java.util.ArrayList; 15 16 /** 17 * Triggers updates to the underlying network state in Chrome. 18 * 19 * By default, connectivity is assumed and changes must pushed from the embedder via the 20 * forceConnectivityState function. 21 * Embedders may choose to have this class auto-detect changes in network connectivity by invoking 22 * the setAutoDetectConnectivityState function. 23 * 24 * WARNING: This class is not thread-safe. 25 */ 26 @JNINamespace("net") 27 public class NetworkChangeNotifier { 28 /** 29 * Alerted when the connection type of the network changes. 30 * The alert is fired on the UI thread. 31 */ 32 public interface ConnectionTypeObserver { onConnectionTypeChanged(int connectionType)33 public void onConnectionTypeChanged(int connectionType); 34 } 35 36 // These constants must always match the ones in network_change_notifier.h. 37 public static final int CONNECTION_UNKNOWN = 0; 38 public static final int CONNECTION_ETHERNET = 1; 39 public static final int CONNECTION_WIFI = 2; 40 public static final int CONNECTION_2G = 3; 41 public static final int CONNECTION_3G = 4; 42 public static final int CONNECTION_4G = 5; 43 public static final int CONNECTION_NONE = 6; 44 45 private final Context mContext; 46 private final ArrayList<Long> mNativeChangeNotifiers; 47 private final ObserverList<ConnectionTypeObserver> mConnectionTypeObservers; 48 private NetworkChangeNotifierAutoDetect mAutoDetector; 49 private int mCurrentConnectionType = CONNECTION_UNKNOWN; 50 51 private static NetworkChangeNotifier sInstance; 52 NetworkChangeNotifier(Context context)53 private NetworkChangeNotifier(Context context) { 54 mContext = context.getApplicationContext(); 55 mNativeChangeNotifiers = new ArrayList<Long>(); 56 mConnectionTypeObservers = new ObserverList<ConnectionTypeObserver>(); 57 } 58 59 /** 60 * Initializes the singleton once. 61 */ 62 @CalledByNative init(Context context)63 public static NetworkChangeNotifier init(Context context) { 64 if (sInstance == null) { 65 sInstance = new NetworkChangeNotifier(context); 66 } 67 return sInstance; 68 } 69 isInitialized()70 public static boolean isInitialized() { 71 return sInstance != null; 72 } 73 resetInstanceForTests(Context context)74 static void resetInstanceForTests(Context context) { 75 sInstance = new NetworkChangeNotifier(context); 76 } 77 78 @CalledByNative getCurrentConnectionType()79 public int getCurrentConnectionType() { 80 return mCurrentConnectionType; 81 } 82 83 /** 84 * Adds a native-side observer. 85 */ 86 @CalledByNative addNativeObserver(long nativeChangeNotifier)87 public void addNativeObserver(long nativeChangeNotifier) { 88 mNativeChangeNotifiers.add(nativeChangeNotifier); 89 } 90 91 /** 92 * Removes a native-side observer. 93 */ 94 @CalledByNative removeNativeObserver(long nativeChangeNotifier)95 public void removeNativeObserver(long nativeChangeNotifier) { 96 mNativeChangeNotifiers.remove(nativeChangeNotifier); 97 } 98 99 /** 100 * Returns the singleton instance. 101 */ getInstance()102 public static NetworkChangeNotifier getInstance() { 103 assert sInstance != null; 104 return sInstance; 105 } 106 107 /** 108 * Enables auto detection of the current network state based on notifications from the system. 109 * Note that passing true here requires the embedding app have the platform ACCESS_NETWORK_STATE 110 * permission. 111 * 112 * @param shouldAutoDetect true if the NetworkChangeNotifier should listen for system changes in 113 * network connectivity. 114 */ setAutoDetectConnectivityState(boolean shouldAutoDetect)115 public static void setAutoDetectConnectivityState(boolean shouldAutoDetect) { 116 getInstance().setAutoDetectConnectivityStateInternal(shouldAutoDetect); 117 } 118 destroyAutoDetector()119 private void destroyAutoDetector() { 120 if (mAutoDetector != null) { 121 mAutoDetector.destroy(); 122 mAutoDetector = null; 123 } 124 } 125 setAutoDetectConnectivityStateInternal(boolean shouldAutoDetect)126 private void setAutoDetectConnectivityStateInternal(boolean shouldAutoDetect) { 127 if (shouldAutoDetect) { 128 if (mAutoDetector == null) { 129 mAutoDetector = new NetworkChangeNotifierAutoDetect( 130 new NetworkChangeNotifierAutoDetect.Observer() { 131 @Override 132 public void onConnectionTypeChanged(int newConnectionType) { 133 updateCurrentConnectionType(newConnectionType); 134 } 135 }, 136 mContext); 137 mCurrentConnectionType = mAutoDetector.getCurrentConnectionType(); 138 } 139 } else { 140 destroyAutoDetector(); 141 } 142 } 143 144 /** 145 * Updates the perceived network state when not auto-detecting changes to connectivity. 146 * 147 * @param networkAvailable True if the NetworkChangeNotifier should perceive a "connected" 148 * state, false implies "disconnected". 149 */ 150 @CalledByNative forceConnectivityState(boolean networkAvailable)151 public static void forceConnectivityState(boolean networkAvailable) { 152 setAutoDetectConnectivityState(false); 153 getInstance().forceConnectivityStateInternal(networkAvailable); 154 } 155 forceConnectivityStateInternal(boolean forceOnline)156 private void forceConnectivityStateInternal(boolean forceOnline) { 157 boolean connectionCurrentlyExists = mCurrentConnectionType != CONNECTION_NONE; 158 if (connectionCurrentlyExists != forceOnline) { 159 updateCurrentConnectionType(forceOnline ? CONNECTION_UNKNOWN : CONNECTION_NONE); 160 } 161 } 162 updateCurrentConnectionType(int newConnectionType)163 private void updateCurrentConnectionType(int newConnectionType) { 164 mCurrentConnectionType = newConnectionType; 165 notifyObserversOfConnectionTypeChange(newConnectionType); 166 } 167 168 /** 169 * Alerts all observers of a connection change. 170 */ notifyObserversOfConnectionTypeChange(int newConnectionType)171 void notifyObserversOfConnectionTypeChange(int newConnectionType) { 172 for (Long nativeChangeNotifier : mNativeChangeNotifiers) { 173 nativeNotifyConnectionTypeChanged(nativeChangeNotifier, newConnectionType); 174 } 175 for (ConnectionTypeObserver observer : mConnectionTypeObservers) { 176 observer.onConnectionTypeChanged(newConnectionType); 177 } 178 } 179 180 /** 181 * Adds an observer for any connection type changes. 182 */ addConnectionTypeObserver(ConnectionTypeObserver observer)183 public static void addConnectionTypeObserver(ConnectionTypeObserver observer) { 184 getInstance().addConnectionTypeObserverInternal(observer); 185 } 186 addConnectionTypeObserverInternal(ConnectionTypeObserver observer)187 private void addConnectionTypeObserverInternal(ConnectionTypeObserver observer) { 188 if (!mConnectionTypeObservers.hasObserver(observer)) { 189 mConnectionTypeObservers.addObserver(observer); 190 } 191 } 192 193 /** 194 * Removes an observer for any connection type changes. 195 */ removeConnectionTypeObserver(ConnectionTypeObserver observer)196 public static void removeConnectionTypeObserver(ConnectionTypeObserver observer) { 197 getInstance().removeConnectionTypeObserverInternal(observer); 198 } 199 removeConnectionTypeObserverInternal(ConnectionTypeObserver observer)200 private void removeConnectionTypeObserverInternal(ConnectionTypeObserver observer) { 201 mConnectionTypeObservers.removeObserver(observer); 202 } 203 204 @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid") nativeNotifyConnectionTypeChanged(long nativePtr, int newConnectionType)205 private native void nativeNotifyConnectionTypeChanged(long nativePtr, int newConnectionType); 206 207 // For testing only. getAutoDetectorForTest()208 public static NetworkChangeNotifierAutoDetect getAutoDetectorForTest() { 209 return getInstance().mAutoDetector; 210 } 211 212 /** 213 * Checks if there currently is connectivity. 214 */ isOnline()215 public static boolean isOnline() { 216 int connectionType = getInstance().getCurrentConnectionType(); 217 return connectionType != CONNECTION_UNKNOWN && connectionType != CONNECTION_NONE; 218 } 219 } 220