1 /******************************************************************************* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. This program and the accompanying materials |
|
4 * are made available under the terms of the Eclipse Public License v1.0 |
|
5 * which accompanies this distribution, and is available at |
|
6 * http://www.eclipse.org/legal/epl-v10.html |
|
7 * |
|
8 * Contributors: |
|
9 * Nokia Corporation - initial implementation |
|
10 *******************************************************************************/ |
|
11 package org.eclipse.swt.internal.extension; |
|
12 |
|
13 import org.eclipse.swt.SWT; |
|
14 import org.eclipse.swt.widgets.Display; |
|
15 import org.eclipse.swt.widgets.Event; |
|
16 import org.eclipse.swt.widgets.Internal_PackageSupport; |
|
17 import org.eclipse.swt.widgets.Listener; |
|
18 |
|
19 /** |
|
20 * This class provides notifications that can be used to determine which type of |
|
21 * network connections are active at any given moment. |
|
22 */ |
|
23 public final class NetworkStatus { |
|
24 |
|
25 /** |
|
26 * A notification state flag that is raised when there are any active |
|
27 * Ethernet data connections. |
|
28 */ |
|
29 public static final int DATA_ETHERNET = 0x00000001; |
|
30 |
|
31 /** |
|
32 * A notification state flag that is raised when there are any active WLAN |
|
33 * data connections. |
|
34 */ |
|
35 public static final int DATA_WLAN = 0x00000002; |
|
36 |
|
37 /** |
|
38 * A notification state flag that is raised when there are any active CSD, |
|
39 * GPRS, HSCSD, EDGE or cdmaOne data connections. |
|
40 */ |
|
41 public static final int DATA_2G = 0x00000004; |
|
42 |
|
43 /** |
|
44 * A notification state flag that is raised when there are any active CDMA |
|
45 * data connections. |
|
46 */ |
|
47 public static final int DATA_CDMA2000 = 0x00000008; |
|
48 |
|
49 /** |
|
50 * A notification state flag that is raised when there are any active |
|
51 * W-CDMA/UMTS data connections. |
|
52 */ |
|
53 public static final int DATA_WCDMA = 0x00000010; |
|
54 |
|
55 /** |
|
56 * A notification state flag that is raised when there are any active High |
|
57 * Speed Packet Access data connections. |
|
58 */ |
|
59 public static final int DATA_HSPA = 0x00000020; |
|
60 |
|
61 /** |
|
62 * A notification state flag that is raised when there are any active |
|
63 * Bluetooth data connections. |
|
64 */ |
|
65 public static final int DATA_BLUETOOTH = 0x00000040; |
|
66 |
|
67 /** |
|
68 * A notification state flag that is raised when there are any active WiMAX |
|
69 * data connections. |
|
70 */ |
|
71 public static final int DATA_WIMAX = 0x00000080; |
|
72 |
|
73 /** |
|
74 * A notification state flag that is raised when there are any active voice |
|
75 * calls. |
|
76 */ |
|
77 public static final int VOICE_CALL = 0x00000100; |
|
78 |
|
79 // This flag is set for any other active types than the ones above. |
|
80 // No events are sent for this type of active connections. |
|
81 private static final int UNKNOWN = 0x80000000; |
|
82 |
|
83 // The notified states of the connection types |
|
84 private static int notifiedStates; |
|
85 |
|
86 // Singleton instance |
|
87 private static NetworkStatus instance; |
|
88 |
|
89 // References to the listeners of the clients |
|
90 private static NetworkStatusListener[] listeners; |
|
91 |
|
92 // QNetworkConfigurationManager and XQCallInfo handles. |
|
93 // Can be 0 if required native parts not compiled in. |
|
94 private static int qNetworkConfigurationManagerHandle; |
|
95 private static int xqCallInfoHandle; |
|
96 |
|
97 // QNetworkConfiguration objects for active connections |
|
98 private static int activeConfigHandles[]; |
|
99 |
|
100 // The dispose listener that is added to Display |
|
101 private static Listener disposeListener; |
|
102 |
|
103 private NetworkStatus() { |
|
104 xqCallInfoHandle = OS.XQCallInfo_create(); |
|
105 qNetworkConfigurationManagerHandle = OS.QNetworkConfigurationManager_new(0); |
|
106 hookEvents(); |
|
107 addDisposeListener(); |
|
108 handleNetworkConfigurationChange(); |
|
109 handleCallInformationChanged(); |
|
110 } |
|
111 |
|
112 private static Display getDisplay() { |
|
113 Display display; |
|
114 display = Internal_PackageSupport.getInternalDisplayInstance(); |
|
115 if(display == null) { |
|
116 display = Internal_PackageSupport.getDisplayInstance(); |
|
117 } |
|
118 return display; |
|
119 } |
|
120 |
|
121 private static void addDisposeListener() { |
|
122 disposeListener = new Listener() { |
|
123 public void handleEvent(Event event) { |
|
124 destroy(); |
|
125 } |
|
126 }; |
|
127 getDisplay().addListener(SWT.Dispose, disposeListener); |
|
128 } |
|
129 |
|
130 private static void removeDisposeListener() { |
|
131 if(disposeListener != null) { |
|
132 Display display = getDisplay(); |
|
133 if(display != null && !display.isDisposed()) { |
|
134 display.removeListener(SWT.Dispose, disposeListener); |
|
135 disposeListener = null; |
|
136 } |
|
137 } |
|
138 } |
|
139 |
|
140 private static void checkThread() { |
|
141 Display display = getDisplay(); |
|
142 if(display == null) { |
|
143 throw new RuntimeException("Display doesn't exist"); |
|
144 } |
|
145 if(!display.getThread().equals(Thread.currentThread())) { |
|
146 throw new RuntimeException("Not the UI thread"); |
|
147 } |
|
148 } |
|
149 |
|
150 private static NetworkStatus instance() { |
|
151 if(instance == null) { |
|
152 instance = new NetworkStatus(); |
|
153 } |
|
154 return instance; |
|
155 } |
|
156 |
|
157 private static boolean hasListeners() { |
|
158 if(listeners == null) return false; |
|
159 for(int i = 0; i < listeners.length; ++i) { |
|
160 if(listeners[i] != null) { |
|
161 return true; |
|
162 } |
|
163 } |
|
164 return false; |
|
165 } |
|
166 |
|
167 private static void destroy() { |
|
168 destroyActiveConfigs(); |
|
169 if(qNetworkConfigurationManagerHandle != 0) { |
|
170 org.eclipse.swt.internal.qt.QObjectDeleteWrapper.deleteSafely( |
|
171 qNetworkConfigurationManagerHandle); |
|
172 qNetworkConfigurationManagerHandle = 0; |
|
173 } |
|
174 if(xqCallInfoHandle != 0) { |
|
175 org.eclipse.swt.internal.qt.QObjectDeleteWrapper.deleteSafely( |
|
176 xqCallInfoHandle); |
|
177 xqCallInfoHandle = 0; |
|
178 } |
|
179 listeners = null; |
|
180 instance = null; |
|
181 } |
|
182 |
|
183 /** |
|
184 * Adds the listener to the collection of listeners who will be notified of |
|
185 * the network status changes. Can only be called by the eSWT UI thread. If |
|
186 * there are active connections at the time of adding a listener the |
|
187 * listener will be notified. Adding the first listener will automatically |
|
188 * allocate the required native resources and removing the last listener |
|
189 * will automatically free them. This class will hold a strong reference to |
|
190 * the listener object preventing it from getting garbage collected until |
|
191 * the listener is removed. |
|
192 * |
|
193 * @param listener |
|
194 * the listener which should be notified when the event occurs |
|
195 * |
|
196 * @exception IllegalArgumentException |
|
197 * <ul> |
|
198 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
|
199 * </ul> |
|
200 * |
|
201 * @exception RuntimeException |
|
202 * <ul> |
|
203 * <li>If eSWT Display doesn't exist</li> |
|
204 * <li>If called in a non-UI thread</li> |
|
205 * </ul> |
|
206 * |
|
207 * @see NetworkStatusListener |
|
208 * @see #removeListener(NetworkStatusListener) |
|
209 */ |
|
210 public static void addListener(NetworkStatusListener listener) { |
|
211 if (listener == null) throw new IllegalArgumentException(); |
|
212 checkThread(); |
|
213 instance(); |
|
214 hook(listener); |
|
215 if(notifiedStates != 0) { |
|
216 final NetworkStatusListener asyncNofityListener = listener; |
|
217 final int asyncNotifyStates = notifiedStates; |
|
218 getDisplay().asyncExec(new Runnable() { |
|
219 public void run() { |
|
220 asyncNofityListener.stateChanged(asyncNotifyStates); |
|
221 } |
|
222 }); |
|
223 } |
|
224 } |
|
225 |
|
226 /** |
|
227 * Removes the listener from the collection of listeners who will be |
|
228 * notified of the network status changes. Can only be called by the eSWT UI |
|
229 * thread. Removing the listener will release the reference held by this |
|
230 * class to the listener object. When all the listeners have been removed |
|
231 * the native resources allocated by this class are no longer needed and are |
|
232 * automatically freed. |
|
233 * |
|
234 * @param listener |
|
235 * the listener which should no longer be notified when the event |
|
236 * occurs |
|
237 * |
|
238 * @exception IllegalArgumentException |
|
239 * <ul> |
|
240 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
|
241 * </ul> |
|
242 * |
|
243 * @exception RuntimeException |
|
244 * <ul> |
|
245 * <li>If eSWT Display doesn't exist</li> |
|
246 * <li>If called in a non-UI thread</li> |
|
247 * </ul> |
|
248 * |
|
249 * @see NetworkStatusListener |
|
250 * @see #addListener(NetworkStatusListener) |
|
251 */ |
|
252 public static void removeListener(NetworkStatusListener listener) { |
|
253 if (listener == null) throw new IllegalArgumentException(); |
|
254 checkThread(); |
|
255 unhook(listener); |
|
256 if(!hasListeners()) { |
|
257 destroy(); |
|
258 removeDisposeListener(); |
|
259 } |
|
260 } |
|
261 |
|
262 // Connect the signals |
|
263 private void hookEvents() { |
|
264 // Packet data connections |
|
265 if(qNetworkConfigurationManagerHandle != 0) { |
|
266 int signalProxy = org.eclipse.swt.internal.qt.OS.SignalForwarder_new( |
|
267 qNetworkConfigurationManagerHandle, this, OS.QSIGNAL_NETWORKCONFIGURATIONCHANGED); |
|
268 org.eclipse.swt.internal.qt.OS.QObject_connectOrThrow( |
|
269 qNetworkConfigurationManagerHandle, |
|
270 "configurationAdded(const QNetworkConfiguration&)", |
|
271 signalProxy, "widgetSignal()", |
|
272 org.eclipse.swt.internal.qt.OS.QT_AUTOCONNECTION); |
|
273 org.eclipse.swt.internal.qt.OS.QObject_connectOrThrow( |
|
274 qNetworkConfigurationManagerHandle, |
|
275 "configurationChanged(const QNetworkConfiguration&)", |
|
276 signalProxy, "widgetSignal()", |
|
277 org.eclipse.swt.internal.qt.OS.QT_AUTOCONNECTION); |
|
278 org.eclipse.swt.internal.qt.OS.QObject_connectOrThrow( |
|
279 qNetworkConfigurationManagerHandle, |
|
280 "configurationRemoved(const QNetworkConfiguration&)", |
|
281 signalProxy, "widgetSignal()", |
|
282 org.eclipse.swt.internal.qt.OS.QT_AUTOCONNECTION); |
|
283 } |
|
284 |
|
285 // Voice calls |
|
286 if(xqCallInfoHandle != 0) { |
|
287 int signalProxy = org.eclipse.swt.internal.qt.OS.SignalForwarder_new( |
|
288 xqCallInfoHandle, this, OS.QSIGNAL_CALLINFORMATIONCHANGED); |
|
289 org.eclipse.swt.internal.qt.OS.QObject_connectOrThrow( |
|
290 xqCallInfoHandle, |
|
291 "callInformationChanged()", |
|
292 signalProxy, "widgetSignal()", |
|
293 org.eclipse.swt.internal.qt.OS.QT_AUTOCONNECTION); |
|
294 } |
|
295 } |
|
296 |
|
297 // Connected signals come here |
|
298 boolean eventProcess(int widgetHandle, int eventType, int time, |
|
299 int arg1, int arg2, int arg3, int arg4, int arg5, String arg6) { |
|
300 switch(eventType) { |
|
301 case OS.QSIGNAL_NETWORKCONFIGURATIONCHANGED: |
|
302 handleNetworkConfigurationChange(); |
|
303 break; |
|
304 case OS.QSIGNAL_CALLINFORMATIONCHANGED: |
|
305 handleCallInformationChanged(); |
|
306 break; |
|
307 default: |
|
308 break; |
|
309 } |
|
310 return false; |
|
311 } |
|
312 |
|
313 private static void destroyActiveConfigs() { |
|
314 // Free the QNetworkConfiguration objects |
|
315 if(activeConfigHandles != null) { |
|
316 for(int i = 0; i < activeConfigHandles.length; ++i) { |
|
317 OS.QNetworkConfiguration_delete(activeConfigHandles[i]); |
|
318 activeConfigHandles[i] = 0; |
|
319 } |
|
320 activeConfigHandles = null; |
|
321 } |
|
322 } |
|
323 |
|
324 private static void updateActiveConfigs() { |
|
325 destroyActiveConfigs(); |
|
326 // Get all the currently active configurations |
|
327 if(qNetworkConfigurationManagerHandle != 0) { |
|
328 activeConfigHandles = OS.QNetworkConfigurationManager_allConfigurations( |
|
329 qNetworkConfigurationManagerHandle, OS.QNETWORKCONFIGURATION_ACTIVE); |
|
330 } |
|
331 } |
|
332 |
|
333 private static void handleNetworkConfigurationChange() { |
|
334 updateActiveConfigs(); |
|
335 |
|
336 // Find out the new states of all connection types |
|
337 int newStates = 0; |
|
338 newStates |= (notifiedStates & VOICE_CALL); // Voice call state didn't change |
|
339 for(int i = 0; i < activeConfigHandles.length; ++i) { |
|
340 int activeFlag = bearerNameToConnectionFlag( |
|
341 OS.QNetworkConfiguration_bearerName(activeConfigHandles[i])); |
|
342 if(activeFlag == UNKNOWN) continue; |
|
343 newStates |= activeFlag; |
|
344 } |
|
345 |
|
346 notifyChangedStates(newStates); |
|
347 } |
|
348 |
|
349 private static void handleCallInformationChanged() { |
|
350 if(xqCallInfoHandle != 0) { |
|
351 int newStates = notifiedStates; |
|
352 if(OS.XQCallInfo_swt_hasCalls(xqCallInfoHandle)) { |
|
353 newStates |= VOICE_CALL; |
|
354 } else { |
|
355 newStates &= ~VOICE_CALL; |
|
356 } |
|
357 |
|
358 notifyChangedStates(newStates); |
|
359 } |
|
360 } |
|
361 |
|
362 private static void notifyChangedStates(int newStates) { |
|
363 if(newStates != notifiedStates) { |
|
364 notifyListeners(newStates); |
|
365 } |
|
366 } |
|
367 |
|
368 private static void notifyListeners(int state) { |
|
369 if(listeners != null) { |
|
370 for(int i = 0; i < listeners.length; ++i) { |
|
371 if(listeners[i] == null) break; |
|
372 listeners[i].stateChanged(state); |
|
373 } |
|
374 } |
|
375 notifiedStates = state; |
|
376 } |
|
377 |
|
378 private static int bearerNameToConnectionFlag(String bearerName) { |
|
379 if(bearerName.equalsIgnoreCase("WCDMA")) { |
|
380 return DATA_WCDMA; |
|
381 } else if(bearerName.equalsIgnoreCase("HSPA")) { |
|
382 return DATA_HSPA; |
|
383 } else if(bearerName.equalsIgnoreCase("2G")) { |
|
384 return DATA_2G; |
|
385 } else if(bearerName.equalsIgnoreCase("WLAN")) { |
|
386 return DATA_WLAN; |
|
387 } else if(bearerName.equalsIgnoreCase("Bluetooth")) { |
|
388 return DATA_BLUETOOTH; |
|
389 } else if(bearerName.equalsIgnoreCase("CDMA2000")) { |
|
390 return DATA_CDMA2000; |
|
391 } else if(bearerName.equalsIgnoreCase("WiMAX")) { |
|
392 return DATA_WIMAX; |
|
393 } else if(bearerName.equalsIgnoreCase("Ethernet")) { |
|
394 return DATA_ETHERNET; |
|
395 } |
|
396 return UNKNOWN; |
|
397 } |
|
398 |
|
399 private static void hook(NetworkStatusListener listener) { |
|
400 if (listeners == null) listeners = new NetworkStatusListener[1]; |
|
401 int length = listeners.length, index = length - 1; |
|
402 while (index >= 0) { |
|
403 if (listeners [index] != null) break; |
|
404 --index; |
|
405 } |
|
406 index++; |
|
407 if (index == length) { |
|
408 NetworkStatusListener[] newListeners = new NetworkStatusListener[length + 1]; |
|
409 System.arraycopy (listeners, 0, newListeners, 0, length); |
|
410 listeners = newListeners; |
|
411 } |
|
412 listeners [index] = listener; |
|
413 } |
|
414 |
|
415 private static void unhook(NetworkStatusListener listener) { |
|
416 if (listeners == null) return; |
|
417 for (int i = 0; i < listeners.length; i++) { |
|
418 if (listeners [i] == listener) { |
|
419 remove (i); |
|
420 return; |
|
421 } |
|
422 } |
|
423 } |
|
424 |
|
425 private static void remove (int index) { |
|
426 int end = listeners.length - 1; |
|
427 System.arraycopy (listeners, index + 1, listeners, index, end - index); |
|
428 index = end; |
|
429 listeners [index] = null; |
|
430 } |
|
431 } |
|