com.codename1.io.NetworkManager Maven / Gradle / Ivy
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.codename1.io;
import com.codename1.annotations.Async;
import com.codename1.impl.CodenameOneImplementation;
import com.codename1.ui.CN;
import com.codename1.ui.Dialog;
import com.codename1.ui.Display;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.util.EventDispatcher;
import com.codename1.util.AsyncResource;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* Main entry point for managing the connection requests, this is essentially a
* threaded queue that makes sure to route all connections via the network thread
* while sending the callbacks through the Codename One EDT.
*
* The sample
* code below fetches a page of data from the nestoria housing listing API.
* You can see instructions on how to display the data in the {@link com.codename1.components.InfiniteScrollAdapter}
* class. You can read more about networking in Codename One {@link com.codename1.io here}
*
*
* @author Shai Almog
*/
public class NetworkManager {
/**
* Indicates an unknown access point type
*/
public static final int ACCESS_POINT_TYPE_UNKNOWN = 1;
/**
* Indicates a wlan (802.11b/c/g/n) access point type
*/
public static final int ACCESS_POINT_TYPE_WLAN = 2;
/**
* Indicates an access point based on a cable
*/
public static final int ACCESS_POINT_TYPE_CABLE = 3;
/**
* Indicates a 3g network access point type
*/
public static final int ACCESS_POINT_TYPE_NETWORK3G = 4;
/**
* Indicates a 2g network access point type
*/
public static final int ACCESS_POINT_TYPE_NETWORK2G = 5;
/**
* Indicates a corporate routing server access point type (e.g. BIS etc.)
*/
public static final int ACCESS_POINT_TYPE_CORPORATE = 6;
private static final Object LOCK = new Object();
private static final NetworkManager INSTANCE = new NetworkManager();
/**
* This URL is used to check whether an Internet connection is available
* @return the autoDetectURL
*/
public static String getAutoDetectURL() {
return autoDetectURL;
}
/**
* This URL is used to check whether an Internet connection is available
* @param aAutoDetectURL the autoDetectURL to set
*/
public static void setAutoDetectURL(String aAutoDetectURL) {
autoDetectURL = aAutoDetectURL;
}
private Vector pending = new Vector();
private boolean running;
private int threadCount = 1;
private NetworkThread[] networkThreads;
private EventDispatcher errorListeners;
private EventDispatcher progressListeners;
private int timeout = 300000;
private Hashtable threadAssignements = new Hashtable();
private Hashtable userHeaders;
private boolean autoDetected;
private static String autoDetectURL = "https://www.google.com/";
private int nextConnectionId=1;
private NetworkManager() {
}
/**
* Callback for native layer to check the certificates of a connection request.
* @param connectionId THe connection ID of the connection request to check.
* @return True if the certificates check out, or if the ConnectionRequest is not set
* to check certificates.
*
* Currently this is only used by iOS.
* To use this method in other ports, you need to implement the {@link CodenameOneImplementation#checkSSLCertificatesRequiresCallbackFromNative() } to return true.
*
* @see CodenameOneImplementation#checkSSLCertificatesRequiresCallbackFromNative()
* @deprecated For internal use only
*/
static boolean checkCertificatesNativeCallback(int connectionId) {
ArrayList threads = new ArrayList();
synchronized(LOCK) {
if (INSTANCE == null || INSTANCE.networkThreads == null) {
return true;
}
for (NetworkThread nt : INSTANCE.networkThreads) {
if (nt != null) {
threads.add(nt);
}
}
}
for (NetworkThread nt : threads) {
if (nt.currentRequest == null) {
continue;
}
if (nt.currentRequest.getId() == connectionId) {
return nt.currentRequest.checkCertificatesNativeCallback();
}
}
return true;
}
void resetAPN() {
autoDetected = false;
}
boolean handleErrorCode(ConnectionRequest r, int code, String message) {
if(errorListeners != null) {
ActionEvent ev = new NetworkEvent(r, code, message);
errorListeners.fireActionEvent(ev);
return ev.isConsumed();
}
return false;
}
private boolean handleException(ConnectionRequest r, Exception o) {
if(errorListeners != null) {
ActionEvent ev = new NetworkEvent(r, o);
errorListeners.fireActionEvent(ev);
return ev.isConsumed();
}
return false;
}
/**
* The number of threads
*
* @return the threadCount
*/
public int getThreadCount() {
return threadCount;
}
/**
* Thread count should never be changed when the network is running since it will have no effect.
* Increasing the thread count can bring many race conditions and problems to the surface,
* furthermore MIDP doesn't require support for more than one network thread hence increasing
* the thread count might fail.
*
* @param threadCount the threadCount to set
* @deprecated since the network is always running in Codename One this method is quite confusing
* unfortunately fixing it will probably break working code. You should migrate the code to use
* {@link #updateThreadCount(int)}
*/
public void setThreadCount(int threadCount) {
// in auto detect mode multiple threads can break the detections
if(!Util.getImplementation().shouldAutoDetectAccessPoint()) {
this.threadCount = threadCount;
}
}
/**
* Sets the number of network threads and restarts the network threads
* @param threadCount the new number of threads
*/
public void updateThreadCount(int threadCount) {
this.threadCount = threadCount;
shutdown();
start();
}
class NetworkThread implements Runnable {
private ConnectionRequest currentRequest;
private Thread threadInstance;
boolean stopped = false;
public NetworkThread() {
}
public ConnectionRequest getCurrentRequest() {
return currentRequest;
}
public void join() {
try {
threadInstance.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public void start() {
Util.getImplementation().startThread("Network Thread", this);
}
public void interrupt() {
if(threadInstance != null) {
threadInstance.interrupt();
}
}
public Thread getThreadInstance() {
return threadInstance;
}
private boolean runCurrentRequest(@Async.Execute ConnectionRequest req) {
if(threadAssignements.size() > 0) {
String n = currentRequest.getClass().getName();
Integer threadOffset = (Integer)threadAssignements.get(n);
NetworkThread[] networkThreads = NetworkManager.this.networkThreads;
if(networkThreads == null) {
return false;
}
if(threadOffset != null && networkThreads[threadOffset.intValue()] != this) {
synchronized(LOCK) {
if(pending.size() > 0) {
pending.insertElementAt(currentRequest, 1);
return false;
}
pending.addElement(currentRequest);
LOCK.notify();
try {
LOCK.wait(30);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
int frameRate = -1;
boolean requestWasCompleted=true;
// Default this to true because if, for some reason an exception is thrown
// before calling performOperationComplete(), then the request
// won't be retried.
try {
// for higher priority tasks increase the thread priority, for lower
// prioirty tasks decrease it. In critical priority reduce the Codename One
// rendering thread speed for even faster download
switch(currentRequest.getPriority()) {
case ConnectionRequest.PRIORITY_CRITICAL:
frameRate = Display.getInstance().getFrameRate();
Display.getInstance().setFramerate(4);
Thread.currentThread().setPriority(Thread.MAX_PRIORITY - 1);
break;
case ConnectionRequest.PRIORITY_HIGH:
Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2);
break;
case ConnectionRequest.PRIORITY_NORMAL:
break;
case ConnectionRequest.PRIORITY_LOW:
Thread.currentThread().setPriority(Thread.MIN_PRIORITY + 2);
break;
case ConnectionRequest.PRIORITY_REDUNDANT:
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
break;
}
if(progressListeners != null) {
progressListeners.fireActionEvent(new NetworkEvent(currentRequest, NetworkEvent.PROGRESS_TYPE_INITIALIZING));
}
if(currentRequest.getShowOnInit() != null) {
currentRequest.getShowOnInit().showModeless();
}
requestWasCompleted = currentRequest.performOperationComplete();
} catch(IOException e) {
if(!currentRequest.isFailSilently()) {
if(!handleException(currentRequest, e)) {
currentRequest.handleIOException(e);
}
} else {
// for the record
Log.e(e);
}
} catch(RuntimeException er) {
if(!currentRequest.isFailSilently()) {
if(!handleException(currentRequest, er)) {
currentRequest.handleRuntimeException(er);
}
} else {
// for the record
Log.e(er);
}
} finally {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
if(frameRate > -1) {
Display.getInstance().setFramerate(frameRate);
}
if (requestWasCompleted) {
currentRequest.complete = true;
}
if(progressListeners != null) {
progressListeners.fireActionEvent(new NetworkEvent(currentRequest, NetworkEvent.PROGRESS_TYPE_COMPLETED));
}
if(currentRequest.getDisposeOnCompletion() != null && !currentRequest.isRedirecting()) {
// there may be a race condition where the dialog hasn't yet appeared but the
// network request completed
final ConnectionRequest finalReq = currentRequest;
Display.getInstance().callSerially(new Runnable() {
public void run() {
Dialog dlg = finalReq.getDisposeOnCompletion();
if (dlg != null) {
dlg.dispose();
}
}
});
}
}
return true;
}
public void run() {
threadInstance = Thread.currentThread();
while(running && !stopped) {
if(pending.size() > 0) {
// the synchronization here isn't essential, only for good measure
synchronized(LOCK) {
//double lock to prevent a potential exception
if(pending.size() == 0){
continue;
}
currentRequest = (ConnectionRequest)pending.elementAt(0);
pending.removeElementAt(0);
currentRequest.prepare();
if(currentRequest.isKilled()){
continue;
}
currentRequest.setId(nextConnectionId++);
if (nextConnectionId > 2000000000) {
nextConnectionId = 1;
}
}
if(userHeaders != null) {
Enumeration e = userHeaders.keys();
while(e.hasMoreElements()) {
String key = (String)e.nextElement();
String value = (String)userHeaders.get(key);
currentRequest.addRequestHeaderDontRepleace(key, value);
}
}
if (!runCurrentRequest(currentRequest)) {
continue;
}
currentRequest = null;
// wakeup threads waiting for the completion of this network operation
synchronized(LOCK) {
LOCK.notifyAll();
}
} else {
synchronized(LOCK) {
try {
// prevent waiting when there is still a pending request
// this can occur with a race condition since the synchronize
// scope is limited to prevent blocking on add...
if(pending.size() == 0) {
LOCK.wait();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
}
}
boolean hasProgressListeners() {
return progressListeners != null;
}
void fireProgressEvent(ConnectionRequest c, int type, int length, int sentReceived) {
// progressListeners might be made null by a separate thread
EventDispatcher d = progressListeners;
if(d != null) {
NetworkEvent n = new NetworkEvent(c, type);
n.setLength(length);
n.setSentReceived(sentReceived);
d.fireActionEvent(n);
}
}
private NetworkThread createNetworkThread() {
return new NetworkThread();
}
class AutoDetectAPN extends ConnectionRequest {
private Vector aps = null;
private int currentAP;
protected void handleErrorResponseCode(int code, String message) {
retryWithDifferentAPN();
}
protected void handleException(Exception err) {
retryWithDifferentAPN();
}
protected void readResponse(InputStream input) throws IOException {
String s = Util.readToString(input);
if(!s.equals("hi")) {
retryWithDifferentAPN();
}
}
private String nextAP() {
if(aps == null) {
aps = new Vector();
String[] ids = getAPIds();
int idlen = ids.length;
for(int iter = 0 ; iter < idlen ; iter++) {
int t = getAPType(ids[iter]);
if(t == ACCESS_POINT_TYPE_WLAN) {
aps.insertElementAt(ids[iter ], 0);
} else {
if(t == ACCESS_POINT_TYPE_CORPORATE || t == ACCESS_POINT_TYPE_NETWORK3G) {
aps.addElement(ids[iter]);
}
}
}
// add all the 2G networks at the end
for(int iter = 0 ; iter < idlen ; iter++) {
int t = getAPType(ids[iter]);
if(t == ACCESS_POINT_TYPE_NETWORK2G) {
aps.addElement(ids[iter]);
}
}
}
if(currentAP >= aps.size()) {
return null;
}
String s = (String)aps.elementAt(currentAP);
currentAP++;
return s;
}
private void retryWithDifferentAPN() {
String n = nextAP();
if(n == null) {
return;
}
setCurrentAccessPoint(n);
AutoDetectAPN r = new AutoDetectAPN();
r.setPost(false);
r.currentAP = currentAP;
r.aps = aps;
r.setUrl(autoDetectURL);
r.setPriority(ConnectionRequest.PRIORITY_CRITICAL);
addToQueue(r);
}
public boolean equals(Object o) {
return false;
}
}
/**
* There is no need to invoke this method since the network manager is started
* implicitly. It is useful only if you explicitly stop the network manager.
* Invoking this method otherwise will just do nothing.
*/
public void start() {
if(networkThreads != null) {
//throw new IllegalStateException("Network manager already initialized");
return;
}
running = true;
networkThreads = new NetworkThread[getThreadCount()];
for(int iter = 0 ; iter < getThreadCount() ; iter++) {
networkThreads[iter] = createNetworkThread();
networkThreads[iter].start();
}
// we need to implement a timeout thread of our own for this case...
if(!Util.getImplementation().isTimeoutSupported()) {
Util.getImplementation().startThread("Timeout Thread", new Runnable() {
public void run() {
// detect timeout violations by polling
while(running) {
try {
Thread.sleep(timeout / 10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
NetworkThread[] networkThreads = NetworkManager.this.networkThreads;
if(networkThreads == null) {
return;
}
// check for timeout violations on the currently executing threads
int ntlen = networkThreads.length;
for(int iter = 0 ; iter < ntlen ; iter++) {
ConnectionRequest c = networkThreads[iter].getCurrentRequest();
if(c != null) {
int cTimeout = Math.min(timeout, c.getTimeout());
if(c.getTimeout() < 0) {
cTimeout = timeout;
}
if(c.getTimeSinceLastActivity() > cTimeout) {
// we have a timeout problem on our hands! We need to try and kill!
c.kill();
networkThreads[iter].interrupt();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// did the attempt work?
if(networkThreads[iter].getCurrentRequest() == c) {
if(c.getTimeSinceLastActivity() > cTimeout) {
// we need to create a whole new network thread and abandon this one!
if(running) {
networkThreads[iter] = createNetworkThread();
networkThreads[iter].start();
}
}
}
}
}
}
}
}
});
}
}
/**
* Shuts down the network thread, this will trigger failures if you have network requests
* @deprecated This method is for internal use only
*/
public void shutdown() {
running = false;
if(networkThreads != null) {
for(NetworkThread n : networkThreads) {
n.stopped = true;
}
}
networkThreads = null;
synchronized(LOCK) {
LOCK.notifyAll();
}
}
/**
* Shuts down the network thread and waits for shutdown to complete
*/
public void shutdownSync() {
NetworkThread[] n = this.networkThreads;
if(n != null) {
NetworkThread t = n[0];
if(t != null) {
shutdown();
t.join();
}
}
}
/**
* Returns the singleton instance of this class
*
* @return instance of this class
*/
public static NetworkManager getInstance() {
return INSTANCE;
}
private void addSortedToQueue(ConnectionRequest request, int priority) {
for(int iter = 0 ; iter < pending.size() ; iter++) {
ConnectionRequest r = (ConnectionRequest)pending.elementAt(iter);
if(r.getPriority() < priority) {
pending.insertElementAt(request, iter);
return;
}
}
pending.addElement(request);
}
/**
* Adds a header to the global default headers, this header will be implicitly added
* to all requests going out from this point onwards. The main use case for this is
* for authentication information communication via the header.
*
* @param key the key of the header
* @param value the value of the header
*/
public void addDefaultHeader(String key, String value) {
if(userHeaders == null) {
userHeaders = new Hashtable();
}
userHeaders.put(key, value);
}
/**
* Identical to add to queue but returns an AsyncResource object that will resolve to
* the ConnectionRequest.
*
* @param request the request object to add.
* @return AsyncResource resolving to the connection request on complete.
* @since 7.0
*/
public AsyncResource addToQueueAsync(final ConnectionRequest request) {
final AsyncResource out = new AsyncResource();
class WaitingClass implements ActionListener {
public void actionPerformed(NetworkEvent e) {
if(e.getError() != null) {
removeProgressListener(this);
removeErrorListener(this);
if (!out.isDone()) {
out.error(e.getError());
}
return;
}
if(e.getConnectionRequest() == request) {
if(e.getProgressType() == NetworkEvent.PROGRESS_TYPE_COMPLETED) {
if(request.retrying) {
request.retrying = false;
return;
}
removeProgressListener(this);
removeErrorListener(this);
if (!out.isDone()) {
out.complete(request);
}
return;
}
}
}
}
WaitingClass w = new WaitingClass();
addProgressListener(w);
addErrorListener(w);
addToQueue(request);
return out;
}
/**
* Identical to add to queue but waits until the request is processed in the queue,
* this is useful for completely synchronous operations.
*
* @param request the request object to add
*/
public void addToQueueAndWait(final ConnectionRequest request) {
class WaitingClass implements Runnable, ActionListener {
private boolean edt = CN.isEdt();
private boolean finishedWaiting;
public void run() {
if (edt) {
while(!finishedWaiting) {
try {
Thread.sleep(30);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
} else {
while(!request.complete) {
try {
Thread.sleep(30);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
public void actionPerformed(NetworkEvent e) {
if(e.getError() != null) {
finishedWaiting = true;
removeProgressListener(this);
removeErrorListener(this);
return;
}
if(e.getConnectionRequest() == request) {
if(e.getProgressType() == NetworkEvent.PROGRESS_TYPE_COMPLETED) {
if(request.retrying) {
request.retrying = false;
return;
}
finishedWaiting = true;
removeProgressListener(this);
removeErrorListener(this);
return;
}
}
}
}
WaitingClass w = new WaitingClass();
if(Display.getInstance().isEdt()) {
addProgressListener(w);
addErrorListener(w);
addToQueue(request);
Display.getInstance().invokeAndBlock(w);
} else {
addToQueue(request);
w.run();
}
}
/**
* Adds the given network connection to the queue of execution
*
* @param request network request for execution
*/
public void addToQueue(ConnectionRequest request) {
addToQueue(request, false);
}
/**
* Kills the given request and waits until the request is killed if it is
* being processed by one of the threads. This method must not be invoked from
* a network thread!
* @param request
*/
public void killAndWait(final ConnectionRequest request) {
request.kill();
class KillWaitingClass implements Runnable {
public void run() {
for(int iter = 0 ; iter < threadCount ; iter++) {
if(networkThreads[iter].currentRequest == request) {
synchronized(LOCK) {
while(networkThreads[iter].currentRequest == request) {
try {
LOCK.wait(20);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
}
}
}
Display.getInstance().invokeAndBlock(new KillWaitingClass());
}
void kill9(final ConnectionRequest request) {
if (request.isKilled()) {
for(int iter = 0 ; iter < threadCount ; iter++) {
if(networkThreads[iter].currentRequest == request) {
synchronized(LOCK) {
if(networkThreads[iter].currentRequest == request) {
networkThreads[iter].interrupt();
networkThreads[iter].stopped = true;
networkThreads[iter] = createNetworkThread();
networkThreads[iter].start();
}
}
}
}
}
}
/**
* Adds the given network connection to the queue of execution
*
* @param request network request for execution
*/
void addToQueue(@Async.Schedule ConnectionRequest request, boolean retry) {
Util.getImplementation().addConnectionToQueue(request);
if(!running) {
start();
}
if(!autoDetected) {
autoDetected = true;
if(Util.getImplementation().shouldAutoDetectAccessPoint()) {
AutoDetectAPN r = new AutoDetectAPN();
r.setPost(false);
r.setUrl(autoDetectURL);
r.setPriority(ConnectionRequest.PRIORITY_CRITICAL);
addToQueue(r, false);
}
}
request.validateImpl();
synchronized(LOCK) {
int i = request.getPriority();
if(!retry) {
if(!request.isDuplicateSupported()) {
if(pending.contains(request)) {
System.out.println("Duplicate entry in the queue: " + request.getClass().getName() + ": " + request);
return;
}
ConnectionRequest currentRequest = networkThreads[0].getCurrentRequest();
if(currentRequest != null && !currentRequest.retrying && currentRequest.equals(request)) {
System.out.println("Duplicate entry detected");
return;
}
}
} else {
i = ConnectionRequest.PRIORITY_HIGH;
}
switch(i) {
case ConnectionRequest.PRIORITY_CRITICAL:
pending.insertElementAt(request, 0);
ConnectionRequest currentRequest = networkThreads[0].getCurrentRequest();
if(currentRequest != null && currentRequest.getPriority() < ConnectionRequest.PRIORITY_CRITICAL) {
if(currentRequest.isPausable()) {
currentRequest.pause();
pending.insertElementAt(currentRequest, 1);
} else {
currentRequest.kill();
}
}
break;
case ConnectionRequest.PRIORITY_HIGH:
case ConnectionRequest.PRIORITY_NORMAL:
case ConnectionRequest.PRIORITY_LOW:
case ConnectionRequest.PRIORITY_REDUNDANT:
addSortedToQueue(request, i);
break;
}
LOCK.notify();
}
}
/**
* Sets the timeout in milliseconds for network connections, a timeout may be "faked"
* for platforms that don't support the notion of a timeout such as MIDP
*
* @param t the timeout duration
*/
public void setTimeout(int t) {
if(Util.getImplementation().isTimeoutSupported()) {
Util.getImplementation().setTimeout(t);
} else {
timeout = t;
}
}
/**
* Returns the timeout duration
*
* @return timeout in milliseconds
*/
public int getTimeout() {
return timeout;
}
/**
* Adds a generic listener to a network error that is invoked before the exception is propagated.
* Note that this handles also server error codes by default! You can change this default behavior setting to false
* ConnectionRequest.setHandleErrorCodesInGlobalErrorHandler(boolean).
* Consume the event in order to prevent it from propagating further.
*
* @param e callback will be invoked with the Exception as the source object
*/
public void addErrorListener(ActionListener e) {
if(errorListeners == null) {
errorListeners = new EventDispatcher();
errorListeners.setBlocking(true);
}
errorListeners.addListener(e);
}
/**
* Removes the given error listener
*
* @param e callback to remove
*/
public void removeErrorListener(ActionListener e) {
if(errorListeners == null) {
return;
}
errorListeners.removeListener(e);
}
/**
* Adds a listener to be notified when progress updates
*
* @param al action listener
*/
public void addProgressListener(ActionListener al) {
if(progressListeners == null) {
progressListeners = new EventDispatcher();
progressListeners.setBlocking(false);
}
progressListeners.addListener(al);
}
/**
* Adds a listener to be notified when progress updates
*
* @param al action listener
*/
public void removeProgressListener(ActionListener al) {
if(progressListeners == null) {
return;
}
progressListeners.removeListener(al);
Collection v = progressListeners.getListenerCollection();
if(v == null || v.size() == 0) {
progressListeners = null;
}
}
/**
* Makes sure the given class (subclass of ConnectionRequest) is always assigned
* to the given thread number. This is useful for a case of an application that wants
* all background downloads to occur on one thread so it doesn't tie up the main
* network thread (but doesn't stop like a low priority request would).
*
* @param requestType the class of the specific connection request
* @param offset the offset of the thread starting from 0 and smaller than thread count
*/
public void assignToThread(Class requestType, int offset) {
threadAssignements.put(requestType.getName(), new Integer(offset));
}
/**
* This method returns all pending ConnectioRequest connections.
* @return the queue elements
*/
public Enumeration enumurateQueue(){
Vector elements = new Vector();
synchronized(LOCK) {
Enumeration e = pending.elements();
while(e.hasMoreElements()){
elements.addElement(e.nextElement());
}
}
return elements.elements();
}
/**
* Indicates that the network queue is idle
*
* @return true if no network activity is in progress or pending
*/
public boolean isQueueIdle() {
return pending == null ||
networkThreads == null ||
networkThreads[0] == null ||
(pending.size() == 0 && networkThreads[0].getCurrentRequest() == null);
}
/**
* Indicates whether looking up an access point is supported by this device
*
* @return true if access point lookup is supported
*/
public boolean isAPSupported() {
return Util.getImplementation().isAPSupported();
}
/**
* Returns the ids of the access points available if supported
*
* @return ids of access points
*/
public String[] getAPIds() {
return Util.getImplementation().getAPIds();
}
/**
* Returns the type of the access point
*
* @param id access point id
* @return one of the supported access point types from network manager
*/
public int getAPType(String id) {
return Util.getImplementation().getAPType(id);
}
/**
* Returns the user displayable name for the given access point
*
* @param id the id of the access point
* @return the name of the access point
*/
public String getAPName(String id) {
return Util.getImplementation().getAPName(id);
}
/**
* Returns the id of the current access point
*
* @return id of the current access point
*/
public String getCurrentAccessPoint() {
return Util.getImplementation().getCurrentAccessPoint();
}
/**
* Returns the id of the current access point
*
* @param id id of the current access point
*/
public void setCurrentAccessPoint(String id) {
Util.getImplementation().setCurrentAccessPoint(id);
}
}