NetworkChangeNotifier.java revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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
5package org.chromium.net;
6
7import android.content.Context;
8
9import org.chromium.base.CalledByNative;
10import org.chromium.base.JNINamespace;
11import org.chromium.base.NativeClassQualifiedName;
12import org.chromium.base.ObserverList;
13
14import 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")
27public 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 {
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    public static final int CONNECTION_BLUETOOTH = 7;
45
46    private final Context mContext;
47    private final ArrayList<Long> mNativeChangeNotifiers;
48    private final ObserverList<ConnectionTypeObserver> mConnectionTypeObservers;
49    private NetworkChangeNotifierAutoDetect mAutoDetector;
50    private int mCurrentConnectionType = CONNECTION_UNKNOWN;
51
52    private static NetworkChangeNotifier sInstance;
53
54    private NetworkChangeNotifier(Context context) {
55        mContext = context.getApplicationContext();
56        mNativeChangeNotifiers = new ArrayList<Long>();
57        mConnectionTypeObservers = new ObserverList<ConnectionTypeObserver>();
58    }
59
60    /**
61     * Initializes the singleton once.
62     */
63    @CalledByNative
64    public static NetworkChangeNotifier init(Context context) {
65        if (sInstance == null) {
66            sInstance = new NetworkChangeNotifier(context);
67        }
68        return sInstance;
69    }
70
71    public static boolean isInitialized() {
72        return sInstance != null;
73    }
74
75    static void resetInstanceForTests(Context context) {
76        sInstance = new NetworkChangeNotifier(context);
77    }
78
79    @CalledByNative
80    public int getCurrentConnectionType() {
81        return mCurrentConnectionType;
82    }
83
84    /**
85     * Adds a native-side observer.
86     */
87    @CalledByNative
88    public void addNativeObserver(long nativeChangeNotifier) {
89        mNativeChangeNotifiers.add(nativeChangeNotifier);
90    }
91
92    /**
93     * Removes a native-side observer.
94     */
95    @CalledByNative
96    public void removeNativeObserver(long nativeChangeNotifier) {
97        mNativeChangeNotifiers.remove(nativeChangeNotifier);
98    }
99
100    /**
101     * Returns the singleton instance.
102     */
103    public static NetworkChangeNotifier getInstance() {
104        assert sInstance != null;
105        return sInstance;
106    }
107
108    /**
109     * Enables auto detection of the current network state based on notifications from the system.
110     * Note that passing true here requires the embedding app have the platform ACCESS_NETWORK_STATE
111     * permission.
112     *
113     * @param shouldAutoDetect true if the NetworkChangeNotifier should listen for system changes in
114     *    network connectivity.
115     */
116    public static void setAutoDetectConnectivityState(boolean shouldAutoDetect) {
117        getInstance().setAutoDetectConnectivityStateInternal(shouldAutoDetect);
118    }
119
120    private void destroyAutoDetector() {
121        if (mAutoDetector != null) {
122            mAutoDetector.destroy();
123            mAutoDetector = null;
124        }
125    }
126
127    private void setAutoDetectConnectivityStateInternal(boolean shouldAutoDetect) {
128        if (shouldAutoDetect) {
129            if (mAutoDetector == null) {
130                mAutoDetector = new NetworkChangeNotifierAutoDetect(
131                    new NetworkChangeNotifierAutoDetect.Observer() {
132                        @Override
133                        public void onConnectionTypeChanged(int newConnectionType) {
134                            updateCurrentConnectionType(newConnectionType);
135                        }
136                    },
137                    mContext);
138                updateCurrentConnectionType(mAutoDetector.getCurrentConnectionType());
139            }
140        } else {
141            destroyAutoDetector();
142        }
143    }
144
145    /**
146     * Updates the perceived network state when not auto-detecting changes to connectivity.
147     *
148     * @param networkAvailable True if the NetworkChangeNotifier should perceive a "connected"
149     *    state, false implies "disconnected".
150     */
151    @CalledByNative
152    public static void forceConnectivityState(boolean networkAvailable) {
153        setAutoDetectConnectivityState(false);
154        getInstance().forceConnectivityStateInternal(networkAvailable);
155    }
156
157    private void forceConnectivityStateInternal(boolean forceOnline) {
158        boolean connectionCurrentlyExists = mCurrentConnectionType != CONNECTION_NONE;
159        if (connectionCurrentlyExists != forceOnline) {
160            updateCurrentConnectionType(forceOnline ? CONNECTION_UNKNOWN : CONNECTION_NONE);
161        }
162    }
163
164    private void updateCurrentConnectionType(int newConnectionType) {
165        mCurrentConnectionType = newConnectionType;
166        notifyObserversOfConnectionTypeChange(newConnectionType);
167    }
168
169    /**
170     * Alerts all observers of a connection change.
171     */
172    void notifyObserversOfConnectionTypeChange(int newConnectionType) {
173        for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
174            nativeNotifyConnectionTypeChanged(nativeChangeNotifier, newConnectionType);
175        }
176        for (ConnectionTypeObserver observer : mConnectionTypeObservers) {
177            observer.onConnectionTypeChanged(newConnectionType);
178        }
179    }
180
181    /**
182     * Adds an observer for any connection type changes.
183     */
184    public static void addConnectionTypeObserver(ConnectionTypeObserver observer) {
185        getInstance().addConnectionTypeObserverInternal(observer);
186    }
187
188    private void addConnectionTypeObserverInternal(ConnectionTypeObserver observer) {
189        mConnectionTypeObservers.addObserver(observer);
190    }
191
192    /**
193     * Removes an observer for any connection type changes.
194     */
195    public static void removeConnectionTypeObserver(ConnectionTypeObserver observer) {
196        getInstance().removeConnectionTypeObserverInternal(observer);
197    }
198
199    private void removeConnectionTypeObserverInternal(ConnectionTypeObserver observer) {
200        mConnectionTypeObservers.removeObserver(observer);
201    }
202
203    @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
204    private native void nativeNotifyConnectionTypeChanged(long nativePtr, int newConnectionType);
205
206    // For testing only.
207    public static NetworkChangeNotifierAutoDetect getAutoDetectorForTest() {
208        return getInstance().mAutoDetector;
209    }
210
211    /**
212     * Checks if there currently is connectivity.
213     */
214    public static boolean isOnline() {
215        int connectionType = getInstance().getCurrentConnectionType();
216        return connectionType != CONNECTION_UNKNOWN && connectionType != CONNECTION_NONE;
217    }
218}
219