From 7686c061b7c5c981c79e566e3b31f68b9e21c730 Mon Sep 17 00:00:00 2001 From: Vinit Deshpande Date: Mon, 30 Jun 2014 15:25:01 -0700 Subject: Introduce RTT Manager Change-Id: Ic9a16d587897b272f85cdb2b8c52b51bca060d18 --- wifi/java/android/net/wifi/RttManager.java | 334 +++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 wifi/java/android/net/wifi/RttManager.java (limited to 'wifi/java/android/net/wifi/RttManager.java') diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java new file mode 100644 index 0000000..50fd260 --- /dev/null +++ b/wifi/java/android/net/wifi/RttManager.java @@ -0,0 +1,334 @@ +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; + } + } + } + +} + -- cgit v1.1