blob: 50fd2609945abdc8c291696d80029bd4b90df4cf [file] [log] [blame]
package android.net.wifi;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.util.AsyncChannel;
import java.util.concurrent.CountDownLatch;
/** @hide */
@SystemApi
public class RttManager {
private static final boolean DBG = true;
private static final String TAG = "RttManager";
public static final int RTT_TYPE_UNSPECIFIED = 0;
public static final int RTT_TYPE_ONE_SIDED = 1;
public static final int RTT_TYPE_11_V = 2;
public static final int RTT_TYPE_11_MC = 4;
public static final int RTT_PEER_TYPE_UNSPECIFIED = 0;
public static final int RTT_PEER_TYPE_AP = 1;
public static final int RTT_PEER_TYPE_STA = 2; /* requires NAN */
public static final int RTT_CHANNEL_WIDTH_20 = 0;
public static final int RTT_CHANNEL_WIDTH_40 = 1;
public static final int RTT_CHANNEL_WIDTH_80 = 2;
public static final int RTT_CHANNEL_WIDTH_160 = 3;
public static final int RTT_CHANNEL_WIDTH_80P80 = 4;
public static final int RTT_CHANNEL_WIDTH_5 = 5;
public static final int RTT_CHANNEL_WIDTH_10 = 6;
public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1;
public static final int RTT_STATUS_SUCCESS = 0;
public static final int RTT_STATUS_FAILURE = 1;
public static final int RTT_STATUS_FAIL_NO_RSP = 2;
public static final int RTT_STATUS_FAIL_REJECTED = 3;
public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4;
public static final int RTT_STATUS_FAIL_TM_TIMEOUT = 5;
public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6;
public static final int RTT_STATUS_FAIL_NO_CAPABILITY = 7;
public static final int RTT_STATUS_ABORTED = 8;
public static final int REASON_UNSPECIFIED = -1;
public static final int REASON_INVALID_LISTENER = -2;
public static final int REASON_INVALID_REQUEST = -3;
public class Capabilities {
int supportedType;
int supportedPeerType;
}
public Capabilities getCapabilities() {
return new Capabilities();
}
/** specifies parameters for RTT request */
public static class RttParams {
/** type of device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA */
public int deviceType;
/** type of RTT being sought; one of RTT_TYPE_ONE_SIDED
* RTT_TYPE_11_V or RTT_TYPE_11_MC or RTT_TYPE_UNSPECIFIED */
public int requestType;
/** mac address of the device being ranged */
public String bssid;
/** channel frequency that the device is on; optional */
public int frequency;
/** optional channel width. wider channels result in better accuracy,
* but they take longer time, and even get aborted may times; use
* RTT_CHANNEL_WIDTH_UNSPECIFIED if not specifying */
public int channelWidth;
/** number of samples to be taken */
public int num_samples;
/** number of retries if a sample fails */
public int num_retries;
}
/** specifies RTT results */
public static class RttResult {
/** mac address of the device being ranged */
public String bssid;
/** status of the request */
public int status;
/** timestamp of completion, in microsecond since boot */
public long ts;
/** average RSSI observed */
public int rssi;
/** RSSI spread (i.e. max - min) */
public int rssi_spread;
/** average transmit rate */
public int tx_rate;
/** average round trip time in nano second */
public long rtt_ns;
/** standard deviation observed in round trip time */
public long rtt_sd_ns;
/** spread (i.e. max - min) round trip time */
public long rtt_spread_ns;
/** average distance in centimeter, computed based on rtt_ns */
public long distance_cm;
/** standard deviation observed in distance */
public long distance_sd_cm;
/** spread (i.e. max - min) distance */
public long distance_spread_cm;
}
public static interface RttListener {
public void onSuccess(RttResult results[]);
public void onFailure(int reason, String description);
public void onAborted();
}
public void startRanging(RttParams params[], RttListener listener) {
validateChannel();
sAsyncChannel.sendMessage(CMD_OP_START_RANGING, 0, removeListener(listener), params);
}
public void stopRanging(RttListener listener) {
validateChannel();
sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
}
/* private methods */
public static final int CMD_OP_START_RANGING = 0;
public static final int CMD_OP_STOP_RANGING = 1;
public static final int CMD_OP_FAILED = 2;
public static final int CMD_OP_SUCCEEDED = 3;
public static final int CMD_OP_ABORTED = 4;
private Context mContext;
private IRttManager mService;
private static final int INVALID_KEY = 0;
private static int sListenerKey = 1;
private static final SparseArray sListenerMap = new SparseArray();
private static final Object sListenerMapLock = new Object();
private static AsyncChannel sAsyncChannel;
private static CountDownLatch sConnected;
private static final Object sThreadRefLock = new Object();
private static int sThreadRefCount;
private static HandlerThread sHandlerThread;
/**
* Create a new WifiScanner instance.
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
* @param context the application context
* @param service the Binder interface
* @hide
*/
public RttManager(Context context, IRttManager service) {
mContext = context;
mService = service;
init();
}
private void init() {
synchronized (sThreadRefLock) {
if (++sThreadRefCount == 1) {
Messenger messenger = null;
try {
messenger = mService.getMessenger();
} catch (RemoteException e) {
/* do nothing */
} catch (SecurityException e) {
/* do nothing */
}
if (messenger == null) {
sAsyncChannel = null;
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");
}
}
}
}
private void validateChannel() {
if (sAsyncChannel == null) throw new IllegalStateException(
"No permission to access and change wifi or a bad initialization");
}
private static int putListener(Object listener) {
if (listener == null) return INVALID_KEY;
int key;
synchronized (sListenerMapLock) {
do {
key = sListenerKey++;
} while (key == INVALID_KEY);
sListenerMap.put(key, listener);
}
return key;
}
private static Object getListener(int key) {
if (key == INVALID_KEY) return null;
synchronized (sListenerMapLock) {
Object listener = sListenerMap.get(key);
return listener;
}
}
private static int getListenerKey(Object listener) {
if (listener == null) return INVALID_KEY;
synchronized (sListenerMapLock) {
int index = sListenerMap.indexOfValue(listener);
if (index == -1) {
return INVALID_KEY;
} else {
return sListenerMap.keyAt(index);
}
}
}
private static Object removeListener(int key) {
if (key == INVALID_KEY) return null;
synchronized (sListenerMapLock) {
Object listener = sListenerMap.get(key);
sListenerMap.remove(key);
return listener;
}
}
private static int removeListener(Object listener) {
int key = getListenerKey(listener);
if (key == INVALID_KEY) return key;
synchronized (sListenerMapLock) {
sListenerMap.remove(key);
return key;
}
}
private static class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
} else {
Log.e(TAG, "Failed to set up channel connection");
// This will cause all further async API calls on the WifiManager
// to fail and throw an exception
sAsyncChannel = null;
}
sConnected.countDown();
return;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
return;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Log.e(TAG, "Channel connection lost");
// This will cause all further async API calls on the WifiManager
// to fail and throw an exception
sAsyncChannel = null;
getLooper().quit();
return;
}
Object listener = getListener(msg.arg2);
if (listener == null) {
if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
return;
} else {
if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
}
switch (msg.what) {
/* ActionListeners grouped together */
case CMD_OP_SUCCEEDED :
((RttListener) listener).onSuccess((RttResult[])msg.obj);
break;
case CMD_OP_FAILED :
((RttListener) listener).onFailure(msg.arg1, (String)msg.obj);
removeListener(msg.arg2);
break;
case CMD_OP_ABORTED :
((RttListener) listener).onAborted();
removeListener(msg.arg2);
break;
default:
if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
return;
}
}
}
}