| 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; |
| } |
| } |
| } |
| |
| } |
| |