This change includes various fixes to first RTT implementation and enables RTT APIs end-to-end. Fixes include proper marshalling of objects across API and service and JNI fixes. Change-Id: Ie4ba1ffc6c5b42593e931438b3f03e201ff6be71
509 lines
18 KiB
Java
509 lines
18 KiB
Java
package android.net.wifi;
|
|
|
|
import android.annotation.SystemApi;
|
|
import android.content.Context;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.HandlerThread;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.Messenger;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.os.RemoteException;
|
|
import android.util.Log;
|
|
import android.util.SparseArray;
|
|
|
|
import com.android.internal.util.AsyncChannel;
|
|
import com.android.internal.util.Protocol;
|
|
|
|
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_NOT_AVAILABLE = -2;
|
|
public static final int REASON_INVALID_LISTENER = -3;
|
|
public static final int REASON_INVALID_REQUEST = -4;
|
|
|
|
public static final String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description";
|
|
|
|
public class Capabilities {
|
|
public int supportedType;
|
|
public 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;
|
|
}
|
|
|
|
/** pseudo-private class used to parcel arguments */
|
|
public static class ParcelableRttParams implements Parcelable {
|
|
|
|
public RttParams mParams[];
|
|
|
|
ParcelableRttParams(RttParams[] params) {
|
|
mParams = params;
|
|
}
|
|
|
|
/** Implement the Parcelable interface {@hide} */
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
/** Implement the Parcelable interface {@hide} */
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
if (mParams != null) {
|
|
dest.writeInt(mParams.length);
|
|
|
|
for (RttParams params : mParams) {
|
|
dest.writeInt(params.deviceType);
|
|
dest.writeInt(params.requestType);
|
|
dest.writeString(params.bssid);
|
|
dest.writeInt(params.frequency);
|
|
dest.writeInt(params.channelWidth);
|
|
dest.writeInt(params.num_samples);
|
|
dest.writeInt(params.num_retries);
|
|
}
|
|
} else {
|
|
dest.writeInt(0);
|
|
}
|
|
}
|
|
|
|
/** Implement the Parcelable interface {@hide} */
|
|
public static final Creator<ParcelableRttParams> CREATOR =
|
|
new Creator<ParcelableRttParams>() {
|
|
public ParcelableRttParams createFromParcel(Parcel in) {
|
|
|
|
int num = in.readInt();
|
|
|
|
if (num == 0) {
|
|
return new ParcelableRttParams(null);
|
|
}
|
|
|
|
RttParams params[] = new RttParams[num];
|
|
for (int i = 0; i < num; i++) {
|
|
params[i] = new RttParams();
|
|
params[i].deviceType = in.readInt();
|
|
params[i].requestType = in.readInt();
|
|
params[i].bssid = in.readString();
|
|
params[i].frequency = in.readInt();
|
|
params[i].channelWidth = in.readInt();
|
|
params[i].num_samples = in.readInt();
|
|
params[i].num_retries = in.readInt();
|
|
|
|
}
|
|
|
|
ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
|
|
return parcelableParams;
|
|
}
|
|
|
|
public ParcelableRttParams[] newArray(int size) {
|
|
return new ParcelableRttParams[size];
|
|
}
|
|
};
|
|
}
|
|
|
|
/** specifies RTT results */
|
|
public static class RttResult {
|
|
/** mac address of the device being ranged */
|
|
public String bssid;
|
|
|
|
/** status of the request */
|
|
public int status;
|
|
|
|
/** type of the request used */
|
|
public int requestType;
|
|
|
|
/** 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 int distance_cm;
|
|
|
|
/** standard deviation observed in distance */
|
|
public int distance_sd_cm;
|
|
|
|
/** spread (i.e. max - min) distance */
|
|
public int distance_spread_cm;
|
|
}
|
|
|
|
|
|
/** pseudo-private class used to parcel results */
|
|
public static class ParcelableRttResults implements Parcelable {
|
|
|
|
public RttResult mResults[];
|
|
|
|
public ParcelableRttResults(RttResult[] results) {
|
|
mResults = results;
|
|
}
|
|
|
|
/** Implement the Parcelable interface {@hide} */
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
/** Implement the Parcelable interface {@hide} */
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
if (mResults != null) {
|
|
dest.writeInt(mResults.length);
|
|
for (RttResult result : mResults) {
|
|
dest.writeString(result.bssid);
|
|
dest.writeInt(result.status);
|
|
dest.writeInt(result.requestType);
|
|
dest.writeLong(result.ts);
|
|
dest.writeInt(result.rssi);
|
|
dest.writeInt(result.rssi_spread);
|
|
dest.writeInt(result.tx_rate);
|
|
dest.writeLong(result.rtt_ns);
|
|
dest.writeLong(result.rtt_sd_ns);
|
|
dest.writeLong(result.rtt_spread_ns);
|
|
dest.writeInt(result.distance_cm);
|
|
dest.writeInt(result.distance_sd_cm);
|
|
dest.writeInt(result.distance_spread_cm);
|
|
}
|
|
} else {
|
|
dest.writeInt(0);
|
|
}
|
|
}
|
|
|
|
/** Implement the Parcelable interface {@hide} */
|
|
public static final Creator<ParcelableRttResults> CREATOR =
|
|
new Creator<ParcelableRttResults>() {
|
|
public ParcelableRttResults createFromParcel(Parcel in) {
|
|
|
|
int num = in.readInt();
|
|
|
|
if (num == 0) {
|
|
return new ParcelableRttResults(null);
|
|
}
|
|
|
|
RttResult results[] = new RttResult[num];
|
|
for (int i = 0; i < num; i++) {
|
|
results[i] = new RttResult();
|
|
results[i].bssid = in.readString();
|
|
results[i].status = in.readInt();
|
|
results[i].requestType = in.readInt();
|
|
results[i].ts = in.readLong();
|
|
results[i].rssi = in.readInt();
|
|
results[i].rssi_spread = in.readInt();
|
|
results[i].tx_rate = in.readInt();
|
|
results[i].rtt_ns = in.readLong();
|
|
results[i].rtt_sd_ns = in.readLong();
|
|
results[i].rtt_spread_ns = in.readLong();
|
|
results[i].distance_cm = in.readInt();
|
|
results[i].distance_sd_cm = in.readInt();
|
|
results[i].distance_spread_cm = in.readInt();
|
|
}
|
|
|
|
ParcelableRttResults parcelableResults = new ParcelableRttResults(results);
|
|
return parcelableResults;
|
|
}
|
|
|
|
public ParcelableRttResults[] newArray(int size) {
|
|
return new ParcelableRttResults[size];
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
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();
|
|
ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
|
|
sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
|
|
0, putListener(listener), parcelableParams);
|
|
}
|
|
|
|
public void stopRanging(RttListener listener) {
|
|
validateChannel();
|
|
sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
|
|
}
|
|
|
|
/* private methods */
|
|
public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER;
|
|
|
|
public static final int CMD_OP_START_RANGING = BASE + 0;
|
|
public static final int CMD_OP_STOP_RANGING = BASE + 1;
|
|
public static final int CMD_OP_FAILED = BASE + 2;
|
|
public static final int CMD_OP_SUCCEEDED = BASE + 3;
|
|
public static final int CMD_OP_ABORTED = BASE + 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_RTT_SERVICE Context.WIFI_RTT_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 {
|
|
Log.d(TAG, "Get the messenger from " + mService);
|
|
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 :
|
|
reportSuccess(listener, msg);
|
|
removeListener(msg.arg2);
|
|
break;
|
|
case CMD_OP_FAILED :
|
|
reportFailure(listener, msg);
|
|
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;
|
|
}
|
|
}
|
|
|
|
void reportSuccess(Object listener, Message msg) {
|
|
RttListener rttListener = (RttListener) listener;
|
|
ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
|
|
((RttListener) listener).onSuccess(parcelableResults.mResults);
|
|
}
|
|
|
|
void reportFailure(Object listener, Message msg) {
|
|
RttListener rttListener = (RttListener) listener;
|
|
Bundle bundle = (Bundle) msg.obj;
|
|
((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
|
|
}
|
|
}
|
|
|
|
}
|
|
|