diff --git a/api/system-current.txt b/api/system-current.txt index 3706140aa36f0..1de5a9da6a9f4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -21211,6 +21211,7 @@ package android.net.wifi { method public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener); field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000 field public static final int MIN_SCAN_PERIOD_MS = 1000; // 0x3e8 + field public static final int REASON_DUPLICATE_REQEUST = -5; // 0xfffffffb field public static final int REASON_INVALID_LISTENER = -2; // 0xfffffffe field public static final int REASON_INVALID_REQUEST = -3; // 0xfffffffd field public static final int REASON_NOT_AUTHORIZED = -4; // 0xfffffffc diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 2373754d41a07..69e179ddfaf5b 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -30,7 +30,9 @@ import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; import java.util.List; @@ -78,6 +80,8 @@ public class WifiScanner { public static final int REASON_INVALID_REQUEST = -3; /** Invalid request */ public static final int REASON_NOT_AUTHORIZED = -4; + /** An outstanding request with the same listener hasn't finished yet. */ + public static final int REASON_DUPLICATE_REQEUST = -5; /** @hide */ public static final String GET_AVAILABLE_CHANNELS_EXTRA = "Channels"; @@ -460,8 +464,11 @@ public class WifiScanner { * scans should also not share this object. */ public void startBackgroundScan(ScanSettings settings, ScanListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + int key = addListener(listener); + if (key == INVALID_KEY) return; validateChannel(); - sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, putListener(listener), settings); + sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, settings); } /** * stop an ongoing wifi scan @@ -469,8 +476,11 @@ public class WifiScanner { * #startBackgroundScan} */ public void stopBackgroundScan(ScanListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + int key = removeListener(listener); + if (key == INVALID_KEY) return; validateChannel(); - sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener)); + sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key); } /** * reports currently available scan results on appropriate listeners @@ -491,8 +501,11 @@ public class WifiScanner { * scans should also not share this object. */ public void startScan(ScanSettings settings, ScanListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + int key = addListener(listener); + if (key == INVALID_KEY) return; validateChannel(); - sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, putListener(listener), settings); + sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, settings); } /** @@ -501,8 +514,11 @@ public class WifiScanner { * @param listener */ public void stopScan(ScanListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + int key = removeListener(listener); + if (key == INVALID_KEY) return; validateChannel(); - sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, removeListener(listener)); + sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key); } /** specifies information about an access point of interest */ @@ -634,8 +650,11 @@ public class WifiScanner { * provided on {@link #stopTrackingWifiChange} */ public void startTrackingWifiChange(WifiChangeListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + int key = addListener(listener); + if (key == INVALID_KEY) return; validateChannel(); - sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, putListener(listener)); + sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, key); } /** @@ -644,8 +663,10 @@ public class WifiScanner { * #stopTrackingWifiChange} */ public void stopTrackingWifiChange(WifiChangeListener listener) { + int key = removeListener(listener); + if (key == INVALID_KEY) return; validateChannel(); - sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, removeListener(listener)); + sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, key); } /** @hide */ @@ -730,11 +751,14 @@ public class WifiScanner { */ public void startTrackingBssids(BssidInfo[] bssidInfos, int apLostThreshold, BssidListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + int key = addListener(listener); + if (key == INVALID_KEY) return; validateChannel(); HotlistSettings settings = new HotlistSettings(); settings.bssidInfos = bssidInfos; settings.apLostThreshold = apLostThreshold; - sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings); + sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, key, settings); } /** @@ -742,8 +766,11 @@ public class WifiScanner { * @param listener same object provided in {@link #startTrackingBssids} */ public void stopTrackingBssids(BssidListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + int key = removeListener(listener); + if (key == INVALID_KEY) return; validateChannel(); - sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener)); + sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, key); } @@ -812,7 +839,7 @@ public class WifiScanner { private static final Object sThreadRefLock = new Object(); private static int sThreadRefCount; - private static HandlerThread sHandlerThread; + private static Handler sInternalHandler; /** * Create a new WifiScanner instance. @@ -824,12 +851,29 @@ public class WifiScanner { * @hide */ public WifiScanner(Context context, IWifiScanner service) { - mContext = context; - mService = service; - init(); + this(context, service, null, true); } - private void init() { + /** + * Create a new WifiScanner instance. + * + * @param context The application context. + * @param service The IWifiScanner Binder interface + * @param looper Looper for running WifiScanner operations. If null, a handler thread will be + * created for running WifiScanner operations. + * @param waitForConnection If true, this will not return until a connection to Wifi Scanner + * service is established. + * @hide + */ + @VisibleForTesting + public WifiScanner(Context context, IWifiScanner service, Looper looper, + boolean waitForConnection) { + mContext = context; + mService = service; + init(looper, waitForConnection); + } + + private void init(Looper looper, boolean waitForConnection) { synchronized (sThreadRefLock) { if (++sThreadRefCount == 1) { Messenger messenger = null; @@ -846,17 +890,23 @@ public class WifiScanner { return; } - sHandlerThread = new HandlerThread("WifiScanner"); sAsyncChannel = new AsyncChannel(); sConnected = new CountDownLatch(1); - sHandlerThread.start(); - Handler handler = new ServiceHandler(sHandlerThread.getLooper()); - sAsyncChannel.connect(mContext, handler, messenger); - try { - sConnected.await(); - } catch (InterruptedException e) { - Log.e(TAG, "interrupted wait at init"); + if (looper == null) { + HandlerThread thread = new HandlerThread("WifiScanner"); + thread.start(); + sInternalHandler = new ServiceHandler(thread.getLooper()); + } else { + sInternalHandler = new ServiceHandler(looper); + } + sAsyncChannel.connect(mContext, sInternalHandler, messenger); + if (waitForConnection) { + try { + sConnected.await(); + } catch (InterruptedException e) { + Log.e(TAG, "interrupted wait at init"); + } } } } @@ -867,6 +917,30 @@ public class WifiScanner { "No permission to access and change wifi or a bad initialization"); } + // Add a listener into listener map. If the listener already exists, return INVALID_KEY and + // send an error message to internal handler; Otherwise add the listener to the listener map and + // return the key of the listener. + private int addListener(ActionListener listener) { + synchronized (sListenerMap) { + boolean keyExists = (getListenerKey(listener) != INVALID_KEY); + // Note we need to put the listener into listener map even if it's a duplicate as the + // internal handler will need the key to find the listener. In case of duplicates, + // removing duplicate key logic will be handled in internal handler. + int key = putListener(listener); + if (keyExists) { + if (DBG) Log.d(TAG, "listener key already exists"); + OperationResult operationResult = new OperationResult(REASON_DUPLICATE_REQEUST, + "Outstanding request with same key not stopped yet"); + Message message = Message.obtain(sInternalHandler, CMD_OP_FAILED, 0, key, + operationResult); + message.sendToTarget(); + return INVALID_KEY; + } else { + return key; + } + } + } + private static int putListener(Object listener) { if (listener == null) return INVALID_KEY; int key; @@ -910,7 +984,10 @@ public class WifiScanner { private static int removeListener(Object listener) { int key = getListenerKey(listener); - if (key == INVALID_KEY) return key; + if (key == INVALID_KEY) { + Log.e(TAG, "listener cannot be found"); + return key; + } synchronized (sListenerMapLock) { sListenerMap.remove(key); return key;