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