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