1 package android.net.wifi; 2 3 import android.annotation.SystemApi; 4 import android.content.Context; 5 import android.os.Bundle; 6 import android.os.Handler; 7 import android.os.HandlerThread; 8 import android.os.Looper; 9 import android.os.Message; 10 import android.os.Messenger; 11 import android.os.Parcel; 12 import android.os.Parcelable; 13 import android.os.RemoteException; 14 import android.util.Log; 15 import android.util.SparseArray; 16 17 import com.android.internal.util.AsyncChannel; 18 import com.android.internal.util.Protocol; 19 20 import java.util.concurrent.CountDownLatch; 21 22 /** @hide */ 23 @SystemApi 24 public class RttManager { 25 26 private static final boolean DBG = true; 27 private static final String TAG = "RttManager"; 28 29 public static final int RTT_TYPE_UNSPECIFIED = 0; 30 public static final int RTT_TYPE_ONE_SIDED = 1; 31 public static final int RTT_TYPE_11_V = 2; 32 public static final int RTT_TYPE_11_MC = 4; 33 34 public static final int RTT_PEER_TYPE_UNSPECIFIED = 0; 35 public static final int RTT_PEER_TYPE_AP = 1; 36 public static final int RTT_PEER_TYPE_STA = 2; /* requires NAN */ 37 38 public static final int RTT_CHANNEL_WIDTH_20 = 0; 39 public static final int RTT_CHANNEL_WIDTH_40 = 1; 40 public static final int RTT_CHANNEL_WIDTH_80 = 2; 41 public static final int RTT_CHANNEL_WIDTH_160 = 3; 42 public static final int RTT_CHANNEL_WIDTH_80P80 = 4; 43 public static final int RTT_CHANNEL_WIDTH_5 = 5; 44 public static final int RTT_CHANNEL_WIDTH_10 = 6; 45 public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; 46 47 public static final int RTT_STATUS_SUCCESS = 0; 48 public static final int RTT_STATUS_FAILURE = 1; 49 public static final int RTT_STATUS_FAIL_NO_RSP = 2; 50 public static final int RTT_STATUS_FAIL_REJECTED = 3; 51 public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4; 52 public static final int RTT_STATUS_FAIL_TM_TIMEOUT = 5; 53 public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6; 54 public static final int RTT_STATUS_FAIL_NO_CAPABILITY = 7; 55 public static final int RTT_STATUS_ABORTED = 8; 56 57 public static final int REASON_UNSPECIFIED = -1; 58 public static final int REASON_NOT_AVAILABLE = -2; 59 public static final int REASON_INVALID_LISTENER = -3; 60 public static final int REASON_INVALID_REQUEST = -4; 61 62 public static final String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description"; 63 64 public class Capabilities { 65 public int supportedType; 66 public int supportedPeerType; 67 } 68 69 public Capabilities getCapabilities() { 70 return new Capabilities(); 71 } 72 73 /** specifies parameters for RTT request */ 74 public static class RttParams { 75 76 /** type of device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA */ 77 public int deviceType; 78 79 /** type of RTT being sought; one of RTT_TYPE_ONE_SIDED 80 * RTT_TYPE_11_V or RTT_TYPE_11_MC or RTT_TYPE_UNSPECIFIED */ 81 public int requestType; 82 83 /** mac address of the device being ranged */ 84 public String bssid; 85 86 /** channel frequency that the device is on; optional */ 87 public int frequency; 88 89 /** optional channel width. wider channels result in better accuracy, 90 * but they take longer time, and even get aborted may times; use 91 * RTT_CHANNEL_WIDTH_UNSPECIFIED if not specifying */ 92 public int channelWidth; 93 94 /** number of samples to be taken */ 95 public int num_samples; 96 97 /** number of retries if a sample fails */ 98 public int num_retries; 99 } 100 101 /** pseudo-private class used to parcel arguments */ 102 public static class ParcelableRttParams implements Parcelable { 103 104 public RttParams mParams[]; 105 106 ParcelableRttParams(RttParams[] params) { 107 mParams = params; 108 } 109 110 /** Implement the Parcelable interface {@hide} */ 111 public int describeContents() { 112 return 0; 113 } 114 115 /** Implement the Parcelable interface {@hide} */ 116 public void writeToParcel(Parcel dest, int flags) { 117 if (mParams != null) { 118 dest.writeInt(mParams.length); 119 120 for (RttParams params : mParams) { 121 dest.writeInt(params.deviceType); 122 dest.writeInt(params.requestType); 123 dest.writeString(params.bssid); 124 dest.writeInt(params.frequency); 125 dest.writeInt(params.channelWidth); 126 dest.writeInt(params.num_samples); 127 dest.writeInt(params.num_retries); 128 } 129 } else { 130 dest.writeInt(0); 131 } 132 } 133 134 /** Implement the Parcelable interface {@hide} */ 135 public static final Creator<ParcelableRttParams> CREATOR = 136 new Creator<ParcelableRttParams>() { 137 public ParcelableRttParams createFromParcel(Parcel in) { 138 139 int num = in.readInt(); 140 141 if (num == 0) { 142 return new ParcelableRttParams(null); 143 } 144 145 RttParams params[] = new RttParams[num]; 146 for (int i = 0; i < num; i++) { 147 params[i] = new RttParams(); 148 params[i].deviceType = in.readInt(); 149 params[i].requestType = in.readInt(); 150 params[i].bssid = in.readString(); 151 params[i].frequency = in.readInt(); 152 params[i].channelWidth = in.readInt(); 153 params[i].num_samples = in.readInt(); 154 params[i].num_retries = in.readInt(); 155 156 } 157 158 ParcelableRttParams parcelableParams = new ParcelableRttParams(params); 159 return parcelableParams; 160 } 161 162 public ParcelableRttParams[] newArray(int size) { 163 return new ParcelableRttParams[size]; 164 } 165 }; 166 } 167 168 /** specifies RTT results */ 169 public static class RttResult { 170 /** mac address of the device being ranged */ 171 public String bssid; 172 173 /** status of the request */ 174 public int status; 175 176 /** type of the request used */ 177 public int requestType; 178 179 /** timestamp of completion, in microsecond since boot */ 180 public long ts; 181 182 /** average RSSI observed */ 183 public int rssi; 184 185 /** RSSI spread (i.e. max - min) */ 186 public int rssi_spread; 187 188 /** average transmit rate */ 189 public int tx_rate; 190 191 /** average round trip time in nano second */ 192 public long rtt_ns; 193 194 /** standard deviation observed in round trip time */ 195 public long rtt_sd_ns; 196 197 /** spread (i.e. max - min) round trip time */ 198 public long rtt_spread_ns; 199 200 /** average distance in centimeter, computed based on rtt_ns */ 201 public int distance_cm; 202 203 /** standard deviation observed in distance */ 204 public int distance_sd_cm; 205 206 /** spread (i.e. max - min) distance */ 207 public int distance_spread_cm; 208 } 209 210 211 /** pseudo-private class used to parcel results */ 212 public static class ParcelableRttResults implements Parcelable { 213 214 public RttResult mResults[]; 215 216 public ParcelableRttResults(RttResult[] results) { 217 mResults = results; 218 } 219 220 /** Implement the Parcelable interface {@hide} */ 221 public int describeContents() { 222 return 0; 223 } 224 225 /** Implement the Parcelable interface {@hide} */ 226 public void writeToParcel(Parcel dest, int flags) { 227 if (mResults != null) { 228 dest.writeInt(mResults.length); 229 for (RttResult result : mResults) { 230 dest.writeString(result.bssid); 231 dest.writeInt(result.status); 232 dest.writeInt(result.requestType); 233 dest.writeLong(result.ts); 234 dest.writeInt(result.rssi); 235 dest.writeInt(result.rssi_spread); 236 dest.writeInt(result.tx_rate); 237 dest.writeLong(result.rtt_ns); 238 dest.writeLong(result.rtt_sd_ns); 239 dest.writeLong(result.rtt_spread_ns); 240 dest.writeInt(result.distance_cm); 241 dest.writeInt(result.distance_sd_cm); 242 dest.writeInt(result.distance_spread_cm); 243 } 244 } else { 245 dest.writeInt(0); 246 } 247 } 248 249 /** Implement the Parcelable interface {@hide} */ 250 public static final Creator<ParcelableRttResults> CREATOR = 251 new Creator<ParcelableRttResults>() { 252 public ParcelableRttResults createFromParcel(Parcel in) { 253 254 int num = in.readInt(); 255 256 if (num == 0) { 257 return new ParcelableRttResults(null); 258 } 259 260 RttResult results[] = new RttResult[num]; 261 for (int i = 0; i < num; i++) { 262 results[i] = new RttResult(); 263 results[i].bssid = in.readString(); 264 results[i].status = in.readInt(); 265 results[i].requestType = in.readInt(); 266 results[i].ts = in.readLong(); 267 results[i].rssi = in.readInt(); 268 results[i].rssi_spread = in.readInt(); 269 results[i].tx_rate = in.readInt(); 270 results[i].rtt_ns = in.readLong(); 271 results[i].rtt_sd_ns = in.readLong(); 272 results[i].rtt_spread_ns = in.readLong(); 273 results[i].distance_cm = in.readInt(); 274 results[i].distance_sd_cm = in.readInt(); 275 results[i].distance_spread_cm = in.readInt(); 276 } 277 278 ParcelableRttResults parcelableResults = new ParcelableRttResults(results); 279 return parcelableResults; 280 } 281 282 public ParcelableRttResults[] newArray(int size) { 283 return new ParcelableRttResults[size]; 284 } 285 }; 286 } 287 288 289 public static interface RttListener { 290 public void onSuccess(RttResult[] results); 291 public void onFailure(int reason, String description); 292 public void onAborted(); 293 } 294 295 public void startRanging(RttParams[] params, RttListener listener) { 296 validateChannel(); 297 ParcelableRttParams parcelableParams = new ParcelableRttParams(params); 298 sAsyncChannel.sendMessage(CMD_OP_START_RANGING, 299 0, putListener(listener), parcelableParams); 300 } 301 302 public void stopRanging(RttListener listener) { 303 validateChannel(); 304 sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener)); 305 } 306 307 /* private methods */ 308 public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER; 309 310 public static final int CMD_OP_START_RANGING = BASE + 0; 311 public static final int CMD_OP_STOP_RANGING = BASE + 1; 312 public static final int CMD_OP_FAILED = BASE + 2; 313 public static final int CMD_OP_SUCCEEDED = BASE + 3; 314 public static final int CMD_OP_ABORTED = BASE + 4; 315 316 private Context mContext; 317 private IRttManager mService; 318 319 private static final int INVALID_KEY = 0; 320 private static int sListenerKey = 1; 321 322 private static final SparseArray sListenerMap = new SparseArray(); 323 private static final Object sListenerMapLock = new Object(); 324 325 private static AsyncChannel sAsyncChannel; 326 private static CountDownLatch sConnected; 327 328 private static final Object sThreadRefLock = new Object(); 329 private static int sThreadRefCount; 330 private static HandlerThread sHandlerThread; 331 332 /** 333 * Create a new WifiScanner instance. 334 * Applications will almost always want to use 335 * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve 336 * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}. 337 * @param context the application context 338 * @param service the Binder interface 339 * @hide 340 */ 341 342 public RttManager(Context context, IRttManager service) { 343 mContext = context; 344 mService = service; 345 init(); 346 } 347 348 private void init() { 349 synchronized (sThreadRefLock) { 350 if (++sThreadRefCount == 1) { 351 Messenger messenger = null; 352 try { 353 Log.d(TAG, "Get the messenger from " + mService); 354 messenger = mService.getMessenger(); 355 } catch (RemoteException e) { 356 /* do nothing */ 357 } catch (SecurityException e) { 358 /* do nothing */ 359 } 360 361 if (messenger == null) { 362 sAsyncChannel = null; 363 return; 364 } 365 366 sHandlerThread = new HandlerThread("WifiScanner"); 367 sAsyncChannel = new AsyncChannel(); 368 sConnected = new CountDownLatch(1); 369 370 sHandlerThread.start(); 371 Handler handler = new ServiceHandler(sHandlerThread.getLooper()); 372 sAsyncChannel.connect(mContext, handler, messenger); 373 try { 374 sConnected.await(); 375 } catch (InterruptedException e) { 376 Log.e(TAG, "interrupted wait at init"); 377 } 378 } 379 } 380 } 381 382 private void validateChannel() { 383 if (sAsyncChannel == null) throw new IllegalStateException( 384 "No permission to access and change wifi or a bad initialization"); 385 } 386 387 private static int putListener(Object listener) { 388 if (listener == null) return INVALID_KEY; 389 int key; 390 synchronized (sListenerMapLock) { 391 do { 392 key = sListenerKey++; 393 } while (key == INVALID_KEY); 394 sListenerMap.put(key, listener); 395 } 396 return key; 397 } 398 399 private static Object getListener(int key) { 400 if (key == INVALID_KEY) return null; 401 synchronized (sListenerMapLock) { 402 Object listener = sListenerMap.get(key); 403 return listener; 404 } 405 } 406 407 private static int getListenerKey(Object listener) { 408 if (listener == null) return INVALID_KEY; 409 synchronized (sListenerMapLock) { 410 int index = sListenerMap.indexOfValue(listener); 411 if (index == -1) { 412 return INVALID_KEY; 413 } else { 414 return sListenerMap.keyAt(index); 415 } 416 } 417 } 418 419 private static Object removeListener(int key) { 420 if (key == INVALID_KEY) return null; 421 synchronized (sListenerMapLock) { 422 Object listener = sListenerMap.get(key); 423 sListenerMap.remove(key); 424 return listener; 425 } 426 } 427 428 private static int removeListener(Object listener) { 429 int key = getListenerKey(listener); 430 if (key == INVALID_KEY) return key; 431 synchronized (sListenerMapLock) { 432 sListenerMap.remove(key); 433 return key; 434 } 435 } 436 437 private static class ServiceHandler extends Handler { 438 ServiceHandler(Looper looper) { 439 super(looper); 440 } 441 @Override 442 public void handleMessage(Message msg) { 443 switch (msg.what) { 444 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 445 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 446 sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 447 } else { 448 Log.e(TAG, "Failed to set up channel connection"); 449 // This will cause all further async API calls on the WifiManager 450 // to fail and throw an exception 451 sAsyncChannel = null; 452 } 453 sConnected.countDown(); 454 return; 455 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: 456 return; 457 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 458 Log.e(TAG, "Channel connection lost"); 459 // This will cause all further async API calls on the WifiManager 460 // to fail and throw an exception 461 sAsyncChannel = null; 462 getLooper().quit(); 463 return; 464 } 465 466 Object listener = getListener(msg.arg2); 467 if (listener == null) { 468 if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2); 469 return; 470 } else { 471 if (DBG) Log.d(TAG, "listener key = " + msg.arg2); 472 } 473 474 switch (msg.what) { 475 /* ActionListeners grouped together */ 476 case CMD_OP_SUCCEEDED : 477 reportSuccess(listener, msg); 478 removeListener(msg.arg2); 479 break; 480 case CMD_OP_FAILED : 481 reportFailure(listener, msg); 482 removeListener(msg.arg2); 483 break; 484 case CMD_OP_ABORTED : 485 ((RttListener) listener).onAborted(); 486 removeListener(msg.arg2); 487 break; 488 default: 489 if (DBG) Log.d(TAG, "Ignoring message " + msg.what); 490 return; 491 } 492 } 493 494 void reportSuccess(Object listener, Message msg) { 495 RttListener rttListener = (RttListener) listener; 496 ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj; 497 ((RttListener) listener).onSuccess(parcelableResults.mResults); 498 } 499 500 void reportFailure(Object listener, Message msg) { 501 RttListener rttListener = (RttListener) listener; 502 Bundle bundle = (Bundle) msg.obj; 503 ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY)); 504 } 505 } 506 507 } 508 509