• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.wifi.rtt;
18 
19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
20 import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_LCI;
21 import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_LCR;
22 import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR;
23 import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_ONE_SIDED_RTT;
24 import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_STA_RESPONDER;
25 
26 import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED;
27 
28 import android.annotation.NonNull;
29 import android.app.ActivityManager;
30 import android.content.AttributionSource;
31 import android.content.BroadcastReceiver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.pm.PackageManager;
36 import android.location.LocationManager;
37 import android.net.MacAddress;
38 import android.net.wifi.WifiManager;
39 import android.net.wifi.aware.IWifiAwareMacAddressProvider;
40 import android.net.wifi.aware.MacAddrMapping;
41 import android.net.wifi.aware.WifiAwareManager;
42 import android.net.wifi.rtt.IRttCallback;
43 import android.net.wifi.rtt.IWifiRttManager;
44 import android.net.wifi.rtt.RangingRequest;
45 import android.net.wifi.rtt.RangingResult;
46 import android.net.wifi.rtt.RangingResultCallback;
47 import android.net.wifi.rtt.ResponderConfig;
48 import android.net.wifi.rtt.ResponderLocation;
49 import android.net.wifi.rtt.WifiRttManager;
50 import android.os.Binder;
51 import android.os.Bundle;
52 import android.os.Handler;
53 import android.os.IBinder;
54 import android.os.Looper;
55 import android.os.ParcelFileDescriptor;
56 import android.os.PowerManager;
57 import android.os.RemoteException;
58 import android.os.UserHandle;
59 import android.os.WorkSource;
60 import android.os.WorkSource.WorkChain;
61 import android.text.TextUtils;
62 import android.util.Log;
63 import android.util.SparseIntArray;
64 
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.internal.util.WakeupMessage;
67 import com.android.modules.utils.BasicShellCommandHandler;
68 import com.android.modules.utils.build.SdkLevel;
69 import com.android.server.wifi.BuildProperties;
70 import com.android.server.wifi.Clock;
71 import com.android.server.wifi.FrameworkFacade;
72 import com.android.server.wifi.HalDeviceManager;
73 import com.android.server.wifi.SystemBuildProperties;
74 import com.android.server.wifi.WifiSettingsConfigStore;
75 import com.android.server.wifi.hal.WifiRttController;
76 import com.android.server.wifi.proto.nano.WifiMetricsProto;
77 import com.android.server.wifi.util.WifiPermissionsUtil;
78 import com.android.wifi.resources.R;
79 
80 import org.json.JSONException;
81 import org.json.JSONObject;
82 
83 import java.io.FileDescriptor;
84 import java.io.PrintWriter;
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.HashMap;
88 import java.util.LinkedList;
89 import java.util.List;
90 import java.util.ListIterator;
91 import java.util.Map;
92 
93 /**
94  * Implementation of the IWifiRttManager AIDL interface and of the RttService state manager.
95  */
96 public class RttServiceImpl extends IWifiRttManager.Stub {
97     private static final String TAG = "RttServiceImpl";
98     private static final boolean VDBG = false; // STOPSHIP if true
99     private boolean mVerboseLoggingEnabled = false;
100     private boolean mVerboseHalLoggingEnabled = false;
101 
102     private final Context mContext;
103     private final RttShellCommand mShellCommand;
104     private Clock mClock;
105     private WifiAwareManager mAwareManager;
106     private WifiRttController mWifiRttController;
107     private HalDeviceManager mHalDeviceManager;
108     private RttMetrics mRttMetrics;
109     private WifiPermissionsUtil mWifiPermissionsUtil;
110     private ActivityManager mActivityManager;
111     private PowerManager mPowerManager;
112     private long mLastRequestTimestamp;
113     private final BuildProperties mBuildProperties;
114     private FrameworkFacade mFrameworkFacade;
115     private WifiRttController.Capabilities mCapabilities;
116 
117     private RttServiceSynchronized mRttServiceSynchronized;
118 
119     /* package */ static final String HAL_RANGING_TIMEOUT_TAG = TAG + " HAL Ranging Timeout";
120 
121     @VisibleForTesting
122     public static final long HAL_RANGING_TIMEOUT_MS = 5_000; // 5 sec
123     @VisibleForTesting
124     public static final long HAL_AWARE_RANGING_TIMEOUT_MS = 10_000; // 10 sec
125 
126     // arbitrary, larger than anything reasonable
127     /* package */ static final int MAX_QUEUED_PER_UID = 20;
128 
129     private final WifiRttController.RttControllerRangingResultsCallback mRangingResultsCallback =
130             new WifiRttController.RttControllerRangingResultsCallback() {
131                 @Override
132                 public void onRangingResults(int cmdId, List<RangingResult> rangingResults) {
133                     if (mVerboseLoggingEnabled) Log.d(TAG, "onRangingResults: cmdId=" + cmdId);
134                     mRttServiceSynchronized.mHandler.post(() -> {
135                         mRttServiceSynchronized.onRangingResults(cmdId, rangingResults);
136                     });
137                 }
138             };
139 
140     private final HalDeviceManager.InterfaceRttControllerLifecycleCallback mRttLifecycleCb =
141             new HalDeviceManager.InterfaceRttControllerLifecycleCallback() {
142                 @Override
143                 public void onNewRttController(WifiRttController controller) {
144                     if (mVerboseLoggingEnabled) {
145                         Log.d(TAG, "onNewRttController: controller=" + controller);
146                     }
147                     boolean changed = mWifiRttController == null;
148                     mWifiRttController = controller;
149                     mWifiRttController.registerRangingResultsCallback(mRangingResultsCallback);
150                     if (changed) {
151                         enableIfPossible();
152                     }
153                 }
154 
155                 @Override
156                 public void onRttControllerDestroyed() {
157                     if (mVerboseLoggingEnabled) Log.d(TAG, "onRttControllerDestroyed");
158                     mWifiRttController = null;
159                     disable();
160                 }
161             };
162 
RttServiceImpl(Context context)163     public RttServiceImpl(Context context) {
164         mContext = context;
165         mBuildProperties = new SystemBuildProperties();
166         mFrameworkFacade = new FrameworkFacade();
167         mShellCommand = new RttShellCommand();
168         mShellCommand.reset();
169     }
170 
updateVerboseLoggingEnabled()171     private void updateVerboseLoggingEnabled() {
172         final int verboseAlwaysOnLevel = mContext.getResources().getInteger(
173                 R.integer.config_wifiVerboseLoggingAlwaysOnLevel);
174         mVerboseLoggingEnabled = mFrameworkFacade.isVerboseLoggingAlwaysOn(verboseAlwaysOnLevel,
175                 mBuildProperties) || mVerboseHalLoggingEnabled;
176     }
177 
178     /*
179      * Shell command: adb shell cmd wifirtt ...
180      */
181 
182     // If set to 0: normal behavior, if set to 1: do not allow any caller (including system
183     // callers) privileged API access
184     private static final String CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_NAME =
185             "override_assume_no_privilege";
186     private static final int CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_DEFAULT = 0;
187 
188     private class RttShellCommand extends BasicShellCommandHandler {
189         private Map<String, Integer> mControlParams = new HashMap<>();
190 
191         @Override
onCommand(String cmd)192         public int onCommand(String cmd) {
193             final int uid = Binder.getCallingUid();
194             if (uid != 0) {
195                 throw new SecurityException(
196                         "Uid " + uid + " does not have access to wifirtt commands");
197             }
198 
199             final PrintWriter pw = getErrPrintWriter();
200             try {
201                 if ("reset".equals(cmd)) {
202                     reset();
203                     return 0;
204                 } else if ("get".equals(cmd)) {
205                     String name = getNextArgRequired();
206                     if (!mControlParams.containsKey(name)) {
207                         pw.println("Unknown parameter name -- '" + name + "'");
208                         return -1;
209                     }
210                     getOutPrintWriter().println(mControlParams.get(name));
211                     return 0;
212                 } else if ("set".equals(cmd)) {
213                     String name = getNextArgRequired();
214                     String valueStr = getNextArgRequired();
215 
216                     if (!mControlParams.containsKey(name)) {
217                         pw.println("Unknown parameter name -- '" + name + "'");
218                         return -1;
219                     }
220 
221                     try {
222                         mControlParams.put(name, Integer.valueOf(valueStr));
223                         return 0;
224                     } catch (NumberFormatException e) {
225                         pw.println("Can't convert value to integer -- '" + valueStr + "'");
226                         return -1;
227                     }
228                 } else if ("get_capabilities".equals(cmd)) {
229                     if (mCapabilities == null && mWifiRttController != null) {
230                         mCapabilities = mWifiRttController.getRttCapabilities();
231                     }
232                     JSONObject j = new JSONObject();
233                     if (mCapabilities != null) {
234                         try {
235                             j.put("rttOneSidedSupported", mCapabilities.oneSidedRttSupported);
236                             j.put("rttFtmSupported", mCapabilities.rttFtmSupported);
237                             j.put("lciSupported", mCapabilities.lciSupported);
238                             j.put("lcrSupported", mCapabilities.lcrSupported);
239                             j.put("responderSupported", mCapabilities.responderSupported);
240                             j.put("mcVersion", mCapabilities.mcVersion);
241                             j.put("ntbInitiatorSupported", mCapabilities.ntbInitiatorSupported);
242                             j.put("ntbResponderSupported", mCapabilities.ntbResponderSupported);
243                         } catch (JSONException e) {
244                             Log.e(TAG, "onCommand: get_capabilities e=" + e);
245                         }
246                     }
247                     getOutPrintWriter().println(j.toString());
248                     return 0;
249                 } else {
250                     handleDefaultCommands(cmd);
251                 }
252             } catch (Exception e) {
253                 pw.println("Exception: " + e);
254             }
255             return -1;
256         }
257 
258         @Override
onHelp()259         public void onHelp() {
260             final PrintWriter pw = getOutPrintWriter();
261 
262             pw.println("Wi-Fi RTT (wifirt) commands:");
263             pw.println("  help");
264             pw.println("    Print this help text.");
265             pw.println("  reset");
266             pw.println("    Reset parameters to default values.");
267             pw.println("  get_capabilities: prints out the RTT capabilities as a JSON string");
268             pw.println("  get <name>");
269             pw.println("    Get the value of the control parameter.");
270             pw.println("  set <name> <value>");
271             pw.println("    Set the value of the control parameter.");
272             pw.println("  Control parameters:");
273             for (String name : mControlParams.keySet()) {
274                 pw.println("    " + name);
275             }
276             pw.println();
277         }
278 
getControlParam(String name)279         public int getControlParam(String name) {
280             if (mControlParams.containsKey(name)) {
281                 return mControlParams.get(name);
282             }
283 
284             Log.wtf(TAG, "getControlParam for unknown variable: " + name);
285             return 0;
286         }
287 
reset()288         public void reset() {
289             mControlParams.put(CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_NAME,
290                     CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_DEFAULT);
291         }
292     }
293 
294     /*
295      * INITIALIZATION
296      */
297 
298     /**
299      * Initializes the RTT service (usually with objects from an injector).
300      *
301      * @param looper The looper on which to synchronize operations.
302      * @param clock A mockable clock.
303      * @param awareManager The Wi-Fi Aware service (binder) if supported on the system.
304      * @param rttMetrics The Wi-Fi RTT metrics object.
305      * @param wifiPermissionsUtil Utility for permission checks.
306      * @param settingsConfigStore Used for retrieving verbose logging level.
307      * @param halDeviceManager The HAL device manager object.
308      */
start(Looper looper, Clock clock, WifiAwareManager awareManager, RttMetrics rttMetrics, WifiPermissionsUtil wifiPermissionsUtil, WifiSettingsConfigStore settingsConfigStore, HalDeviceManager halDeviceManager)309     public void start(Looper looper, Clock clock, WifiAwareManager awareManager,
310             RttMetrics rttMetrics, WifiPermissionsUtil wifiPermissionsUtil,
311             WifiSettingsConfigStore settingsConfigStore, HalDeviceManager halDeviceManager) {
312         mClock = clock;
313         mAwareManager = awareManager;
314         mHalDeviceManager = halDeviceManager;
315         mRttMetrics = rttMetrics;
316         mWifiPermissionsUtil = wifiPermissionsUtil;
317         mRttServiceSynchronized = new RttServiceSynchronized(looper);
318         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
319         mPowerManager = mContext.getSystemService(PowerManager.class);
320 
321         mRttServiceSynchronized.mHandler.post(() -> {
322             IntentFilter intentFilter = new IntentFilter();
323             intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
324             mContext.registerReceiver(new BroadcastReceiver() {
325                 @Override
326                 public void onReceive(Context context, Intent intent) {
327                     String action = intent.getAction();
328                     if (mVerboseLoggingEnabled) {
329                         Log.v(TAG, "BroadcastReceiver: action=" + action);
330                     }
331                     if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
332                         if (mPowerManager.isDeviceIdleMode()) {
333                             disable();
334                         } else {
335                             enableIfPossible();
336                         }
337                     }
338                 }
339             }, intentFilter);
340 
341             settingsConfigStore.registerChangeListener(
342                     WIFI_VERBOSE_LOGGING_ENABLED,
343                     (key, newValue) -> enableVerboseLogging(newValue),
344                     mRttServiceSynchronized.mHandler);
345             enableVerboseLogging(settingsConfigStore.get(WIFI_VERBOSE_LOGGING_ENABLED));
346 
347             intentFilter = new IntentFilter();
348             intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
349             mContext.registerReceiver(new BroadcastReceiver() {
350                 @Override
351                 public void onReceive(Context context, Intent intent) {
352                     if (mVerboseLoggingEnabled) {
353                         Log.v(TAG, "onReceive: MODE_CHANGED_ACTION: intent=" + intent);
354                     }
355                     if (mWifiPermissionsUtil.isLocationModeEnabled()) {
356                         enableIfPossible();
357                     } else {
358                         disable();
359                     }
360                 }
361             }, intentFilter);
362 
363             mHalDeviceManager.initialize();
364             mHalDeviceManager.registerStatusListener(() -> {
365                 if (VDBG) Log.d(TAG, "hdm.onStatusChanged");
366                 if (mHalDeviceManager.isStarted()) {
367                     mHalDeviceManager.registerRttControllerLifecycleCallback(mRttLifecycleCb,
368                             mRttServiceSynchronized.mHandler);
369                 }
370             }, mRttServiceSynchronized.mHandler);
371             if (mHalDeviceManager.isStarted()) {
372                 mHalDeviceManager.registerRttControllerLifecycleCallback(
373                         mRttLifecycleCb, mRttServiceSynchronized.mHandler);
374             }
375         });
376     }
377 
enableVerboseLogging(boolean verboseEnabled)378     private void enableVerboseLogging(boolean verboseEnabled) {
379         mVerboseHalLoggingEnabled = verboseEnabled || VDBG;
380         updateVerboseLoggingEnabled();
381         mRttMetrics.enableVerboseLogging(mVerboseLoggingEnabled);
382         if (mWifiRttController != null) {
383             mWifiRttController.enableVerboseLogging(mVerboseLoggingEnabled);
384         }
385     }
386 
387     /**
388      * Handles the transition to boot completed phase
389      */
handleBootCompleted()390     public void handleBootCompleted() {
391         updateVerboseLoggingEnabled();
392     }
393 
394     /*
395      * ASYNCHRONOUS DOMAIN - can be called from different threads!
396      */
397 
398     /**
399      * Proxy for the final native call of the parent class. Enables mocking of
400      * the function.
401      */
getMockableCallingUid()402     public int getMockableCallingUid() {
403         return getCallingUid();
404     }
405 
406     /**
407      * Enable the API if possible: broadcast notification & start launching any queued requests
408      *
409      * If possible:
410      * - RTT HAL is available
411      * - Not in Idle mode
412      * - Location Mode allows Wi-Fi based locationing
413      */
enableIfPossible()414     public void enableIfPossible() {
415         boolean isAvailable = isAvailable();
416         if (VDBG) Log.v(TAG, "enableIfPossible: isAvailable=" + isAvailable);
417         if (!isAvailable) {
418             return;
419         }
420         sendRttStateChangedBroadcast(true);
421         mRttServiceSynchronized.mHandler.post(() -> {
422             // queue should be empty at this point (but this call allows validation)
423             mRttServiceSynchronized.executeNextRangingRequestIfPossible(false);
424         });
425     }
426 
427     /**
428      * Disable the API:
429      * - Clean-up (fail) pending requests
430      * - Broadcast notification
431      */
disable()432     public void disable() {
433         if (VDBG) Log.v(TAG, "disable");
434         sendRttStateChangedBroadcast(false);
435         mRttServiceSynchronized.mHandler.post(() -> {
436             mRttServiceSynchronized.cleanUpOnDisable();
437         });
438     }
439 
440     @Override
handleShellCommand(@onNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)441     public int handleShellCommand(@NonNull ParcelFileDescriptor in,
442             @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
443             @NonNull String[] args) {
444         return mShellCommand.exec(
445                 this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
446                 args);
447     }
448 
449     /**
450      * Binder interface API to indicate whether the API is currently available. This requires an
451      * immediate asynchronous response.
452      */
453     @Override
isAvailable()454     public boolean isAvailable() {
455         long ident = Binder.clearCallingIdentity();
456         try {
457             return mWifiRttController != null && !mPowerManager.isDeviceIdleMode()
458                     && mWifiPermissionsUtil.isLocationModeEnabled();
459         } finally {
460             Binder.restoreCallingIdentity(ident);
461         }
462     }
463 
464     @Override
getRttCharacteristics()465     public Bundle getRttCharacteristics() {
466         enforceAccessPermission();
467         if (mCapabilities == null && mWifiRttController != null) {
468             mCapabilities = mWifiRttController.getRttCapabilities();
469         }
470         return covertCapabilitiesToBundle(mCapabilities);
471     }
472 
covertCapabilitiesToBundle(WifiRttController.Capabilities capabilities)473     private Bundle covertCapabilitiesToBundle(WifiRttController.Capabilities capabilities) {
474         Bundle characteristics = new Bundle();
475         if (capabilities == null) {
476             return characteristics;
477         }
478         characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_ONE_SIDED_RTT,
479                 capabilities.oneSidedRttSupported);
480         characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_LCI, capabilities.lciSupported);
481         characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_LCR, capabilities.lcrSupported);
482         characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_STA_RESPONDER,
483                 capabilities.responderSupported);
484         characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR,
485                 capabilities.ntbInitiatorSupported);
486         return characteristics;
487     }
488 
489     /**
490      * Override IEEE 802.11az parameters with overlay values.
491      */
override11azOverlays(RangingRequest rangingRequest)492     private void override11azOverlays(RangingRequest rangingRequest) {
493         int minNtbTime = mContext.getResources().getInteger(
494                 R.integer.config_wifi80211azMinTimeBetweenNtbMeasurementsMicros);
495         int maxNtbTime = mContext.getResources().getInteger(
496                 R.integer.config_wifi80211azMaxTimeBetweenNtbMeasurementsMicros);
497         if (minNtbTime > 0 || maxNtbTime > 0) {
498             for (ResponderConfig peer : rangingRequest.mRttPeers) {
499                 if (peer.is80211azNtbSupported()) {
500                     if (maxNtbTime > 0) peer.setNtbMaxTimeBetweenMeasurementsMicros(maxNtbTime);
501                     if (minNtbTime > 0) peer.setNtbMinTimeBetweenMeasurementsMicros(minNtbTime);
502                 }
503             }
504         }
505     }
506 
507     /**
508      * Binder interface API to start a ranging operation. Called on binder thread, operations needs
509      * to be posted to handler thread.
510      */
511     @Override
startRanging(IBinder binder, String callingPackage, String callingFeatureId, WorkSource workSource, RangingRequest request, IRttCallback callback, Bundle extras)512     public void startRanging(IBinder binder, String callingPackage, String callingFeatureId,
513             WorkSource workSource, RangingRequest request, IRttCallback callback, Bundle extras)
514             throws RemoteException {
515         if (VDBG) {
516             Log.v(TAG, "startRanging: binder=" + binder + ", callingPackage=" + callingPackage
517                     + ", workSource=" + workSource + ", request=" + request + ", callback="
518                     + callback);
519         }
520         // verify arguments
521         if (binder == null) {
522             throw new IllegalArgumentException("Binder must not be null");
523         }
524         if (request == null || request.mRttPeers == null || request.mRttPeers.size() == 0) {
525             throw new IllegalArgumentException("Request must not be null or empty");
526         }
527         for (ResponderConfig responder : request.mRttPeers) {
528             if (responder == null) {
529                 throw new IllegalArgumentException("Request must not contain null Responders");
530             }
531         }
532         if (callback == null) {
533             throw new IllegalArgumentException("Callback must not be null");
534         }
535         request.enforceValidity(mAwareManager != null);
536 
537         if (!isAvailable()) {
538             try {
539                 mRttMetrics.recordOverallStatus(
540                         WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE);
541                 callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE);
542             } catch (RemoteException e) {
543                 Log.e(TAG, "startRanging: disabled, callback failed -- " + e);
544             }
545             return;
546         }
547 
548         final int uid = getMockableCallingUid();
549 
550         // permission checks
551         enforceAccessPermission();
552         enforceChangePermission();
553         mWifiPermissionsUtil.checkPackage(uid, callingPackage);
554         // check if only Aware APs are ranged.
555         boolean onlyAwareApRanged = request.mRttPeers.stream().allMatch(
556                 config -> config.responderType == ResponderConfig.RESPONDER_AWARE);
557         final Object attributionSource;
558         if (onlyAwareApRanged && SdkLevel.isAtLeastT()) {
559             // Special case: if only aware APs are ranged, then allow this request if the caller
560             // has nearby permission.
561             attributionSource = extras.getParcelable(
562                     WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE);
563             if (!mWifiPermissionsUtil.checkNearbyDevicesPermission(
564                     (AttributionSource) attributionSource, true,
565                     "wifi aware ranging")) {
566                 // No nearby permission. Still check for location permission.
567                 mWifiPermissionsUtil.enforceFineLocationPermission(
568                         callingPackage, callingFeatureId, uid);
569             }
570         } else {
571             attributionSource = null;
572             mWifiPermissionsUtil.enforceFineLocationPermission(
573                     callingPackage, callingFeatureId, uid);
574         }
575 
576         final WorkSource ws;
577         if (workSource != null) {
578             enforceLocationHardware();
579             // We only care about UIDs in the incoming worksources and not their associated
580             // tags. Clear names so that other operations involving wakesources become simpler.
581             ws = workSource.withoutNames();
582         } else {
583             ws = null;
584         }
585 
586         boolean isCalledFromPrivilegedContext =
587                 checkLocationHardware() && mShellCommand.getControlParam(
588                         CONTROL_PARAM_OVERRIDE_ASSUME_NO_PRIVILEGE_NAME) == 0;
589 
590         // register for binder death
591         IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
592             @Override
593             public void binderDied() {
594                 if (mVerboseLoggingEnabled) Log.v(TAG, "binderDied: uid=" + uid);
595                 binder.unlinkToDeath(this, 0);
596 
597                 mRttServiceSynchronized.mHandler.post(() -> {
598                     mRttServiceSynchronized.cleanUpClientRequests(uid, null);
599                 });
600             }
601         };
602 
603         try {
604             binder.linkToDeath(dr, 0);
605         } catch (RemoteException e) {
606             Log.e(TAG, "Error on linkToDeath - " + e);
607             return;
608         }
609 
610         override11azOverlays(request);
611 
612         mRttServiceSynchronized.mHandler.post(() -> {
613             WorkSource sourceToUse = ws;
614             if (ws == null || ws.isEmpty()) {
615                 sourceToUse = new WorkSource(uid);
616             }
617             mRttServiceSynchronized.queueRangingRequest(uid, sourceToUse, binder, dr,
618                     callingPackage, callingFeatureId, request, callback,
619                     isCalledFromPrivilegedContext, attributionSource);
620         });
621     }
622 
623     @Override
cancelRanging(WorkSource workSource)624     public void cancelRanging(WorkSource workSource) throws RemoteException {
625         if (VDBG) Log.v(TAG, "cancelRanging: workSource=" + workSource);
626         enforceLocationHardware();
627         // We only care about UIDs in the incoming worksources and not their associated
628         // tags. Clear names so that other operations involving wakesources become simpler.
629         final WorkSource ws = (workSource != null) ? workSource.withoutNames() : null;
630 
631         if (ws == null || ws.isEmpty()) {
632             Log.e(TAG, "cancelRanging: invalid work-source -- " + ws);
633             return;
634         }
635 
636         mRttServiceSynchronized.mHandler.post(() -> {
637             mRttServiceSynchronized.cleanUpClientRequests(0, ws);
638         });
639     }
640 
enforceAccessPermission()641     private void enforceAccessPermission() {
642         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
643     }
644 
enforceChangePermission()645     private void enforceChangePermission() {
646         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
647     }
648 
enforceLocationHardware()649     private void enforceLocationHardware() {
650         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE,
651                 TAG);
652     }
653 
checkLocationHardware()654     private boolean checkLocationHardware() {
655         return mContext.checkCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE)
656                 == PackageManager.PERMISSION_GRANTED;
657     }
658 
sendRttStateChangedBroadcast(boolean enabled)659     private void sendRttStateChangedBroadcast(boolean enabled) {
660         if (VDBG) Log.v(TAG, "sendRttStateChangedBroadcast: enabled=" + enabled);
661         final Intent intent = new Intent(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
662         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
663         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
664     }
665 
666     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)667     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
668         if (mContext.checkCallingOrSelfPermission(
669                 android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
670             pw.println("Permission Denial: can't dump RttService from pid="
671                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
672             return;
673         }
674         pw.println("Wi-Fi RTT Service");
675         mRttServiceSynchronized.dump(fd, pw, args);
676         pw.println("  mWifiRttController: " + mWifiRttController);
677         if (mWifiRttController != null) {
678             mWifiRttController.dump(pw);
679         }
680     }
681 
682     /*
683      * SYNCHRONIZED DOMAIN
684      */
685 
686     /**
687      * RTT service implementation - synchronized on a single thread. All commands should be posted
688      * to the exposed handler.
689      */
690     private class RttServiceSynchronized {
691         public Handler mHandler;
692 
693         private int mNextCommandId = 1000;
694         private Map<Integer, RttRequesterInfo> mRttRequesterInfo = new HashMap<>();
695         private List<RttRequestInfo> mRttRequestQueue = new LinkedList<>();
696         private WakeupMessage mRangingTimeoutMessage = null;
697 
RttServiceSynchronized(Looper looper)698         RttServiceSynchronized(Looper looper) {
699             mHandler = new Handler(looper);
700             mRangingTimeoutMessage = new WakeupMessage(mContext, mHandler,
701                     HAL_RANGING_TIMEOUT_TAG, () -> {
702                 timeoutRangingRequest();
703             });
704         }
705 
cancelRanging(RttRequestInfo rri)706         private void cancelRanging(RttRequestInfo rri) {
707             ArrayList<MacAddress> macAddresses = new ArrayList<>();
708             for (ResponderConfig peer : rri.request.mRttPeers) {
709                 macAddresses.add(peer.macAddress);
710             }
711 
712             if (mWifiRttController != null) {
713                 mWifiRttController.rangeCancel(rri.cmdId, macAddresses);
714             } else {
715                 Log.e(TAG, "Could not call cancelRanging, rttControllerHal is null");
716             }
717         }
718 
cleanUpOnDisable()719         private void cleanUpOnDisable() {
720             if (VDBG) Log.v(TAG, "RttServiceSynchronized.cleanUpOnDisable");
721             for (RttRequestInfo rri : mRttRequestQueue) {
722                 try {
723                     if (rri.dispatchedToNative) {
724                         // may not be necessary in some cases (e.g. Wi-Fi disable may already clear
725                         // up active RTT), but in other cases will be needed (doze disabling RTT
726                         // but Wi-Fi still up). Doesn't hurt - worst case will fail.
727                         cancelRanging(rri);
728                     }
729                     mRttMetrics.recordOverallStatus(
730                             WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE);
731                     rri.callback.onRangingFailure(
732                             RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE);
733                 } catch (RemoteException e) {
734                     Log.e(TAG, "RttServiceSynchronized.startRanging: disabled, callback failed -- "
735                             + e);
736                 }
737                 rri.binder.unlinkToDeath(rri.dr, 0);
738             }
739             mRttRequestQueue.clear();
740             mRangingTimeoutMessage.cancel();
741         }
742 
743         /**
744          * Remove entries related to the specified client and cancel any dispatched to HAL
745          * requests. Expected to provide either the UID or the WorkSource (the other will be 0 or
746          * null respectively).
747          *
748          * A workSource specification will be cleared from the requested workSource and the request
749          * cancelled only if there are no remaining uids in the work-source.
750          */
cleanUpClientRequests(int uid, WorkSource workSource)751         private void cleanUpClientRequests(int uid, WorkSource workSource) {
752             if (VDBG) {
753                 Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid
754                         + ", workSource=" + workSource + ", mRttRequestQueue=" + mRttRequestQueue);
755             }
756             boolean dispatchedRequestAborted = false;
757             ListIterator<RttRequestInfo> it = mRttRequestQueue.listIterator();
758             while (it.hasNext()) {
759                 RttRequestInfo rri = it.next();
760 
761                 boolean match = rri.uid == uid; // original UID will never be 0
762                 if (rri.workSource != null && workSource != null) {
763                     rri.workSource.remove(workSource);
764                     if (rri.workSource.isEmpty()) {
765                         match = true;
766                     }
767                 }
768 
769                 if (match) {
770                     if (!rri.dispatchedToNative) {
771                         it.remove();
772                         rri.binder.unlinkToDeath(rri.dr, 0);
773                     } else {
774                         dispatchedRequestAborted = true;
775                         Log.d(TAG, "Client death - cancelling RTT operation in progress: cmdId="
776                                 + rri.cmdId);
777                         mRangingTimeoutMessage.cancel();
778                         cancelRanging(rri);
779                     }
780                 }
781             }
782 
783             if (VDBG) {
784                 Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid
785                         + ", dispatchedRequestAborted=" + dispatchedRequestAborted
786                         + ", after cleanup - mRttRequestQueue=" + mRttRequestQueue);
787             }
788 
789             if (dispatchedRequestAborted) {
790                 executeNextRangingRequestIfPossible(true);
791             }
792         }
793 
timeoutRangingRequest()794         private void timeoutRangingRequest() {
795             if (VDBG) {
796                 Log.v(TAG, "RttServiceSynchronized.timeoutRangingRequest mRttRequestQueue="
797                         + mRttRequestQueue);
798             }
799             if (mRttRequestQueue.size() == 0) {
800                 Log.w(TAG, "RttServiceSynchronized.timeoutRangingRequest: but nothing in queue!?");
801                 return;
802             }
803             RttRequestInfo rri = mRttRequestQueue.get(0);
804             if (!rri.dispatchedToNative) {
805                 Log.w(TAG, "RttServiceSynchronized.timeoutRangingRequest: command not dispatched "
806                         + "to native!?");
807                 return;
808             }
809             cancelRanging(rri);
810             try {
811                 mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_TIMEOUT);
812                 rri.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
813             } catch (RemoteException e) {
814                 Log.e(TAG, "RttServiceSynchronized.timeoutRangingRequest: callback failed: " + e);
815             }
816             executeNextRangingRequestIfPossible(true);
817         }
818 
queueRangingRequest(int uid, WorkSource workSource, IBinder binder, IBinder.DeathRecipient dr, String callingPackage, String callingFeatureId, RangingRequest request, IRttCallback callback, boolean isCalledFromPrivilegedContext, Object attributionSource)819         private void queueRangingRequest(int uid, WorkSource workSource, IBinder binder,
820                 IBinder.DeathRecipient dr, String callingPackage, String callingFeatureId,
821                 RangingRequest request, IRttCallback callback,
822                 boolean isCalledFromPrivilegedContext, Object attributionSource) {
823             mRttMetrics.recordRequest(workSource, request);
824 
825             if (isRequestorSpamming(workSource)) {
826                 Log.w(TAG,
827                         "Work source " + workSource + " is spamming, dropping request: " + request);
828                 binder.unlinkToDeath(dr, 0);
829                 try {
830                     mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_THROTTLE);
831                     callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
832                 } catch (RemoteException e) {
833                     Log.e(TAG, "RttServiceSynchronized.queueRangingRequest: spamming, callback "
834                             + "failed -- " + e);
835                 }
836                 return;
837             }
838 
839             RttRequestInfo newRequest = new RttRequestInfo();
840             newRequest.uid = uid;
841             newRequest.workSource = workSource;
842             newRequest.binder = binder;
843             newRequest.dr = dr;
844             newRequest.callingPackage = callingPackage;
845             newRequest.callingFeatureId = callingFeatureId;
846             newRequest.request = request;
847             newRequest.callback = callback;
848             newRequest.isCalledFromPrivilegedContext = isCalledFromPrivilegedContext;
849             newRequest.attributionSource = attributionSource;
850             mRttRequestQueue.add(newRequest);
851 
852             if (VDBG) {
853                 Log.v(TAG, "RttServiceSynchronized.queueRangingRequest: newRequest=" + newRequest);
854             }
855 
856             executeNextRangingRequestIfPossible(false);
857         }
858 
isRequestorSpamming(WorkSource ws)859         private boolean isRequestorSpamming(WorkSource ws) {
860             if (VDBG) Log.v(TAG, "isRequestorSpamming: ws" + ws);
861 
862             SparseIntArray counts = new SparseIntArray();
863 
864             for (RttRequestInfo rri : mRttRequestQueue) {
865                 for (int i = 0; i < rri.workSource.size(); ++i) {
866                     int uid = rri.workSource.getUid(i);
867                     counts.put(uid, counts.get(uid) + 1);
868                 }
869 
870                 final List<WorkChain> workChains = rri.workSource.getWorkChains();
871                 if (workChains != null) {
872                     for (int i = 0; i < workChains.size(); ++i) {
873                         final int uid = workChains.get(i).getAttributionUid();
874                         counts.put(uid, counts.get(uid) + 1);
875                     }
876                 }
877             }
878 
879             for (int i = 0; i < ws.size(); ++i) {
880                 if (counts.get(ws.getUid(i)) < MAX_QUEUED_PER_UID) {
881                     return false;
882                 }
883             }
884 
885             final List<WorkChain> workChains = ws.getWorkChains();
886             if (workChains != null) {
887                 for (int i = 0; i < workChains.size(); ++i) {
888                     final int uid = workChains.get(i).getAttributionUid();
889                     if (counts.get(uid) < MAX_QUEUED_PER_UID) {
890                         return false;
891                     }
892                 }
893             }
894 
895             if (mVerboseLoggingEnabled) {
896                 Log.v(TAG, "isRequestorSpamming: ws=" + ws + ", someone is spamming: " + counts);
897             }
898             return true;
899         }
900 
executeNextRangingRequestIfPossible(boolean popFirst)901         private void executeNextRangingRequestIfPossible(boolean popFirst) {
902             if (VDBG) Log.v(TAG, "executeNextRangingRequestIfPossible: popFirst=" + popFirst);
903 
904             if (popFirst) {
905                 if (mRttRequestQueue.size() == 0) {
906                     Log.w(TAG, "executeNextRangingRequestIfPossible: pop requested - but empty "
907                             + "queue!? Ignoring pop.");
908                 } else {
909                     RttRequestInfo topOfQueueRequest = mRttRequestQueue.remove(0);
910                     topOfQueueRequest.binder.unlinkToDeath(topOfQueueRequest.dr, 0);
911                 }
912             }
913 
914             if (mRttRequestQueue.size() == 0) {
915                 if (VDBG) Log.v(TAG, "executeNextRangingRequestIfPossible: no requests pending");
916                 return;
917             }
918 
919             // if top of list is in progress then do nothing
920             RttRequestInfo nextRequest = mRttRequestQueue.get(0);
921             if (nextRequest.peerHandlesTranslated || nextRequest.dispatchedToNative) {
922                 if (VDBG) {
923                     Log.v(TAG, "executeNextRangingRequestIfPossible: called but a command is "
924                             + "executing. topOfQueue=" + nextRequest);
925                 }
926                 return;
927             }
928 
929             startRanging(nextRequest);
930         }
931 
startRanging(RttRequestInfo nextRequest)932         private void startRanging(RttRequestInfo nextRequest) {
933             if (VDBG) {
934                 Log.v(TAG, "RttServiceSynchronized.startRanging: nextRequest=" + nextRequest);
935             }
936 
937             if (!isAvailable()) {
938                 Log.d(TAG, "RttServiceSynchronized.startRanging: disabled");
939                 try {
940                     mRttMetrics.recordOverallStatus(
941                             WifiMetricsProto.WifiRttLog.OVERALL_RTT_NOT_AVAILABLE);
942                     nextRequest.callback.onRangingFailure(
943                             RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE);
944                 } catch (RemoteException e) {
945                     Log.e(TAG, "RttServiceSynchronized.startRanging: disabled, callback failed -- "
946                             + e);
947                     executeNextRangingRequestIfPossible(true);
948                     return;
949                 }
950             }
951 
952             if (processAwarePeerHandles(nextRequest)) {
953                 if (VDBG) {
954                     Log.v(TAG, "RttServiceSynchronized.startRanging: deferring due to PeerHandle "
955                             + "Aware requests");
956                 }
957                 return;
958             }
959 
960             if (!preExecThrottleCheck(nextRequest.workSource, nextRequest.callingPackage)) {
961                 Log.w(TAG, "RttServiceSynchronized.startRanging: execution throttled - nextRequest="
962                         + nextRequest + ", mRttRequesterInfo=" + mRttRequesterInfo);
963                 try {
964                     mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_THROTTLE);
965                     nextRequest.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
966                 } catch (RemoteException e) {
967                     Log.e(TAG, "RttServiceSynchronized.startRanging: throttled, callback failed -- "
968                             + e);
969                 }
970                 executeNextRangingRequestIfPossible(true);
971                 return;
972             }
973 
974             nextRequest.cmdId = mNextCommandId++;
975             mLastRequestTimestamp = mClock.getWallClockMillis();
976             if (mWifiRttController != null
977                     && mWifiRttController.rangeRequest(nextRequest.cmdId, nextRequest.request)) {
978                 long timeout = HAL_RANGING_TIMEOUT_MS;
979                 for (ResponderConfig responderConfig : nextRequest.request.mRttPeers) {
980                     if (responderConfig.responderType == ResponderConfig.RESPONDER_AWARE) {
981                         timeout = HAL_AWARE_RANGING_TIMEOUT_MS;
982                         break;
983                     }
984                 }
985                 mRangingTimeoutMessage.schedule(mClock.getElapsedSinceBootMillis() + timeout);
986             } else {
987                 Log.w(TAG, "RttServiceSynchronized.startRanging: native rangeRequest call failed");
988                 if (mWifiRttController == null) {
989                     Log.e(TAG, "mWifiRttController is null");
990                 }
991                 try {
992                     mRttMetrics.recordOverallStatus(
993                             WifiMetricsProto.WifiRttLog.OVERALL_HAL_FAILURE);
994                     nextRequest.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
995                 } catch (RemoteException e) {
996                     Log.e(TAG, "RttServiceSynchronized.startRanging: HAL request failed, callback "
997                             + "failed -- " + e);
998                 }
999                 executeNextRangingRequestIfPossible(true);
1000             }
1001             nextRequest.dispatchedToNative = true;
1002         }
1003 
1004         /**
1005          * Perform pre-execution throttling checks:
1006          * - If all uids in ws are in background then check last execution and block if request is
1007          * more frequent than permitted
1008          * - If executing (i.e. permitted) then update execution time
1009          *
1010          * Returns true to permit execution, false to abort it.
1011          */
preExecThrottleCheck(WorkSource ws, String callingPackage)1012         private boolean preExecThrottleCheck(WorkSource ws, String callingPackage) {
1013             if (VDBG) Log.v(TAG, "preExecThrottleCheck: ws=" + ws);
1014 
1015             // are all UIDs running in the background or is at least 1 in the foreground?
1016             boolean allUidsInBackground = true;
1017             for (int i = 0; i < ws.size(); ++i) {
1018                 int uidImportance = mActivityManager.getUidImportance(ws.getUid(i));
1019                 if (VDBG) {
1020                     Log.v(TAG, "preExecThrottleCheck: uid=" + ws.getUid(i) + " -> importance="
1021                             + uidImportance);
1022                 }
1023                 if (uidImportance <= IMPORTANCE_FOREGROUND_SERVICE) {
1024                     allUidsInBackground = false;
1025                     break;
1026                 }
1027             }
1028 
1029             final List<WorkChain> workChains = ws.getWorkChains();
1030             if (allUidsInBackground && workChains != null) {
1031                 for (int i = 0; i < workChains.size(); ++i) {
1032                     final WorkChain wc = workChains.get(i);
1033                     int uidImportance = mActivityManager.getUidImportance(wc.getAttributionUid());
1034                     if (VDBG) {
1035                         Log.v(TAG, "preExecThrottleCheck: workChain=" + wc + " -> importance="
1036                                 + uidImportance);
1037                     }
1038 
1039                     if (uidImportance <= IMPORTANCE_FOREGROUND_SERVICE) {
1040                         allUidsInBackground = false;
1041                         break;
1042                     }
1043                 }
1044             }
1045             if (allUidsInBackground) {
1046                 String[] exceptionList = mContext.getResources().getStringArray(
1047                         R.array.config_wifiBackgroundRttThrottleExceptionList);
1048                 for (String packageName : exceptionList) {
1049                     if (TextUtils.equals(packageName, callingPackage)) {
1050                         allUidsInBackground = false;
1051                         break;
1052                     }
1053                 }
1054             }
1055 
1056             // if all UIDs are in background then check timestamp since last execution and see if
1057             // any is permitted (infrequent enough)
1058             boolean allowExecution = false;
1059             int backgroundProcessExecGapMs = mContext.getResources().getInteger(
1060                     R.integer.config_wifiRttBackgroundExecGapMs);
1061             long mostRecentExecutionPermitted =
1062                     mClock.getElapsedSinceBootMillis() - backgroundProcessExecGapMs;
1063             if (allUidsInBackground) {
1064                 for (int i = 0; i < ws.size(); ++i) {
1065                     RttRequesterInfo info = mRttRequesterInfo.get(ws.getUid(i));
1066                     if (info == null || info.lastRangingExecuted < mostRecentExecutionPermitted) {
1067                         allowExecution = true;
1068                         break;
1069                     }
1070                 }
1071 
1072                 if (workChains != null & !allowExecution) {
1073                     for (int i = 0; i < workChains.size(); ++i) {
1074                         final WorkChain wc = workChains.get(i);
1075                         RttRequesterInfo info = mRttRequesterInfo.get(wc.getAttributionUid());
1076                         if (info == null
1077                                 || info.lastRangingExecuted < mostRecentExecutionPermitted) {
1078                             allowExecution = true;
1079                             break;
1080                         }
1081                     }
1082                 }
1083             } else {
1084                 allowExecution = true;
1085             }
1086 
1087             // update exec time
1088             if (allowExecution) {
1089                 for (int i = 0; i < ws.size(); ++i) {
1090                     RttRequesterInfo info = mRttRequesterInfo.get(ws.getUid(i));
1091                     if (info == null) {
1092                         info = new RttRequesterInfo();
1093                         mRttRequesterInfo.put(ws.getUid(i), info);
1094                     }
1095                     info.lastRangingExecuted = mClock.getElapsedSinceBootMillis();
1096                 }
1097 
1098                 if (workChains != null) {
1099                     for (int i = 0; i < workChains.size(); ++i) {
1100                         final WorkChain wc = workChains.get(i);
1101                         RttRequesterInfo info = mRttRequesterInfo.get(wc.getAttributionUid());
1102                         if (info == null) {
1103                             info = new RttRequesterInfo();
1104                             mRttRequesterInfo.put(wc.getAttributionUid(), info);
1105                         }
1106                         info.lastRangingExecuted = mClock.getElapsedSinceBootMillis();
1107                     }
1108                 }
1109             }
1110 
1111             return allowExecution;
1112         }
1113 
1114         /**
1115          * Check request for any PeerHandle Aware requests. If there are any: issue requests to
1116          * translate the peer ID to a MAC address and abort current execution of the range request.
1117          * The request will be re-attempted when response is received.
1118          *
1119          * In cases of failure: pop the current request and execute the next one. Failures:
1120          * - Not able to connect to remote service (unlikely)
1121          * - Request already processed: but we're missing information
1122          *
1123          * @return true if need to abort execution, false otherwise.
1124          */
processAwarePeerHandles(RttRequestInfo request)1125         private boolean processAwarePeerHandles(RttRequestInfo request) {
1126             List<Integer> peerIdsNeedingTranslation = new ArrayList<>();
1127             for (ResponderConfig rttPeer : request.request.mRttPeers) {
1128                 if (rttPeer.peerHandle != null && rttPeer.macAddress == null) {
1129                     peerIdsNeedingTranslation.add(rttPeer.peerHandle.peerId);
1130                 }
1131             }
1132 
1133             if (peerIdsNeedingTranslation.size() == 0) {
1134                 return false;
1135             }
1136 
1137             if (request.peerHandlesTranslated) {
1138                 Log.w(TAG, "processAwarePeerHandles: request=" + request
1139                         + ": PeerHandles translated - but information still missing!?");
1140                 try {
1141                     mRttMetrics.recordOverallStatus(
1142                             WifiMetricsProto.WifiRttLog.OVERALL_AWARE_TRANSLATION_FAILURE);
1143                     request.callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
1144                 } catch (RemoteException e) {
1145                     Log.e(TAG, "processAwarePeerHandles: onRangingResults failure -- " + e);
1146                 }
1147                 executeNextRangingRequestIfPossible(true);
1148                 return true; // an abort because we removed request and are executing next one
1149             }
1150 
1151             request.peerHandlesTranslated = true;
1152             int[] peerIdsArray = peerIdsNeedingTranslation.stream().mapToInt(i -> i).toArray();
1153             mAwareManager.requestMacAddresses(request.uid, peerIdsArray,
1154                     new IWifiAwareMacAddressProvider.Stub() {
1155                         @Override
1156                         public void macAddress(MacAddrMapping[] peerIdToMacList) {
1157                             // ASYNC DOMAIN
1158                             mHandler.post(() -> {
1159                                 // BACK TO SYNC DOMAIN
1160                                 processReceivedAwarePeerMacAddresses(request, peerIdToMacList);
1161                             });
1162                         }
1163                     });
1164             return true; // a deferral
1165         }
1166 
processReceivedAwarePeerMacAddresses(RttRequestInfo request, MacAddrMapping[] peerIdToMacList)1167         private void processReceivedAwarePeerMacAddresses(RttRequestInfo request,
1168                 MacAddrMapping[] peerIdToMacList) {
1169             if (VDBG) {
1170                 Log.v(TAG, "processReceivedAwarePeerMacAddresses: request=" + request);
1171                 Log.v(TAG, "processReceivedAwarePeerMacAddresses: peerIdToMacList begin");
1172                 for (MacAddrMapping mapping : peerIdToMacList) {
1173                     Log.v(TAG, "    " + mapping.peerId + ": "
1174                             + MacAddress.fromBytes(mapping.macAddress));
1175                 }
1176                 Log.v(TAG, "processReceivedAwarePeerMacAddresses: peerIdToMacList end");
1177             }
1178 
1179             RangingRequest.Builder newRequestBuilder = new RangingRequest.Builder();
1180             for (ResponderConfig rttPeer : request.request.mRttPeers) {
1181                 if (rttPeer.peerHandle != null && rttPeer.macAddress == null) {
1182                     byte[] mac = null;
1183                     for (MacAddrMapping mapping : peerIdToMacList) {
1184                         if (mapping.peerId == rttPeer.peerHandle.peerId) {
1185                             mac = mapping.macAddress;
1186                             break;
1187                         }
1188                     }
1189                     if (mac == null || mac.length != 6) {
1190                         Log.e(TAG, "processReceivedAwarePeerMacAddresses: received an invalid MAC "
1191                                 + "address for peerId=" + rttPeer.peerHandle.peerId);
1192                         continue;
1193                     }
1194                     // To create a ResponderConfig object with both a MAC address and peer
1195                     // handler, we're bypassing the standard Builder pattern and directly using
1196                     // the constructor. This is because the SDK's Builder.build() method has a
1197                     // built-in restriction that prevents setting both properties simultaneously.
1198                     // To avoid triggering this exception, we're directly invoking the
1199                     // constructor to accommodate both values.
1200                     ResponderConfig.Builder responderConfigBuilder = new ResponderConfig.Builder()
1201                             .setMacAddress(MacAddress.fromBytes(mac))
1202                             .setPeerHandle(rttPeer.peerHandle)
1203                             .setResponderType(rttPeer.getResponderType())
1204                             .set80211mcSupported(rttPeer.is80211mcSupported())
1205                             .set80211azNtbSupported(rttPeer.is80211azNtbSupported())
1206                             .setChannelWidth(rttPeer.getChannelWidth())
1207                             .setFrequencyMhz(rttPeer.getFrequencyMhz())
1208                             .setCenterFreq1Mhz(rttPeer.getCenterFreq1Mhz())
1209                             .setCenterFreq0Mhz(rttPeer.getCenterFreq0Mhz())
1210                             .setPreamble(rttPeer.getPreamble());
1211                     newRequestBuilder.addResponder(new ResponderConfig(responderConfigBuilder));
1212                 } else {
1213                     newRequestBuilder.addResponder(rttPeer);
1214                 }
1215             }
1216             newRequestBuilder.setRttBurstSize(request.request.getRttBurstSize());
1217             request.request = newRequestBuilder.build();
1218 
1219             // run request again
1220             startRanging(request);
1221         }
1222 
onRangingResults(int cmdId, List<RangingResult> results)1223         private void onRangingResults(int cmdId, List<RangingResult> results) {
1224             if (mRttRequestQueue.size() == 0) {
1225                 Log.e(TAG, "RttServiceSynchronized.onRangingResults: no current RTT request "
1226                         + "pending!?");
1227                 return;
1228             }
1229             mRangingTimeoutMessage.cancel();
1230             RttRequestInfo topOfQueueRequest = mRttRequestQueue.get(0);
1231 
1232             if (VDBG) {
1233                 Log.v(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId
1234                         + ", topOfQueueRequest=" + topOfQueueRequest + ", results="
1235                         + Arrays.toString(results.toArray()));
1236             }
1237 
1238             if (topOfQueueRequest.cmdId != cmdId) {
1239                 Log.e(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId
1240                         + ", does not match pending RTT request cmdId=" + topOfQueueRequest.cmdId);
1241                 return;
1242             }
1243 
1244             boolean onlyAwareApRanged = topOfQueueRequest.request.mRttPeers.stream().allMatch(
1245                     config -> config.responderType == ResponderConfig.RESPONDER_AWARE);
1246             boolean permissionGranted = false;
1247             if (onlyAwareApRanged && SdkLevel.isAtLeastT()) {
1248                 // Special case: if only aware APs are ranged, then allow this request if the caller
1249                 // has nearby permission.
1250                 permissionGranted = mWifiPermissionsUtil.checkNearbyDevicesPermission(
1251                         (AttributionSource) topOfQueueRequest.attributionSource, true,
1252                         "wifi aware on ranging result");
1253             }
1254             if (!permissionGranted) {
1255                 permissionGranted =
1256                         mWifiPermissionsUtil.checkCallersLocationPermission(
1257                                 topOfQueueRequest.callingPackage,
1258                                 topOfQueueRequest.callingFeatureId,
1259                                 topOfQueueRequest.uid, /* coarseForTargetSdkLessThanQ */ false,
1260                                 null) && mWifiPermissionsUtil.isLocationModeEnabled();
1261             }
1262             try {
1263                 if (permissionGranted) {
1264                     List<RangingResult> finalResults = postProcessResults(topOfQueueRequest.request,
1265                             results, topOfQueueRequest.isCalledFromPrivilegedContext);
1266                     mRttMetrics.recordOverallStatus(WifiMetricsProto.WifiRttLog.OVERALL_SUCCESS);
1267                     mRttMetrics.recordResult(topOfQueueRequest.request, results,
1268                             (int) (mClock.getWallClockMillis() - mLastRequestTimestamp));
1269                     if (VDBG) {
1270                         Log.v(TAG, "RttServiceSynchronized.onRangingResults: finalResults="
1271                                 + finalResults);
1272                     }
1273                     topOfQueueRequest.callback.onRangingResults(finalResults);
1274                 } else {
1275                     Log.w(TAG, "RttServiceSynchronized.onRangingResults: location permission "
1276                             + "revoked - not forwarding results");
1277                     mRttMetrics.recordOverallStatus(
1278                             WifiMetricsProto.WifiRttLog.OVERALL_LOCATION_PERMISSION_MISSING);
1279                     topOfQueueRequest.callback.onRangingFailure(
1280                             RangingResultCallback.STATUS_CODE_FAIL);
1281                 }
1282             } catch (RemoteException e) {
1283                 Log.e(TAG,
1284                         "RttServiceSynchronized.onRangingResults: callback exception -- " + e);
1285             }
1286 
1287             executeNextRangingRequestIfPossible(true);
1288         }
1289 
1290         /*
1291          * Post process the results:
1292          * - For requests without results: add FAILED results
1293          * - For Aware requests using PeerHandle: replace MAC address with PeerHandle
1294          * - Effectively: throws away results which don't match requests
1295          */
postProcessResults(RangingRequest request, List<RangingResult> results, boolean isCalledFromPrivilegedContext)1296         private List<RangingResult> postProcessResults(RangingRequest request,
1297                 List<RangingResult> results, boolean isCalledFromPrivilegedContext) {
1298             Map<MacAddress, RangingResult> resultEntries = new HashMap<>();
1299             for (RangingResult result : results) {
1300                 resultEntries.put(result.getMacAddress(), result);
1301             }
1302 
1303             List<RangingResult> finalResults = new ArrayList<>(request.mRttPeers.size());
1304 
1305             for (ResponderConfig peer : request.mRttPeers) {
1306                 RangingResult resultForRequest = resultEntries.get(peer.macAddress);
1307                 if (resultForRequest == null || resultForRequest.getStatus()
1308                         != WifiRttController.FRAMEWORK_RTT_STATUS_SUCCESS) {
1309                     if (mVerboseLoggingEnabled) {
1310                         Log.v(TAG, "postProcessResults: missing=" + peer.macAddress);
1311                     }
1312                     RangingResult.Builder builder = new RangingResult.Builder()
1313                             .setStatus(RangingResult.STATUS_FAIL);
1314                     if (peer.peerHandle == null) {
1315                         builder.setMacAddress(peer.getMacAddress());
1316                     } else {
1317                         builder.setPeerHandle(peer.peerHandle);
1318                     }
1319                     finalResults.add(builder.build());
1320                 } else {
1321 
1322                     // Clear LCI and LCR data if the location data should not be retransmitted,
1323                     // has a retention expiration time, contains no useful data, or did not parse,
1324                     // or the caller is not in a privileged context.
1325                     byte[] lci = resultForRequest.getLci();
1326                     byte[] lcr = resultForRequest.getLcr();
1327                     ResponderLocation responderLocation =
1328                             resultForRequest.getUnverifiedResponderLocation();
1329                     if (responderLocation == null || !isCalledFromPrivilegedContext) {
1330                         lci = null;
1331                         lcr = null;
1332                     }
1333                     RangingResult.Builder builder = new RangingResult.Builder();
1334                     builder.setStatus(RangingResult.STATUS_SUCCESS)
1335                             .setDistanceMm(resultForRequest.getDistanceMm())
1336                             .setDistanceStdDevMm(resultForRequest.getDistanceStdDevMm())
1337                             .setRssi(resultForRequest.getRssi())
1338                             .setNumAttemptedMeasurements(
1339                                     resultForRequest.getNumAttemptedMeasurements())
1340                             .setNumSuccessfulMeasurements(
1341                                     resultForRequest.getNumSuccessfulMeasurements())
1342                             .setLci(lci)
1343                             .setLcr(lcr)
1344                             .setUnverifiedResponderLocation(responderLocation)
1345                             .setRangingTimestampMillis(resultForRequest.getRangingTimestampMillis())
1346                             .set80211mcMeasurement(resultForRequest.is80211mcMeasurement())
1347                             .setMeasurementChannelFrequencyMHz(
1348                                     resultForRequest.getMeasurementChannelFrequencyMHz())
1349                             .setMeasurementBandwidth(resultForRequest.getMeasurementBandwidth())
1350                             .set80211azNtbMeasurement(resultForRequest.is80211azNtbMeasurement())
1351                             .setMinTimeBetweenNtbMeasurementsMicros(
1352                                     resultForRequest.getMinTimeBetweenNtbMeasurementsMicros())
1353                             .setMaxTimeBetweenNtbMeasurementsMicros(
1354                                     resultForRequest.getMaxTimeBetweenNtbMeasurementsMicros())
1355                             .set80211azInitiatorTxLtfRepetitionsCount(
1356                                     resultForRequest.get80211azInitiatorTxLtfRepetitionsCount())
1357                             .set80211azResponderTxLtfRepetitionsCount(
1358                                     resultForRequest.get80211azResponderTxLtfRepetitionsCount())
1359                             .set80211azNumberOfTxSpatialStreams(
1360                                     resultForRequest.get80211azNumberOfTxSpatialStreams())
1361                             .set80211azNumberOfRxSpatialStreams(
1362                                     resultForRequest.get80211azNumberOfRxSpatialStreams());
1363                     if (peer.peerHandle == null) {
1364                         builder.setMacAddress(peer.getMacAddress());
1365                     } else {
1366                         builder.setPeerHandle(peer.peerHandle);
1367                     }
1368                     if (SdkLevel.isAtLeastV() && resultForRequest.getVendorData() != null
1369                             && !resultForRequest.getVendorData().isEmpty()) {
1370                         builder.setVendorData(resultForRequest.getVendorData());
1371                     }
1372                     finalResults.add(builder.build());
1373                 }
1374             }
1375             return finalResults;
1376         }
1377 
1378         // dump call (asynchronous most likely)
dump(FileDescriptor fd, PrintWriter pw, String[] args)1379         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1380             pw.println("  mNextCommandId: " + mNextCommandId);
1381             pw.println("  mRttRequesterInfo: " + mRttRequesterInfo);
1382             pw.println("  mRttRequestQueue: " + mRttRequestQueue);
1383             pw.println("  mRangingTimeoutMessage: " + mRangingTimeoutMessage);
1384             pw.println("  mWifiRttController: " + mWifiRttController);
1385             pw.println("  mHalDeviceManager: " + mHalDeviceManager);
1386             mRttMetrics.dump(fd, pw, args);
1387         }
1388     }
1389 
1390     private static class RttRequestInfo {
1391         public int uid;
1392         public WorkSource workSource;
1393         public IBinder binder;
1394         public IBinder.DeathRecipient dr;
1395         public String callingPackage;
1396         public String callingFeatureId;
1397         public RangingRequest request;
1398         public IRttCallback callback;
1399         public boolean isCalledFromPrivilegedContext;
1400         // This should be of Class AttributionSource, not is declared as Object for mainline
1401         // backward compatibility.
1402         public Object attributionSource;
1403 
1404         public int cmdId = 0; // uninitialized cmdId value
1405         public boolean dispatchedToNative = false;
1406         public boolean peerHandlesTranslated = false;
1407 
1408         @Override
toString()1409         public String toString() {
1410             return new StringBuilder("RttRequestInfo: uid=").append(uid).append(
1411                     ", workSource=").append(workSource).append(", binder=").append(binder).append(
1412                     ", dr=").append(dr).append(", callingPackage=").append(callingPackage).append(
1413                     ", callingFeatureId=").append(callingFeatureId).append(", request=").append(
1414                     request.toString()).append(", callback=").append(callback).append(
1415                     ", cmdId=").append(cmdId).append(", peerHandlesTranslated=").append(
1416                     peerHandlesTranslated).append(", isCalledFromPrivilegedContext=").append(
1417                     isCalledFromPrivilegedContext).toString();
1418         }
1419     }
1420 
1421     private static class RttRequesterInfo {
1422         public long lastRangingExecuted;
1423 
1424         @Override
toString()1425         public String toString() {
1426             return new StringBuilder("RttRequesterInfo: lastRangingExecuted=").append(
1427                     lastRangingExecuted).toString();
1428         }
1429     }
1430 }
1431