RttService.java revision 202fdf96e1e0191be37a916d97116df35fe3cbfc
1package com.android.server.wifi; 2 3import android.Manifest; 4import android.content.BroadcastReceiver; 5import android.content.Context; 6import android.content.Intent; 7import android.content.IntentFilter; 8import android.net.wifi.IRttManager; 9import android.net.wifi.RttManager; 10import android.net.wifi.RttManager.ResponderConfig; 11import android.net.wifi.WifiManager; 12import android.os.Bundle; 13import android.os.Handler; 14import android.os.HandlerThread; 15import android.os.Looper; 16import android.os.Message; 17import android.os.Messenger; 18import android.os.RemoteException; 19import android.util.Log; 20import android.util.Slog; 21 22import com.android.internal.util.AsyncChannel; 23import com.android.internal.util.Protocol; 24import com.android.internal.util.State; 25import com.android.internal.util.StateMachine; 26import com.android.server.SystemService; 27 28import java.util.HashMap; 29import java.util.HashSet; 30import java.util.Iterator; 31import java.util.LinkedList; 32import java.util.Queue; 33import java.util.Set; 34 35public final class RttService extends SystemService { 36 37 public static final boolean DBG = true; 38 39 static class RttServiceImpl extends IRttManager.Stub { 40 41 @Override 42 public Messenger getMessenger() { 43 return new Messenger(mClientHandler); 44 } 45 46 private class ClientHandler extends Handler { 47 48 ClientHandler(android.os.Looper looper) { 49 super(looper); 50 } 51 52 @Override 53 public void handleMessage(Message msg) { 54 55 if (DBG) { 56 Log.d(TAG, "ClientHandler got" + msg + " what = " + getDescription(msg.what)); 57 } 58 59 switch (msg.what) { 60 61 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 62 if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { 63 Slog.e(TAG, "Send failed, client connection lost"); 64 } else { 65 if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); 66 } 67 if (DBG) Slog.d(TAG, "closing client " + msg.replyTo); 68 ClientInfo ci = mClients.remove(msg.replyTo); 69 if (ci != null) ci.cleanup(); 70 return; 71 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: 72 AsyncChannel ac = new AsyncChannel(); 73 ac.connected(mContext, this, msg.replyTo); 74 ClientInfo client = new ClientInfo(ac, msg.replyTo); 75 mClients.put(msg.replyTo, client); 76 ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, 77 AsyncChannel.STATUS_SUCCESSFUL); 78 return; 79 } 80 81 ClientInfo ci = mClients.get(msg.replyTo); 82 if (ci == null) { 83 Slog.e(TAG, "Could not find client info for message " + msg.replyTo); 84 replyFailed(msg, RttManager.REASON_INVALID_LISTENER, "Could not find listener"); 85 return; 86 } 87 88 final int validCommands[] = { 89 RttManager.CMD_OP_START_RANGING, 90 RttManager.CMD_OP_STOP_RANGING, 91 RttManager.CMD_OP_ENABLE_RESPONDER, 92 RttManager.CMD_OP_DISABLE_RESPONDER, 93 }; 94 95 for (int cmd : validCommands) { 96 if (cmd == msg.what) { 97 mStateMachine.sendMessage(Message.obtain(msg)); 98 return; 99 } 100 } 101 102 replyFailed(msg, RttManager.REASON_INVALID_REQUEST, "Invalid request"); 103 } 104 105 private String getDescription(int what) { 106 switch(what) { 107 case RttManager.CMD_OP_ENABLE_RESPONDER: 108 return "CMD_OP_ENABLE_RESPONDER"; 109 case RttManager.CMD_OP_DISABLE_RESPONDER: 110 return "CMD_OP_DISABLE_RESPONDER"; 111 default: 112 return "CMD_UNKNOWN"; 113 } 114 } 115 } 116 117 private final WifiNative mWifiNative; 118 private final Context mContext; 119 private final Looper mLooper; 120 private RttStateMachine mStateMachine; 121 private ClientHandler mClientHandler; 122 123 RttServiceImpl(Context context, Looper looper) { 124 mContext = context; 125 mWifiNative = WifiNative.getWlanNativeInterface(); 126 mLooper = looper; 127 } 128 129 public void startService() { 130 mClientHandler = new ClientHandler(mLooper); 131 mStateMachine = new RttStateMachine(mLooper); 132 133 mContext.registerReceiver( 134 new BroadcastReceiver() { 135 @Override 136 public void onReceive(Context context, Intent intent) { 137 int state = intent.getIntExtra( 138 WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED); 139 if (DBG) Log.d(TAG, "SCAN_AVAILABLE : " + state); 140 if (state == WifiManager.WIFI_STATE_ENABLED) { 141 mStateMachine.sendMessage(CMD_DRIVER_LOADED); 142 } else if (state == WifiManager.WIFI_STATE_DISABLED) { 143 mStateMachine.sendMessage(CMD_DRIVER_UNLOADED); 144 } 145 } 146 }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE)); 147 148 mStateMachine.start(); 149 } 150 151 private class RttRequest { 152 Integer key; 153 ClientInfo ci; 154 RttManager.RttParams[] params; 155 156 @Override 157 public String toString() { 158 String str = getClass().getName() + "@" + Integer.toHexString(hashCode()); 159 if(this.key != null) { 160 return str + " key: " + this.key; 161 } else { 162 return str + " key: " + " , null"; 163 } 164 } 165 } 166 167 private class ClientInfo { 168 private final AsyncChannel mChannel; 169 private final Messenger mMessenger; 170 HashMap<Integer, RttRequest> mRequests = new HashMap<Integer, 171 RttRequest>(); 172 // Client keys of all outstanding responders. 173 Set<Integer> mResponderRequests = new HashSet<>(); 174 175 ClientInfo(AsyncChannel c, Messenger m) { 176 mChannel = c; 177 mMessenger = m; 178 } 179 180 void addResponderRequest(int key) { 181 mResponderRequests.add(key); 182 } 183 184 void removeResponderRequest(int key) { 185 mResponderRequests.remove(key); 186 } 187 188 boolean addRttRequest(int key, RttManager.ParcelableRttParams parcelableParams) { 189 if (parcelableParams == null) { 190 return false; 191 } 192 193 RttManager.RttParams params[] = parcelableParams.mParams; 194 195 RttRequest request = new RttRequest(); 196 request.key = key; 197 request.ci = this; 198 request.params = params; 199 mRequests.put(key, request); 200 mRequestQueue.add(request); 201 return true; 202 } 203 204 void removeRttRequest(int key) { 205 mRequests.remove(key); 206 } 207 208 void reportResponderEnableSucceed(int key, ResponderConfig config) { 209 mChannel.sendMessage(RttManager.CMD_OP_ENALBE_RESPONDER_SUCCEEDED, 0, key, config); 210 } 211 212 void reportResponderEnableFailed(int key) { 213 mChannel.sendMessage(RttManager.CMD_OP_ENALBE_RESPONDER_FAILED, 0, key); 214 mResponderRequests.remove(key); 215 } 216 217 void reportResult(RttRequest request, RttManager.RttResult[] results) { 218 RttManager.ParcelableRttResults parcelableResults = 219 new RttManager.ParcelableRttResults(results); 220 221 mChannel.sendMessage(RttManager.CMD_OP_SUCCEEDED, 222 0, request.key, parcelableResults); 223 mRequests.remove(request.key); 224 } 225 226 void reportFailed(RttRequest request, int reason, String description) { 227 reportFailed(request.key, reason, description); 228 } 229 230 void reportFailed(int key, int reason, String description) { 231 Bundle bundle = new Bundle(); 232 bundle.putString(RttManager.DESCRIPTION_KEY, description); 233 mChannel.sendMessage(RttManager.CMD_OP_FAILED, key, reason, bundle); 234 mRequests.remove(key); 235 } 236 237 void reportAborted(int key) { 238 mChannel.sendMessage(RttManager.CMD_OP_ABORTED, 0, key); 239 //All Queued RTT request will be cleaned 240 cleanup(); 241 } 242 243 void cleanup() { 244 mRequests.clear(); 245 mRequestQueue.clear(); 246 } 247 } 248 249 private Queue<RttRequest> mRequestQueue = new LinkedList<RttRequest>(); 250 private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(4); 251 252 private static final int BASE = Protocol.BASE_WIFI_RTT_SERVICE; 253 254 private static final int CMD_DRIVER_LOADED = BASE + 0; 255 private static final int CMD_DRIVER_UNLOADED = BASE + 1; 256 private static final int CMD_ISSUE_NEXT_REQUEST = BASE + 2; 257 private static final int CMD_RTT_RESPONSE = BASE + 3; 258 259 // Maximum duration for responder role. 260 private static final int MAX_RESPONDER_DURATION_SECONDS = 60 * 10; 261 262 class RttStateMachine extends StateMachine { 263 264 DefaultState mDefaultState = new DefaultState(); 265 EnabledState mEnabledState = new EnabledState(); 266 InitiatorEnabledState mInitiatorEnabledState = new InitiatorEnabledState(); 267 ResponderEnabledState mResponderEnabledState = new ResponderEnabledState(); 268 ResponderConfig mResponderConfig; 269 270 RttStateMachine(Looper looper) { 271 super("RttStateMachine", looper); 272 273 // CHECKSTYLE:OFF IndentationCheck 274 addState(mDefaultState); 275 addState(mEnabledState); 276 addState(mInitiatorEnabledState, mEnabledState); 277 addState(mResponderEnabledState, mEnabledState); 278 // CHECKSTYLE:ON IndentationCheck 279 280 setInitialState(mDefaultState); 281 } 282 283 class DefaultState extends State { 284 @Override 285 public boolean processMessage(Message msg) { 286 if (DBG) Log.d(TAG, "DefaultState got" + msg); 287 switch (msg.what) { 288 case CMD_DRIVER_LOADED: 289 transitionTo(mEnabledState); 290 break; 291 case CMD_ISSUE_NEXT_REQUEST: 292 deferMessage(msg); 293 break; 294 case RttManager.CMD_OP_START_RANGING: 295 replyFailed(msg, RttManager.REASON_NOT_AVAILABLE, "Try later"); 296 break; 297 case RttManager.CMD_OP_STOP_RANGING: 298 return HANDLED; 299 case RttManager.CMD_OP_ENABLE_RESPONDER: 300 replyFailed(msg, RttManager.REASON_NOT_AVAILABLE, 301 "Wifi not enabled"); 302 break; 303 case RttManager.CMD_OP_DISABLE_RESPONDER: 304 return HANDLED; 305 default: 306 return NOT_HANDLED; 307 } 308 return HANDLED; 309 } 310 } 311 312 class EnabledState extends State { 313 @Override 314 public boolean processMessage(Message msg) { 315 if (DBG) Log.d(TAG, "EnabledState got" + msg); 316 ClientInfo ci = mClients.get(msg.replyTo); 317 318 switch (msg.what) { 319 case CMD_DRIVER_UNLOADED: 320 transitionTo(mDefaultState); 321 break; 322 case CMD_ISSUE_NEXT_REQUEST: 323 deferMessage(msg); 324 transitionTo(mInitiatorEnabledState); 325 break; 326 case RttManager.CMD_OP_START_RANGING: { 327 //check permission 328 if(DBG) Log.d(TAG, "UID is: " + msg.sendingUid); 329 if (!enforcePermissionCheck(msg)) { 330 break; 331 } 332 333 RttManager.ParcelableRttParams params = 334 (RttManager.ParcelableRttParams)msg.obj; 335 if (params == null || params.mParams == null 336 || params.mParams.length == 0) { 337 replyFailed(msg, 338 RttManager.REASON_INVALID_REQUEST, "No params"); 339 } else if (ci.addRttRequest(msg.arg2, params) == false) { 340 replyFailed(msg, 341 RttManager.REASON_INVALID_REQUEST, "Unspecified"); 342 } else { 343 sendMessage(CMD_ISSUE_NEXT_REQUEST); 344 } 345 } 346 break; 347 case RttManager.CMD_OP_STOP_RANGING: 348 if(!enforcePermissionCheck(msg)) { 349 break; 350 } 351 352 for (Iterator<RttRequest> it = mRequestQueue.iterator(); 353 it.hasNext(); ) { 354 RttRequest request = it.next(); 355 if (request.key == msg.arg2) { 356 if (DBG) Log.d(TAG, "Cancelling not-yet-scheduled RTT"); 357 mRequestQueue.remove(request); 358 request.ci.reportAborted(request.key); 359 break; 360 } 361 } 362 break; 363 case RttManager.CMD_OP_ENABLE_RESPONDER: 364 if (!enforcePermissionCheck(msg)) { 365 break; 366 } 367 int key = msg.arg2; 368 mResponderConfig = 369 mWifiNative.enableRttResponder(MAX_RESPONDER_DURATION_SECONDS); 370 if (DBG) Log.d(TAG, "mWifiNative.enableRttResponder called"); 371 372 if (mResponderConfig != null) { 373 // TODO: remove once mac address is added when enabling responder. 374 mResponderConfig.macAddress = mWifiNative.getMacAddress(); 375 ci.addResponderRequest(key); 376 ci.reportResponderEnableSucceed(key, mResponderConfig); 377 transitionTo(mResponderEnabledState); 378 } else { 379 Log.e(TAG, "enable responder failed"); 380 ci.reportResponderEnableFailed(key); 381 } 382 break; 383 case RttManager.CMD_OP_DISABLE_RESPONDER: 384 if (!enforcePermissionCheck(msg)) { 385 break; 386 } 387 default: 388 return NOT_HANDLED; 389 } 390 return HANDLED; 391 } 392 } 393 394 class InitiatorEnabledState extends State { 395 RttRequest mOutstandingRequest; 396 @Override 397 public boolean processMessage(Message msg) { 398 if (DBG) Log.d(TAG, "RequestPendingState got" + msg); 399 switch (msg.what) { 400 case CMD_DRIVER_UNLOADED: 401 if (mOutstandingRequest != null) { 402 mWifiNative.cancelRtt(mOutstandingRequest.params); 403 if (DBG) Log.d(TAG, "abort key: " + mOutstandingRequest.key); 404 mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key); 405 mOutstandingRequest = null; 406 } 407 transitionTo(mDefaultState); 408 break; 409 case CMD_ISSUE_NEXT_REQUEST: 410 if (mOutstandingRequest == null) { 411 mOutstandingRequest = issueNextRequest(); 412 if (mOutstandingRequest == null) { 413 transitionTo(mEnabledState); 414 } 415 if(mOutstandingRequest != null) { 416 if (DBG) Log.d(TAG, "new mOutstandingRequest.key is: " + 417 mOutstandingRequest.key); 418 } else { 419 if (DBG) Log.d(TAG, 420 "CMD_ISSUE_NEXT_REQUEST: mOutstandingRequest =null "); 421 } 422 } else { 423 /* just wait; we'll issue next request after 424 * current one is finished */ 425 if (DBG) Log.d(TAG, "Current mOutstandingRequest.key is: " + 426 mOutstandingRequest.key); 427 if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST"); 428 } 429 break; 430 case CMD_RTT_RESPONSE: 431 if (DBG) Log.d(TAG, "Received an RTT response from: " + msg.arg2); 432 mOutstandingRequest.ci.reportResult( 433 mOutstandingRequest, (RttManager.RttResult[])msg.obj); 434 mOutstandingRequest = null; 435 sendMessage(CMD_ISSUE_NEXT_REQUEST); 436 break; 437 case RttManager.CMD_OP_STOP_RANGING: 438 if(!enforcePermissionCheck(msg)) { 439 break; 440 } 441 442 if (mOutstandingRequest != null 443 && msg.arg2 == mOutstandingRequest.key) { 444 if (DBG) Log.d(TAG, "Cancelling ongoing RTT of: " + msg.arg2); 445 mWifiNative.cancelRtt(mOutstandingRequest.params); 446 mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key); 447 mOutstandingRequest = null; 448 sendMessage(CMD_ISSUE_NEXT_REQUEST); 449 } else { 450 /* Let EnabledState handle this */ 451 return NOT_HANDLED; 452 } 453 break; 454 default: 455 return NOT_HANDLED; 456 } 457 return HANDLED; 458 } 459 } 460 461 // Check if there are still outstanding responder requests from any client. 462 private boolean hasOutstandingReponderRequests() { 463 for (ClientInfo client : mClients.values()) { 464 if (!client.mResponderRequests.isEmpty()) { 465 return true; 466 } 467 } 468 return false; 469 } 470 471 /** 472 * Representing an outstanding RTT responder state. 473 */ 474 class ResponderEnabledState extends State { 475 @Override 476 public boolean processMessage(Message msg) { 477 if (DBG) Log.d(TAG, "ResponderEnabledState got " + msg); 478 ClientInfo ci = mClients.get(msg.replyTo); 479 int key = msg.arg2; 480 switch(msg.what) { 481 case RttManager.CMD_OP_ENABLE_RESPONDER: 482 // Responder already enabled, simply return the responder config. 483 ci.addResponderRequest(key); 484 ci.reportResponderEnableSucceed(key, mResponderConfig); 485 return HANDLED; 486 case RttManager.CMD_OP_DISABLE_RESPONDER: 487 ci.removeResponderRequest(key); 488 // Only disable responder when there are no outstanding clients. 489 if (!hasOutstandingReponderRequests()) { 490 if (!mWifiNative.disableRttResponder()) { 491 Log.e(TAG, "disable responder failed"); 492 } 493 if (DBG) Log.d(TAG, "mWifiNative.disableRttResponder called"); 494 transitionTo(mEnabledState); 495 } 496 return HANDLED; 497 case RttManager.CMD_OP_START_RANGING: 498 case RttManager.CMD_OP_STOP_RANGING: // fall through 499 // Concurrent initiator and responder role is not supported. 500 replyFailed(msg, 501 RttManager.REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON, 502 "Initiator not allowed when responder is turned on"); 503 return HANDLED; 504 default: 505 return NOT_HANDLED; 506 } 507 } 508 } 509 } 510 511 void replySucceeded(Message msg, Object obj) { 512 if (msg.replyTo != null) { 513 Message reply = Message.obtain(); 514 reply.what = RttManager.CMD_OP_SUCCEEDED; 515 reply.arg2 = msg.arg2; 516 reply.obj = obj; 517 try { 518 msg.replyTo.send(reply); 519 } catch (RemoteException e) { 520 // There's not much we can do if reply can't be sent! 521 } 522 } else { 523 // locally generated message; doesn't need a reply! 524 } 525 } 526 527 void replyFailed(Message msg, int reason, String description) { 528 Message reply = Message.obtain(); 529 reply.what = RttManager.CMD_OP_FAILED; 530 reply.arg1 = reason; 531 reply.arg2 = msg.arg2; 532 533 Bundle bundle = new Bundle(); 534 bundle.putString(RttManager.DESCRIPTION_KEY, description); 535 reply.obj = bundle; 536 537 try { 538 msg.replyTo.send(reply); 539 } catch (RemoteException e) { 540 // There's not much we can do if reply can't be sent! 541 } 542 } 543 544 boolean enforcePermissionCheck(Message msg) { 545 try { 546 mContext.enforcePermission(Manifest.permission.LOCATION_HARDWARE, 547 -1, msg.sendingUid, "LocationRTT"); 548 } catch (SecurityException e) { 549 Log.e(TAG, "UID: " + msg.sendingUid + " has no LOCATION_HARDWARE Permission"); 550 replyFailed(msg,RttManager.REASON_PERMISSION_DENIED, "No params"); 551 return false; 552 } 553 return true; 554 } 555 556 private WifiNative.RttEventHandler mEventHandler = new WifiNative.RttEventHandler() { 557 @Override 558 public void onRttResults(RttManager.RttResult[] result) { 559 mStateMachine.sendMessage(CMD_RTT_RESPONSE, result); 560 } 561 }; 562 563 RttRequest issueNextRequest() { 564 RttRequest request = null; 565 while (mRequestQueue.isEmpty() == false) { 566 request = mRequestQueue.remove(); 567 if(request != null) { 568 if (mWifiNative.requestRtt(request.params, mEventHandler)) { 569 if (DBG) Log.d(TAG, "Issued next RTT request with key: " + request.key); 570 return request; 571 } else { 572 Log.e(TAG, "Fail to issue key at native layer"); 573 request.ci.reportFailed(request, 574 RttManager.REASON_UNSPECIFIED, "Failed to start"); 575 } 576 } 577 } 578 579 /* all requests exhausted */ 580 if (DBG) Log.d(TAG, "No more requests left"); 581 return null; 582 } 583 @Override 584 public RttManager.RttCapabilities getRttCapabilities() { 585 return mWifiNative.getRttCapabilities(); 586 } 587 } 588 589 private static final String TAG = "RttService"; 590 RttServiceImpl mImpl; 591 private final HandlerThread mHandlerThread; 592 593 public RttService(Context context) { 594 super(context); 595 mHandlerThread = new HandlerThread("WifiRttService"); 596 mHandlerThread.start(); 597 Log.i(TAG, "Creating " + Context.WIFI_RTT_SERVICE); 598 } 599 600 @Override 601 public void onStart() { 602 mImpl = new RttServiceImpl(getContext(), mHandlerThread.getLooper()); 603 604 Log.i(TAG, "Starting " + Context.WIFI_RTT_SERVICE); 605 publishBinderService(Context.WIFI_RTT_SERVICE, mImpl); 606 } 607 608 @Override 609 public void onBootPhase(int phase) { 610 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 611 Log.i(TAG, "Registering " + Context.WIFI_RTT_SERVICE); 612 if (mImpl == null) { 613 mImpl = new RttServiceImpl(getContext(), mHandlerThread.getLooper()); 614 } 615 mImpl.startService(); 616 } 617 } 618 619 620} 621