1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi.rtt; 18 19import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 20 21import android.app.ActivityManager; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.pm.PackageManager; 27import android.database.ContentObserver; 28import android.hardware.wifi.V1_0.RttResult; 29import android.hardware.wifi.V1_0.RttStatus; 30import android.location.LocationManager; 31import android.net.MacAddress; 32import android.net.wifi.aware.IWifiAwareMacAddressProvider; 33import android.net.wifi.aware.IWifiAwareManager; 34import android.net.wifi.rtt.IRttCallback; 35import android.net.wifi.rtt.IWifiRttManager; 36import android.net.wifi.rtt.RangingRequest; 37import android.net.wifi.rtt.RangingResult; 38import android.net.wifi.rtt.RangingResultCallback; 39import android.net.wifi.rtt.ResponderConfig; 40import android.net.wifi.rtt.WifiRttManager; 41import android.os.Binder; 42import android.os.Handler; 43import android.os.IBinder; 44import android.os.Looper; 45import android.os.PowerManager; 46import android.os.RemoteException; 47import android.os.ResultReceiver; 48import android.os.ShellCallback; 49import android.os.ShellCommand; 50import android.os.UserHandle; 51import android.os.WorkSource; 52import android.os.WorkSource.WorkChain; 53import android.provider.Settings; 54import android.util.Log; 55import android.util.SparseIntArray; 56 57import com.android.internal.util.WakeupMessage; 58import com.android.server.wifi.Clock; 59import com.android.server.wifi.FrameworkFacade; 60import com.android.server.wifi.nano.WifiMetricsProto; 61import com.android.server.wifi.util.NativeUtil; 62import com.android.server.wifi.util.WifiPermissionsUtil; 63 64import java.io.FileDescriptor; 65import java.io.PrintWriter; 66import java.util.ArrayList; 67import java.util.Arrays; 68import java.util.HashMap; 69import java.util.LinkedList; 70import java.util.List; 71import java.util.ListIterator; 72import java.util.Map; 73 74/** 75 * Implementation of the IWifiRttManager AIDL interface and of the RttService state manager. 76 */ 77public class RttServiceImpl extends IWifiRttManager.Stub { 78 private static final String TAG = "RttServiceImpl"; 79 private static final boolean VDBG = false; // STOPSHIP if true 80 private boolean mDbg = false; 81 82 private final Context mContext; 83 private final RttShellCommand mShellCommand; 84 private Clock mClock; 85 private IWifiAwareManager mAwareBinder; 86 private RttNative mRttNative; 87 private RttMetrics mRttMetrics; 88 private WifiPermissionsUtil mWifiPermissionsUtil; 89 private ActivityManager mActivityManager; 90 private PowerManager mPowerManager; 91 private LocationManager mLocationManager; 92 private FrameworkFacade mFrameworkFacade; 93 private long mBackgroundProcessExecGapMs; 94 95 private RttServiceSynchronized mRttServiceSynchronized; 96 97 private static final int CONVERSION_US_TO_MS = 1_000; 98 99 /* package */ static final String HAL_RANGING_TIMEOUT_TAG = TAG + " HAL Ranging Timeout"; 100 101 private static final long HAL_RANGING_TIMEOUT_MS = 5_000; // 5 sec 102 103 // Default value for RTT background throttling interval. 104 private static final long DEFAULT_BACKGROUND_PROCESS_EXEC_GAP_MS = 1_800_000; // 30 min 105 106 // arbitrary, larger than anything reasonable 107 /* package */ static final int MAX_QUEUED_PER_UID = 20; 108 109 public RttServiceImpl(Context context) { 110 mContext = context; 111 mShellCommand = new RttShellCommand(); 112 mShellCommand.reset(); 113 } 114 115 /* 116 * Shell command: adb shell cmd wifirtt ... 117 */ 118 119 // If set to 0: normal behavior, if set to 1: do not allow any caller (including system 120 // callers) privileged API access 121 private static final String CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_NAME = 122 "override_assume_no_privilege"; 123 private static final int CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_DEFAULT = 0; 124 125 private class RttShellCommand extends ShellCommand { 126 private Map<String, Integer> mControlParams = new HashMap<>(); 127 128 @Override 129 public int onCommand(String cmd) { 130 final int uid = Binder.getCallingUid(); 131 if (uid != 0) { 132 throw new SecurityException( 133 "Uid " + uid + " does not have access to wifirtt commands"); 134 } 135 136 final PrintWriter pw = getErrPrintWriter(); 137 try { 138 if ("reset".equals(cmd)) { 139 reset(); 140 return 0; 141 } else if ("get".equals(cmd)) { 142 String name = getNextArgRequired(); 143 if (!mControlParams.containsKey(name)) { 144 pw.println("Unknown parameter name -- '" + name + "'"); 145 return -1; 146 } 147 getOutPrintWriter().println(mControlParams.get(name)); 148 return 0; 149 } else if ("set".equals(cmd)) { 150 String name = getNextArgRequired(); 151 String valueStr = getNextArgRequired(); 152 153 if (!mControlParams.containsKey(name)) { 154 pw.println("Unknown parameter name -- '" + name + "'"); 155 return -1; 156 } 157 158 try { 159 mControlParams.put(name, Integer.valueOf(valueStr)); 160 return 0; 161 } catch (NumberFormatException e) { 162 pw.println("Can't convert value to integer -- '" + valueStr + "'"); 163 return -1; 164 } 165 } else { 166 handleDefaultCommands(cmd); 167 } 168 } catch (Exception e) { 169 pw.println("Exception: " + e); 170 } 171 return -1; 172 } 173 174 @Override 175 public void onHelp() { 176 final PrintWriter pw = getOutPrintWriter(); 177 178 pw.println("Wi-Fi RTT (wifirt) commands:"); 179 pw.println(" help"); 180 pw.println(" Print this help text."); 181 pw.println(" reset"); 182 pw.println(" Reset parameters to default values."); 183 pw.println(" get <name>"); 184 pw.println(" Get the value of the control parameter."); 185 pw.println(" set <name> <value>"); 186 pw.println(" Set the value of the control parameter."); 187 pw.println(" Control parameters:"); 188 for (String name : mControlParams.keySet()) { 189 pw.println(" " + name); 190 } 191 pw.println(); 192 } 193 194 public int getControlParam(String name) { 195 if (mControlParams.containsKey(name)) { 196 return mControlParams.get(name); 197 } 198 199 Log.wtf(TAG, "getControlParam for unknown variable: " + name); 200 return 0; 201 } 202 203 public void reset() { 204 mControlParams.put(CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_NAME, 205 CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_DEFAULT); 206 } 207 } 208 209 /* 210 * INITIALIZATION 211 */ 212 213 /** 214 * Initializes the RTT service (usually with objects from an injector). 215 * 216 * @param looper The looper on which to synchronize operations. 217 * @param clock A mockable clock. 218 * @param awareBinder The Wi-Fi Aware service (binder) if supported on the system. 219 * @param rttNative The Native interface to the HAL. 220 * @param rttMetrics The Wi-Fi RTT metrics object. 221 * @param wifiPermissionsUtil Utility for permission checks. 222 * @param frameworkFacade Facade for framework classes, allows mocking. 223 */ 224 public void start(Looper looper, Clock clock, IWifiAwareManager awareBinder, 225 RttNative rttNative, RttMetrics rttMetrics, WifiPermissionsUtil wifiPermissionsUtil, 226 FrameworkFacade frameworkFacade) { 227 mClock = clock; 228 mAwareBinder = awareBinder; 229 mRttNative = rttNative; 230 mRttMetrics = rttMetrics; 231 mWifiPermissionsUtil = wifiPermissionsUtil; 232 mFrameworkFacade = frameworkFacade; 233 mRttServiceSynchronized = new RttServiceSynchronized(looper, rttNative); 234 235 mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 236 mPowerManager = mContext.getSystemService(PowerManager.class); 237 mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 238 IntentFilter intentFilter = new IntentFilter(); 239 intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 240 mContext.registerReceiver(new BroadcastReceiver() { 241 @Override 242 public void onReceive(Context context, Intent intent) { 243 String action = intent.getAction(); 244 if (mDbg) Log.v(TAG, "BroadcastReceiver: action=" + action); 245 246 if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { 247 if (mPowerManager.isDeviceIdleMode()) { 248 disable(); 249 } else { 250 enableIfPossible(); 251 } 252 } 253 } 254 }, intentFilter); 255 256 frameworkFacade.registerContentObserver(mContext, 257 Settings.Global.getUriFor(Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED), true, 258 new ContentObserver(mRttServiceSynchronized.mHandler) { 259 @Override 260 public void onChange(boolean selfChange) { 261 enableVerboseLogging(frameworkFacade.getIntegerSetting(mContext, 262 Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0)); 263 } 264 }); 265 266 enableVerboseLogging(frameworkFacade.getIntegerSetting(mContext, 267 Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0)); 268 269 frameworkFacade.registerContentObserver(mContext, 270 Settings.Global.getUriFor(Settings.Global.WIFI_RTT_BACKGROUND_EXEC_GAP_MS), 271 true, 272 new ContentObserver(mRttServiceSynchronized.mHandler) { 273 @Override 274 public void onChange(boolean selfChange) { 275 updateBackgroundThrottlingInterval(frameworkFacade); 276 } 277 }); 278 279 updateBackgroundThrottlingInterval(frameworkFacade); 280 281 intentFilter = new IntentFilter(); 282 intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION); 283 mContext.registerReceiver(new BroadcastReceiver() { 284 @Override 285 public void onReceive(Context context, Intent intent) { 286 if (mDbg) Log.v(TAG, "onReceive: MODE_CHANGED_ACTION: intent=" + intent); 287 if (mLocationManager.isLocationEnabled()) { 288 enableIfPossible(); 289 } else { 290 disable(); 291 } 292 } 293 }, intentFilter); 294 295 mRttServiceSynchronized.mHandler.post(() -> { 296 rttNative.start(mRttServiceSynchronized.mHandler); 297 }); 298 } 299 300 private void enableVerboseLogging(int verbose) { 301 if (verbose > 0) { 302 mDbg = true; 303 } else { 304 mDbg = false; 305 } 306 if (VDBG) { 307 mDbg = true; // just override 308 } 309 mRttNative.mDbg = mDbg; 310 mRttMetrics.mDbg = mDbg; 311 } 312 313 private void updateBackgroundThrottlingInterval(FrameworkFacade frameworkFacade) { 314 mBackgroundProcessExecGapMs = frameworkFacade.getLongSetting(mContext, 315 Settings.Global.WIFI_RTT_BACKGROUND_EXEC_GAP_MS, 316 DEFAULT_BACKGROUND_PROCESS_EXEC_GAP_MS); 317 } 318 319 /* 320 * ASYNCHRONOUS DOMAIN - can be called from different threads! 321 */ 322 323 /** 324 * Proxy for the final native call of the parent class. Enables mocking of 325 * the function. 326 */ 327 public int getMockableCallingUid() { 328 return getCallingUid(); 329 } 330 331 /** 332 * Enable the API if possible: broadcast notification & start launching any queued requests 333 * 334 * If possible: 335 * - RTT HAL is available 336 * - Not in Idle mode 337 * - Location Mode allows Wi-Fi based locationing 338 */ 339 public void enableIfPossible() { 340 boolean isAvailable = isAvailable(); 341 if (VDBG) Log.v(TAG, "enableIfPossible: isAvailable=" + isAvailable); 342 if (!isAvailable) { 343 return; 344 } 345 sendRttStateChangedBroadcast(true); 346 mRttServiceSynchronized.mHandler.post(() -> { 347 // queue should be empty at this point (but this call allows validation) 348 mRttServiceSynchronized.executeNextRangingRequestIfPossible(false); 349 }); 350 } 351 352 /** 353 * Disable the API: 354 * - Clean-up (fail) pending requests 355 * - Broadcast notification 356 */ 357 public void disable() { 358 if (VDBG) Log.v(TAG, "disable"); 359 sendRttStateChangedBroadcast(false); 360 mRttServiceSynchronized.mHandler.post(() -> { 361 mRttServiceSynchronized.cleanUpOnDisable(); 362 }); 363 } 364 365 @Override 366 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 367 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 368 mShellCommand.exec(this, in, out, err, args, callback, resultReceiver); 369 } 370 371 /** 372 * Binder interface API to indicate whether the API is currently available. This requires an 373 * immediate asynchronous response. 374 */ 375 @Override 376 public boolean isAvailable() { 377 return mRttNative.isReady() && !mPowerManager.isDeviceIdleMode() 378 && mLocationManager.isLocationEnabled(); 379 } 380 381 /** 382 * Binder interface API to start a ranging operation. Called on binder thread, operations needs 383 * to be posted to handler thread. 384 */ 385 @Override 386 public void startRanging(IBinder binder, String callingPackage, WorkSource workSource, 387 RangingRequest request, IRttCallback callback) throws RemoteException { 388 if (VDBG) { 389 Log.v(TAG, "startRanging: binder=" + binder + ", callingPackage=" + callingPackage 390 + ", workSource=" + workSource + ", request=" + request + ", callback=" 391 + callback); 392 } 393 // verify arguments 394 if (binder == null) { 395 throw new IllegalArgumentException("Binder must not be null"); 396 } 397 if (request == null || request.mRttPeers == null || request.mRttPeers.size() == 0) { 398 throw new IllegalArgumentException("Request must not be null or empty"); 399 } 400 for (ResponderConfig responder : request.mRttPeers) { 401 if (responder == null) { 402 throw new IllegalArgumentException("Request must not contain null Responders"); 403 } 404 } 405 if (callback == null) { 406 throw new IllegalArgumentException("Callback must not be null"); 407 } 408 request.enforceValidity(mAwareBinder != null); 409 410 if (!isAvailable()) { 411 try { 412 mRttMetrics.recordOverallStatus( 413 WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE); 414 callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE); 415 } catch (RemoteException e) { 416 Log.e(TAG, "startRanging: disabled, callback failed -- " + e); 417 } 418 return; 419 } 420 421 final int uid = getMockableCallingUid(); 422 423 // permission checks 424 enforceAccessPermission(); 425 enforceChangePermission(); 426 mWifiPermissionsUtil.enforceFineLocationPermission(callingPackage, uid); 427 if (workSource != null) { 428 enforceLocationHardware(); 429 // We only care about UIDs in the incoming worksources and not their associated 430 // tags. Clear names so that other operations involving wakesources become simpler. 431 workSource.clearNames(); 432 } 433 boolean isCalledFromPrivilegedContext = 434 checkLocationHardware() && mShellCommand.getControlParam( 435 CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_NAME) == 0; 436 437 // register for binder death 438 IBinder.DeathRecipient dr = new IBinder.DeathRecipient() { 439 @Override 440 public void binderDied() { 441 if (mDbg) Log.v(TAG, "binderDied: uid=" + uid); 442 binder.unlinkToDeath(this, 0); 443 444 mRttServiceSynchronized.mHandler.post(() -> { 445 mRttServiceSynchronized.cleanUpClientRequests(uid, null); 446 }); 447 } 448 }; 449 450 try { 451 binder.linkToDeath(dr, 0); 452 } catch (RemoteException e) { 453 Log.e(TAG, "Error on linkToDeath - " + e); 454 return; 455 } 456 457 mRttServiceSynchronized.mHandler.post(() -> { 458 WorkSource sourceToUse = workSource; 459 if (workSource == null || workSource.isEmpty()) { 460 sourceToUse = new WorkSource(uid); 461 } 462 mRttServiceSynchronized.queueRangingRequest(uid, sourceToUse, binder, dr, 463 callingPackage, request, callback, isCalledFromPrivilegedContext); 464 }); 465 } 466 467 @Override 468 public void cancelRanging(WorkSource workSource) throws RemoteException { 469 if (VDBG) Log.v(TAG, "cancelRanging: workSource=" + workSource); 470 enforceLocationHardware(); 471 if (workSource != null) { 472 // We only care about UIDs in the incoming worksources and not their associated 473 // tags. Clear names so that other operations involving wakesources become simpler. 474 workSource.clearNames(); 475 } 476 477 if (workSource == null || workSource.isEmpty()) { 478 Log.e(TAG, "cancelRanging: invalid work-source -- " + workSource); 479 return; 480 } 481 482 mRttServiceSynchronized.mHandler.post(() -> { 483 mRttServiceSynchronized.cleanUpClientRequests(0, workSource); 484 }); 485 } 486 487 /** 488 * Called by HAL to report ranging results. Called on HAL thread - needs to post to local 489 * thread. 490 */ 491 public void onRangingResults(int cmdId, List<RttResult> results) { 492 if (VDBG) Log.v(TAG, "onRangingResults: cmdId=" + cmdId); 493 mRttServiceSynchronized.mHandler.post(() -> { 494 mRttServiceSynchronized.onRangingResults(cmdId, results); 495 }); 496 } 497 498 private void enforceAccessPermission() { 499 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG); 500 } 501 502 private void enforceChangePermission() { 503 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG); 504 } 505 506 private void enforceLocationHardware() { 507 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE, 508 TAG); 509 } 510 511 private boolean checkLocationHardware() { 512 return mContext.checkCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE) 513 == PackageManager.PERMISSION_GRANTED; 514 } 515 516 private void sendRttStateChangedBroadcast(boolean enabled) { 517 if (VDBG) Log.v(TAG, "sendRttStateChangedBroadcast: enabled=" + enabled); 518 final Intent intent = new Intent(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED); 519 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 520 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 521 } 522 523 @Override 524 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 525 if (mContext.checkCallingOrSelfPermission( 526 android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { 527 pw.println("Permission Denial: can't dump RttService from pid=" 528 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 529 return; 530 } 531 pw.println("Wi-Fi RTT Service"); 532 mRttServiceSynchronized.dump(fd, pw, args); 533 } 534 535 /* 536 * SYNCHRONIZED DOMAIN 537 */ 538 539 /** 540 * RTT service implementation - synchronized on a single thread. All commands should be posted 541 * to the exposed handler. 542 */ 543 private class RttServiceSynchronized { 544 public Handler mHandler; 545 546 private RttNative mRttNative; 547 private int mNextCommandId = 1000; 548 private Map<Integer, RttRequesterInfo> mRttRequesterInfo = new HashMap<>(); 549 private List<RttRequestInfo> mRttRequestQueue = new LinkedList<>(); 550 private WakeupMessage mRangingTimeoutMessage = null; 551 552 RttServiceSynchronized(Looper looper, RttNative rttNative) { 553 mRttNative = rttNative; 554 555 mHandler = new Handler(looper); 556 mRangingTimeoutMessage = new WakeupMessage(mContext, mHandler, 557 HAL_RANGING_TIMEOUT_TAG, () -> { 558 timeoutRangingRequest(); 559 }); 560 } 561 562 private void cancelRanging(RttRequestInfo rri) { 563 ArrayList<byte[]> macAddresses = new ArrayList<>(); 564 for (ResponderConfig peer : rri.request.mRttPeers) { 565 macAddresses.add(peer.macAddress.toByteArray()); 566 } 567 568 mRttNative.rangeCancel(rri.cmdId, macAddresses); 569 } 570 571 private void cleanUpOnDisable() { 572 if (VDBG) Log.v(TAG, "RttServiceSynchronized.cleanUpOnDisable"); 573 for (RttRequestInfo rri : mRttRequestQueue) { 574 try { 575 if (rri.dispatchedToNative) { 576 // may not be necessary in some cases (e.g. Wi-Fi disable may already clear 577 // up active RTT), but in other cases will be needed (doze disabling RTT 578 // but Wi-Fi still up). Doesn't hurt - worst case will fail. 579 cancelRanging(rri); 580 } 581 mRttMetrics.recordOverallStatus( 582 WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE); 583 rri.callback.onRangingFailure( 584 RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE); 585 } catch (RemoteException e) { 586 Log.e(TAG, "RttServiceSynchronized.startRanging: disabled, callback failed -- " 587 + e); 588 } 589 rri.binder.unlinkToDeath(rri.dr, 0); 590 } 591 mRttRequestQueue.clear(); 592 mRangingTimeoutMessage.cancel(); 593 } 594 595 /** 596 * Remove entries related to the specified client and cancel any dispatched to HAL 597 * requests. Expected to provide either the UID or the WorkSource (the other will be 0 or 598 * null respectively). 599 * 600 * A workSource specification will be cleared from the requested workSource and the request 601 * cancelled only if there are no remaining uids in the work-source. 602 */ 603 private void cleanUpClientRequests(int uid, WorkSource workSource) { 604 if (VDBG) { 605 Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid 606 + ", workSource=" + workSource + ", mRttRequestQueue=" + mRttRequestQueue); 607 } 608 boolean dispatchedRequestAborted = false; 609 ListIterator<RttRequestInfo> it = mRttRequestQueue.listIterator(); 610 while (it.hasNext()) { 611 RttRequestInfo rri = it.next(); 612 613 boolean match = rri.uid == uid; // original UID will never be 0 614 if (rri.workSource != null && workSource != null) { 615 rri.workSource.remove(workSource); 616 if (rri.workSource.isEmpty()) { 617 match = true; 618 } 619 } 620 621 if (match) { 622 if (!rri.dispatchedToNative) { 623 it.remove(); 624 rri.binder.unlinkToDeath(rri.dr, 0); 625 } else { 626 dispatchedRequestAborted = true; 627 Log.d(TAG, "Client death - cancelling RTT operation in progress: cmdId=" 628 + rri.cmdId); 629 mRangingTimeoutMessage.cancel(); 630 cancelRanging(rri); 631 } 632 } 633 } 634 635 if (VDBG) { 636 Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid 637 + ", dispatchedRequestAborted=" + dispatchedRequestAborted 638 + ", after cleanup - mRttRequestQueue=" + mRttRequestQueue); 639 } 640 641 if (dispatchedRequestAborted) { 642 executeNextRangingRequestIfPossible(true); 643 } 644 } 645 646 private void timeoutRangingRequest() { 647 if (VDBG) { 648 Log.v(TAG, "RttServiceSynchronized.timeoutRangingRequest mRttRequestQueue=" 649 + mRttRequestQueue); 650 } 651 if (mRttRequestQueue.size() == 0) { 652 Log.w(TAG, "RttServiceSynchronized.timeoutRangingRequest: but nothing in queue!?"); 653 return; 654 } 655 RttRequestInfo rri = mRttRequestQueue.get(0); 656 if (!rri.dispatchedToNative) { 657 Log.w(TAG, "RttServiceSynchronized.timeoutRangingRequest: command not dispatched " 658 + "to native!?"); 659 return; 660 } 661 cancelRanging(rri); 662 try { 663 mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_TIMEOUT); 664 rri.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL); 665 } catch (RemoteException e) { 666 Log.e(TAG, "RttServiceSynchronized.timeoutRangingRequest: callback failed: " + e); 667 } 668 executeNextRangingRequestIfPossible(true); 669 } 670 671 private void queueRangingRequest(int uid, WorkSource workSource, IBinder binder, 672 IBinder.DeathRecipient dr, String callingPackage, RangingRequest request, 673 IRttCallback callback, boolean isCalledFromPrivilegedContext) { 674 mRttMetrics.recordRequest(workSource, request); 675 676 if (isRequestorSpamming(workSource)) { 677 Log.w(TAG, 678 "Work source " + workSource + " is spamming, dropping request: " + request); 679 binder.unlinkToDeath(dr, 0); 680 try { 681 mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_THROTTLE); 682 callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL); 683 } catch (RemoteException e) { 684 Log.e(TAG, "RttServiceSynchronized.queueRangingRequest: spamming, callback " 685 + "failed -- " + e); 686 } 687 return; 688 } 689 690 RttRequestInfo newRequest = new RttRequestInfo(); 691 newRequest.uid = uid; 692 newRequest.workSource = workSource; 693 newRequest.binder = binder; 694 newRequest.dr = dr; 695 newRequest.callingPackage = callingPackage; 696 newRequest.request = request; 697 newRequest.callback = callback; 698 newRequest.isCalledFromPrivilegedContext = isCalledFromPrivilegedContext; 699 mRttRequestQueue.add(newRequest); 700 701 if (VDBG) { 702 Log.v(TAG, "RttServiceSynchronized.queueRangingRequest: newRequest=" + newRequest); 703 } 704 705 executeNextRangingRequestIfPossible(false); 706 } 707 708 private boolean isRequestorSpamming(WorkSource ws) { 709 if (VDBG) Log.v(TAG, "isRequestorSpamming: ws" + ws); 710 711 SparseIntArray counts = new SparseIntArray(); 712 713 for (RttRequestInfo rri : mRttRequestQueue) { 714 for (int i = 0; i < rri.workSource.size(); ++i) { 715 int uid = rri.workSource.get(i); 716 counts.put(uid, counts.get(uid) + 1); 717 } 718 719 final ArrayList<WorkChain> workChains = rri.workSource.getWorkChains(); 720 if (workChains != null) { 721 for (int i = 0; i < workChains.size(); ++i) { 722 final int uid = workChains.get(i).getAttributionUid(); 723 counts.put(uid, counts.get(uid) + 1); 724 } 725 } 726 } 727 728 for (int i = 0; i < ws.size(); ++i) { 729 if (counts.get(ws.get(i)) < MAX_QUEUED_PER_UID) { 730 return false; 731 } 732 } 733 734 final ArrayList<WorkChain> workChains = ws.getWorkChains(); 735 if (workChains != null) { 736 for (int i = 0; i < workChains.size(); ++i) { 737 final int uid = workChains.get(i).getAttributionUid(); 738 if (counts.get(uid) < MAX_QUEUED_PER_UID) { 739 return false; 740 } 741 } 742 } 743 744 if (mDbg) { 745 Log.v(TAG, "isRequestorSpamming: ws=" + ws + ", someone is spamming: " + counts); 746 } 747 return true; 748 } 749 750 private void executeNextRangingRequestIfPossible(boolean popFirst) { 751 if (VDBG) Log.v(TAG, "executeNextRangingRequestIfPossible: popFirst=" + popFirst); 752 753 if (popFirst) { 754 if (mRttRequestQueue.size() == 0) { 755 Log.w(TAG, "executeNextRangingRequestIfPossible: pop requested - but empty " 756 + "queue!? Ignoring pop."); 757 } else { 758 RttRequestInfo topOfQueueRequest = mRttRequestQueue.remove(0); 759 topOfQueueRequest.binder.unlinkToDeath(topOfQueueRequest.dr, 0); 760 } 761 } 762 763 if (mRttRequestQueue.size() == 0) { 764 if (VDBG) Log.v(TAG, "executeNextRangingRequestIfPossible: no requests pending"); 765 return; 766 } 767 768 // if top of list is in progress then do nothing 769 RttRequestInfo nextRequest = mRttRequestQueue.get(0); 770 if (nextRequest.peerHandlesTranslated || nextRequest.dispatchedToNative) { 771 if (VDBG) { 772 Log.v(TAG, "executeNextRangingRequestIfPossible: called but a command is " 773 + "executing. topOfQueue=" + nextRequest); 774 } 775 return; 776 } 777 778 startRanging(nextRequest); 779 } 780 781 private void startRanging(RttRequestInfo nextRequest) { 782 if (VDBG) { 783 Log.v(TAG, "RttServiceSynchronized.startRanging: nextRequest=" + nextRequest); 784 } 785 786 if (!isAvailable()) { 787 Log.d(TAG, "RttServiceSynchronized.startRanging: disabled"); 788 try { 789 mRttMetrics.recordOverallStatus( 790 WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE); 791 nextRequest.callback.onRangingFailure( 792 RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE); 793 } catch (RemoteException e) { 794 Log.e(TAG, "RttServiceSynchronized.startRanging: disabled, callback failed -- " 795 + e); 796 executeNextRangingRequestIfPossible(true); 797 return; 798 } 799 } 800 801 if (processAwarePeerHandles(nextRequest)) { 802 if (VDBG) { 803 Log.v(TAG, "RttServiceSynchronized.startRanging: deferring due to PeerHandle " 804 + "Aware requests"); 805 } 806 return; 807 } 808 809 if (!preExecThrottleCheck(nextRequest.workSource)) { 810 Log.w(TAG, "RttServiceSynchronized.startRanging: execution throttled - nextRequest=" 811 + nextRequest + ", mRttRequesterInfo=" + mRttRequesterInfo); 812 try { 813 mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_THROTTLE); 814 nextRequest.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL); 815 } catch (RemoteException e) { 816 Log.e(TAG, "RttServiceSynchronized.startRanging: throttled, callback failed -- " 817 + e); 818 } 819 executeNextRangingRequestIfPossible(true); 820 return; 821 } 822 823 nextRequest.cmdId = mNextCommandId++; 824 if (mRttNative.rangeRequest(nextRequest.cmdId, nextRequest.request, 825 nextRequest.isCalledFromPrivilegedContext)) { 826 mRangingTimeoutMessage.schedule( 827 mClock.getElapsedSinceBootMillis() + HAL_RANGING_TIMEOUT_MS); 828 } else { 829 Log.w(TAG, "RttServiceSynchronized.startRanging: native rangeRequest call failed"); 830 try { 831 mRttMetrics.recordOverallStatus( 832 WifiMetricsProto.WifiRttLog.OVERALL_HAL_FAILURE); 833 nextRequest.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL); 834 } catch (RemoteException e) { 835 Log.e(TAG, "RttServiceSynchronized.startRanging: HAL request failed, callback " 836 + "failed -- " + e); 837 } 838 executeNextRangingRequestIfPossible(true); 839 } 840 nextRequest.dispatchedToNative = true; 841 } 842 843 /** 844 * Perform pre-execution throttling checks: 845 * - If all uids in ws are in background then check last execution and block if request is 846 * more frequent than permitted 847 * - If executing (i.e. permitted) then update execution time 848 * 849 * Returns true to permit execution, false to abort it. 850 */ 851 private boolean preExecThrottleCheck(WorkSource ws) { 852 if (VDBG) Log.v(TAG, "preExecThrottleCheck: ws=" + ws); 853 854 // are all UIDs running in the background or is at least 1 in the foreground? 855 boolean allUidsInBackground = true; 856 for (int i = 0; i < ws.size(); ++i) { 857 int uidImportance = mActivityManager.getUidImportance(ws.get(i)); 858 if (VDBG) { 859 Log.v(TAG, "preExecThrottleCheck: uid=" + ws.get(i) + " -> importance=" 860 + uidImportance); 861 } 862 if (uidImportance <= IMPORTANCE_FOREGROUND_SERVICE) { 863 allUidsInBackground = false; 864 break; 865 } 866 } 867 868 final ArrayList<WorkChain> workChains = ws.getWorkChains(); 869 if (allUidsInBackground && workChains != null) { 870 for (int i = 0; i < workChains.size(); ++i) { 871 final WorkChain wc = workChains.get(i); 872 int uidImportance = mActivityManager.getUidImportance(wc.getAttributionUid()); 873 if (VDBG) { 874 Log.v(TAG, "preExecThrottleCheck: workChain=" + wc + " -> importance=" 875 + uidImportance); 876 } 877 878 if (uidImportance <= IMPORTANCE_FOREGROUND_SERVICE) { 879 allUidsInBackground = false; 880 break; 881 } 882 } 883 } 884 885 // if all UIDs are in background then check timestamp since last execution and see if 886 // any is permitted (infrequent enough) 887 boolean allowExecution = false; 888 long mostRecentExecutionPermitted = 889 mClock.getElapsedSinceBootMillis() - mBackgroundProcessExecGapMs; 890 if (allUidsInBackground) { 891 for (int i = 0; i < ws.size(); ++i) { 892 RttRequesterInfo info = mRttRequesterInfo.get(ws.get(i)); 893 if (info == null || info.lastRangingExecuted < mostRecentExecutionPermitted) { 894 allowExecution = true; 895 break; 896 } 897 } 898 899 if (workChains != null & !allowExecution) { 900 for (int i = 0; i < workChains.size(); ++i) { 901 final WorkChain wc = workChains.get(i); 902 RttRequesterInfo info = mRttRequesterInfo.get(wc.getAttributionUid()); 903 if (info == null 904 || info.lastRangingExecuted < mostRecentExecutionPermitted) { 905 allowExecution = true; 906 break; 907 } 908 } 909 } 910 } else { 911 allowExecution = true; 912 } 913 914 // update exec time 915 if (allowExecution) { 916 for (int i = 0; i < ws.size(); ++i) { 917 RttRequesterInfo info = mRttRequesterInfo.get(ws.get(i)); 918 if (info == null) { 919 info = new RttRequesterInfo(); 920 mRttRequesterInfo.put(ws.get(i), info); 921 } 922 info.lastRangingExecuted = mClock.getElapsedSinceBootMillis(); 923 } 924 925 if (workChains != null) { 926 for (int i = 0; i < workChains.size(); ++i) { 927 final WorkChain wc = workChains.get(i); 928 RttRequesterInfo info = mRttRequesterInfo.get(wc.getAttributionUid()); 929 if (info == null) { 930 info = new RttRequesterInfo(); 931 mRttRequesterInfo.put(wc.getAttributionUid(), info); 932 } 933 info.lastRangingExecuted = mClock.getElapsedSinceBootMillis(); 934 } 935 } 936 } 937 938 return allowExecution; 939 } 940 941 /** 942 * Check request for any PeerHandle Aware requests. If there are any: issue requests to 943 * translate the peer ID to a MAC address and abort current execution of the range request. 944 * The request will be re-attempted when response is received. 945 * 946 * In cases of failure: pop the current request and execute the next one. Failures: 947 * - Not able to connect to remote service (unlikely) 948 * - Request already processed: but we're missing information 949 * 950 * @return true if need to abort execution, false otherwise. 951 */ 952 private boolean processAwarePeerHandles(RttRequestInfo request) { 953 List<Integer> peerIdsNeedingTranslation = new ArrayList<>(); 954 for (ResponderConfig rttPeer : request.request.mRttPeers) { 955 if (rttPeer.peerHandle != null && rttPeer.macAddress == null) { 956 peerIdsNeedingTranslation.add(rttPeer.peerHandle.peerId); 957 } 958 } 959 960 if (peerIdsNeedingTranslation.size() == 0) { 961 return false; 962 } 963 964 if (request.peerHandlesTranslated) { 965 Log.w(TAG, "processAwarePeerHandles: request=" + request 966 + ": PeerHandles translated - but information still missing!?"); 967 try { 968 mRttMetrics.recordOverallStatus( 969 WifiMetricsProto.WifiRttLog.OVERALL_AWARE_TRANSLATION_FAILURE); 970 request.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL); 971 } catch (RemoteException e) { 972 Log.e(TAG, "processAwarePeerHandles: onRangingResults failure -- " + e); 973 } 974 executeNextRangingRequestIfPossible(true); 975 return true; // an abort because we removed request and are executing next one 976 } 977 978 request.peerHandlesTranslated = true; 979 try { 980 mAwareBinder.requestMacAddresses(request.uid, peerIdsNeedingTranslation, 981 new IWifiAwareMacAddressProvider.Stub() { 982 @Override 983 public void macAddress(Map peerIdToMacMap) { 984 // ASYNC DOMAIN 985 mHandler.post(() -> { 986 // BACK TO SYNC DOMAIN 987 processReceivedAwarePeerMacAddresses(request, peerIdToMacMap); 988 }); 989 } 990 }); 991 } catch (RemoteException e1) { 992 Log.e(TAG, 993 "processAwarePeerHandles: exception while calling requestMacAddresses -- " 994 + e1 + ", aborting request=" + request); 995 try { 996 mRttMetrics.recordOverallStatus( 997 WifiMetricsProto.WifiRttLog.OVERALL_AWARE_TRANSLATION_FAILURE); 998 request.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL); 999 } catch (RemoteException e2) { 1000 Log.e(TAG, "processAwarePeerHandles: onRangingResults failure -- " + e2); 1001 } 1002 executeNextRangingRequestIfPossible(true); 1003 return true; // an abort because we removed request and are executing next one 1004 } 1005 1006 return true; // a deferral 1007 } 1008 1009 private void processReceivedAwarePeerMacAddresses(RttRequestInfo request, 1010 Map<Integer, byte[]> peerIdToMacMap) { 1011 if (VDBG) { 1012 Log.v(TAG, "processReceivedAwarePeerMacAddresses: request=" + request 1013 + ", peerIdToMacMap=" + peerIdToMacMap); 1014 } 1015 1016 RangingRequest.Builder newRequestBuilder = new RangingRequest.Builder(); 1017 for (ResponderConfig rttPeer : request.request.mRttPeers) { 1018 if (rttPeer.peerHandle != null && rttPeer.macAddress == null) { 1019 newRequestBuilder.addResponder(new ResponderConfig( 1020 MacAddress.fromBytes(peerIdToMacMap.get(rttPeer.peerHandle.peerId)), 1021 rttPeer.peerHandle, rttPeer.responderType, rttPeer.supports80211mc, 1022 rttPeer.channelWidth, rttPeer.frequency, rttPeer.centerFreq0, 1023 rttPeer.centerFreq1, rttPeer.preamble)); 1024 } else { 1025 newRequestBuilder.addResponder(rttPeer); 1026 } 1027 } 1028 request.request = newRequestBuilder.build(); 1029 1030 // run request again 1031 startRanging(request); 1032 } 1033 1034 private void onRangingResults(int cmdId, List<RttResult> results) { 1035 if (mRttRequestQueue.size() == 0) { 1036 Log.e(TAG, "RttServiceSynchronized.onRangingResults: no current RTT request " 1037 + "pending!?"); 1038 return; 1039 } 1040 mRangingTimeoutMessage.cancel(); 1041 RttRequestInfo topOfQueueRequest = mRttRequestQueue.get(0); 1042 1043 if (VDBG) { 1044 Log.v(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId 1045 + ", topOfQueueRequest=" + topOfQueueRequest + ", results=" 1046 + Arrays.toString(results.toArray())); 1047 } 1048 1049 if (topOfQueueRequest.cmdId != cmdId) { 1050 Log.e(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId 1051 + ", does not match pending RTT request cmdId=" + topOfQueueRequest.cmdId); 1052 return; 1053 } 1054 1055 boolean permissionGranted = mWifiPermissionsUtil.checkCallersLocationPermission( 1056 topOfQueueRequest.callingPackage, topOfQueueRequest.uid) 1057 && mLocationManager.isLocationEnabled(); 1058 try { 1059 if (permissionGranted) { 1060 List<RangingResult> finalResults = postProcessResults(topOfQueueRequest.request, 1061 results, topOfQueueRequest.isCalledFromPrivilegedContext); 1062 mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_SUCCESS); 1063 mRttMetrics.recordResult(topOfQueueRequest.request, results); 1064 if (VDBG) { 1065 Log.v(TAG, "RttServiceSynchronized.onRangingResults: finalResults=" 1066 + finalResults); 1067 } 1068 topOfQueueRequest.callback.onRangingResults(finalResults); 1069 } else { 1070 Log.w(TAG, "RttServiceSynchronized.onRangingResults: location permission " 1071 + "revoked - not forwarding results"); 1072 mRttMetrics.recordOverallStatus( 1073 WifiMetricsProto.WifiRttLog.OVERALL_LOCATION_PERMISSION_MISSING); 1074 topOfQueueRequest.callback.onRangingFailure( 1075 RangingResultCallback.STATUS_CODE_FAIL); 1076 } 1077 } catch (RemoteException e) { 1078 Log.e(TAG, 1079 "RttServiceSynchronized.onRangingResults: callback exception -- " + e); 1080 } 1081 1082 executeNextRangingRequestIfPossible(true); 1083 } 1084 1085 /* 1086 * Post process the results: 1087 * - For requests without results: add FAILED results 1088 * - For Aware requests using PeerHandle: replace MAC address with PeerHandle 1089 * - Effectively: throws away results which don't match requests 1090 */ 1091 private List<RangingResult> postProcessResults(RangingRequest request, 1092 List<RttResult> results, boolean isCalledFromPrivilegedContext) { 1093 Map<MacAddress, RttResult> resultEntries = new HashMap<>(); 1094 for (RttResult result : results) { 1095 resultEntries.put(MacAddress.fromBytes(result.addr), result); 1096 } 1097 1098 List<RangingResult> finalResults = new ArrayList<>(request.mRttPeers.size()); 1099 1100 for (ResponderConfig peer : request.mRttPeers) { 1101 RttResult resultForRequest = resultEntries.get(peer.macAddress); 1102 if (resultForRequest == null) { 1103 if (mDbg) { 1104 Log.v(TAG, "postProcessResults: missing=" + peer.macAddress); 1105 } 1106 1107 int errorCode = RangingResult.STATUS_FAIL; 1108 if (!isCalledFromPrivilegedContext) { 1109 if (!peer.supports80211mc) { 1110 errorCode = RangingResult.STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC; 1111 } 1112 } 1113 1114 if (peer.peerHandle == null) { 1115 finalResults.add( 1116 new RangingResult(errorCode, peer.macAddress, 0, 0, 0, 0, 0, null, 1117 null, 0)); 1118 } else { 1119 finalResults.add( 1120 new RangingResult(errorCode, peer.peerHandle, 0, 0, 0, 0, 0, null, 1121 null, 0)); 1122 } 1123 } else { 1124 int status = resultForRequest.status == RttStatus.SUCCESS 1125 ? RangingResult.STATUS_SUCCESS : RangingResult.STATUS_FAIL; 1126 byte[] lci = null; 1127 byte[] lcr = null; 1128 if (isCalledFromPrivilegedContext) { 1129 // should not get results if not privileged - but extra check 1130 lci = NativeUtil.byteArrayFromArrayList(resultForRequest.lci.data); 1131 lcr = NativeUtil.byteArrayFromArrayList(resultForRequest.lcr.data); 1132 } 1133 if (resultForRequest.successNumber <= 1 1134 && resultForRequest.distanceSdInMm != 0) { 1135 if (mDbg) { 1136 Log.w(TAG, "postProcessResults: non-zero distance stdev with 0||1 num " 1137 + "samples!? result=" + resultForRequest); 1138 } 1139 resultForRequest.distanceSdInMm = 0; 1140 } 1141 if (peer.peerHandle == null) { 1142 finalResults.add(new RangingResult(status, peer.macAddress, 1143 resultForRequest.distanceInMm, resultForRequest.distanceSdInMm, 1144 resultForRequest.rssi / -2, resultForRequest.numberPerBurstPeer, 1145 resultForRequest.successNumber, lci, lcr, 1146 resultForRequest.timeStampInUs / CONVERSION_US_TO_MS)); 1147 } else { 1148 finalResults.add(new RangingResult(status, peer.peerHandle, 1149 resultForRequest.distanceInMm, resultForRequest.distanceSdInMm, 1150 resultForRequest.rssi / -2, resultForRequest.numberPerBurstPeer, 1151 resultForRequest.successNumber, lci, lcr, 1152 resultForRequest.timeStampInUs / CONVERSION_US_TO_MS)); 1153 } 1154 } 1155 } 1156 1157 return finalResults; 1158 } 1159 1160 // dump call (asynchronous most likely) 1161 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1162 pw.println(" mNextCommandId: " + mNextCommandId); 1163 pw.println(" mRttRequesterInfo: " + mRttRequesterInfo); 1164 pw.println(" mRttRequestQueue: " + mRttRequestQueue); 1165 pw.println(" mRangingTimeoutMessage: " + mRangingTimeoutMessage); 1166 mRttMetrics.dump(fd, pw, args); 1167 mRttNative.dump(fd, pw, args); 1168 } 1169 } 1170 1171 private static class RttRequestInfo { 1172 public int uid; 1173 public WorkSource workSource; 1174 public IBinder binder; 1175 public IBinder.DeathRecipient dr; 1176 public String callingPackage; 1177 public RangingRequest request; 1178 public IRttCallback callback; 1179 public boolean isCalledFromPrivilegedContext; 1180 1181 public int cmdId = 0; // uninitialized cmdId value 1182 public boolean dispatchedToNative = false; 1183 public boolean peerHandlesTranslated = false; 1184 1185 @Override 1186 public String toString() { 1187 return new StringBuilder("RttRequestInfo: uid=").append(uid).append( 1188 ", workSource=").append(workSource).append(", binder=").append(binder).append( 1189 ", dr=").append(dr).append(", callingPackage=").append(callingPackage).append( 1190 ", request=").append(request.toString()).append(", callback=").append( 1191 callback).append(", cmdId=").append(cmdId).append( 1192 ", peerHandlesTranslated=").append(peerHandlesTranslated).append( 1193 ", isCalledFromPrivilegedContext=").append( 1194 isCalledFromPrivilegedContext).toString(); 1195 } 1196 } 1197 1198 private static class RttRequesterInfo { 1199 public long lastRangingExecuted; 1200 1201 @Override 1202 public String toString() { 1203 return new StringBuilder("RttRequesterInfo: lastRangingExecuted=").append( 1204 lastRangingExecuted).toString(); 1205 } 1206 } 1207} 1208