• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.bluetooth.le_scan;
18 
19 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
20 import static android.Manifest.permission.BLUETOOTH_SCAN;
21 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
22 import static android.bluetooth.BluetoothUtils.extractBytes;
23 
24 import static com.android.bluetooth.Utils.checkCallerTargetSdk;
25 import static com.android.bluetooth.Utils.checkScanPermissionForDataDelivery;
26 import static com.android.bluetooth.flags.Flags.leaudioBassScanWithInternalScanController;
27 
28 import static java.util.Objects.requireNonNull;
29 
30 import android.annotation.RequiresPermission;
31 import android.annotation.SuppressLint;
32 import android.app.AppOpsManager;
33 import android.app.PendingIntent;
34 import android.bluetooth.BluetoothAdapter;
35 import android.bluetooth.BluetoothDevice;
36 import android.bluetooth.BluetoothUtils;
37 import android.bluetooth.le.BluetoothLeScanner;
38 import android.bluetooth.le.IPeriodicAdvertisingCallback;
39 import android.bluetooth.le.IScannerCallback;
40 import android.bluetooth.le.ScanCallback;
41 import android.bluetooth.le.ScanFilter;
42 import android.bluetooth.le.ScanRecord;
43 import android.bluetooth.le.ScanResult;
44 import android.bluetooth.le.ScanSettings;
45 import android.companion.CompanionDeviceManager;
46 import android.content.AttributionSource;
47 import android.content.Intent;
48 import android.net.MacAddress;
49 import android.os.Binder;
50 import android.os.Build;
51 import android.os.Handler;
52 import android.os.HandlerThread;
53 import android.os.IBinder;
54 import android.os.Looper;
55 import android.os.Message;
56 import android.os.RemoteException;
57 import android.os.SystemClock;
58 import android.os.UserHandle;
59 import android.os.WorkSource;
60 import android.provider.DeviceConfig;
61 import android.text.format.DateUtils;
62 import android.util.Log;
63 
64 import com.android.bluetooth.R;
65 import com.android.bluetooth.Utils;
66 import com.android.bluetooth.btservice.AdapterService;
67 import com.android.bluetooth.btservice.BluetoothAdapterProxy;
68 import com.android.bluetooth.btservice.ProfileService;
69 import com.android.bluetooth.util.NumberUtils;
70 import com.android.internal.annotations.VisibleForTesting;
71 
72 import libcore.util.HexEncoding;
73 
74 import com.google.protobuf.ByteString;
75 
76 import java.util.ArrayList;
77 import java.util.Arrays;
78 import java.util.Collections;
79 import java.util.HashMap;
80 import java.util.HashSet;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.Set;
84 import java.util.UUID;
85 import java.util.concurrent.TimeUnit;
86 import java.util.function.Predicate;
87 import java.util.stream.Collectors;
88 
89 public class ScanController {
90     private static final String TAG = ScanController.class.getSimpleName();
91 
92     /** The default floor value for LE batch scan report delays greater than 0 */
93     static final long DEFAULT_REPORT_DELAY_FLOOR = 5000L;
94 
95     // Batch scan related constants.
96     private static final int TRUNCATED_RESULT_SIZE = 11;
97 
98     // onFoundLost related constants
99     @VisibleForTesting static final int ADVT_STATE_ONFOUND = 0;
100     private static final int ADVT_STATE_ONLOST = 1;
101 
102     private static final int ET_LEGACY_MASK = 0x10;
103 
104     /** Example raw beacons captured from a Blue Charm BC011 */
105     private static final String[] TEST_MODE_BEACONS =
106             new String[] {
107                 "020106",
108                 "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000",
109                 "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000",
110                 "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000",
111                 "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000",
112             };
113 
114     private final PendingIntent.CancelListener mScanIntentCancelListener =
115             new PendingIntent.CancelListener() {
116                 public void onCanceled(PendingIntent intent) {
117                     Log.d(TAG, "scanning PendingIntent canceled");
118                     stopScanInternal(intent);
119                 }
120             };
121 
122     private final Map<Integer, Integer> mFilterIndexToMsftAdvMonitorMap = new HashMap<>();
123     private final Object mTestModeLock = new Object();
124 
125     private final BluetoothAdapter mAdapter;
126     private final AdapterService mAdapterService;
127     private final String mExposureNotificationPackage;
128     private final Predicate<ScanResult> mLocationDenylistPredicate;
129     private final Looper mMainLooper;
130     private final ScanBinder mBinder;
131     private final HandlerThread mScanThread;
132     private final AppOpsManager mAppOps;
133     private final CompanionDeviceManager mCompanionManager;
134     private final ScanManager mScanManager;
135     private final PeriodicScanManager mPeriodicScanManager;
136 
137     private volatile boolean mTestModeEnabled = false;
138     private ScannerMap mScannerMap = new ScannerMap();
139     private Handler mTestModeHandler;
140 
ScanController(AdapterService adapterService)141     public ScanController(AdapterService adapterService) {
142         mAdapter = BluetoothAdapter.getDefaultAdapter();
143         mAdapterService = requireNonNull(adapterService);
144         mExposureNotificationPackage =
145                 mAdapterService.getString(R.string.exposure_notification_package);
146         mLocationDenylistPredicate =
147                 (scanResult) -> {
148                     final MacAddress parsedAddress =
149                             MacAddress.fromString(scanResult.getDevice().getAddress());
150                     if (mAdapterService
151                             .getLocationDenylistMac()
152                             .test(parsedAddress.toByteArray())) {
153                         Log.v(TAG, "Skipping device matching denylist: " + scanResult.getDevice());
154                         return true;
155                     }
156                     final ScanRecord scanRecord = scanResult.getScanRecord();
157                     if (scanRecord.matchesAnyField(
158                             mAdapterService.getLocationDenylistAdvertisingData())) {
159                         Log.v(TAG, "Skipping data matching denylist: " + scanRecord);
160                         return true;
161                     }
162                     return false;
163                 };
164         mMainLooper = adapterService.getMainLooper();
165         mBinder = new ScanBinder(this);
166         mScanThread = new HandlerThread("BluetoothScanManager");
167         mScanThread.start();
168         mAppOps = mAdapterService.getSystemService(AppOpsManager.class);
169         mCompanionManager = mAdapterService.getSystemService(CompanionDeviceManager.class);
170         mScanManager =
171                 ScanObjectsFactory.getInstance()
172                         .createScanManager(
173                                 mAdapterService,
174                                 this,
175                                 BluetoothAdapterProxy.getInstance(),
176                                 mScanThread.getLooper());
177         mPeriodicScanManager = ScanObjectsFactory.getInstance().createPeriodicScanManager();
178     }
179 
cleanup()180     public void cleanup() {
181         Log.i(TAG, "Cleanup ScanController");
182         mBinder.cleanup();
183         mScanThread.quitSafely();
184         mScannerMap.clear();
185         mScanManager.cleanup();
186         mPeriodicScanManager.cleanup();
187     }
188 
getScannerMap()189     ScannerMap getScannerMap() {
190         return mScannerMap;
191     }
192 
193     @VisibleForTesting
setScannerMap(ScannerMap scannerMap)194     void setScannerMap(ScannerMap scannerMap) {
195         mScannerMap = scannerMap;
196     }
197 
198     /** Notify Scan manager of bluetooth profile connection state changes */
notifyProfileConnectionStateChange(int profile, int fromState, int toState)199     public void notifyProfileConnectionStateChange(int profile, int fromState, int toState) {
200         mScanManager.handleBluetoothProfileConnectionStateChanged(profile, fromState, toState);
201     }
202 
getBinder()203     public IBinder getBinder() {
204         return mBinder;
205     }
206 
setTestModeEnabled(boolean enableTestMode)207     public void setTestModeEnabled(boolean enableTestMode) {
208         synchronized (mTestModeLock) {
209             if (mTestModeHandler == null) {
210                 mTestModeHandler =
211                         new Handler(mMainLooper) {
212                             public void handleMessage(Message msg) {
213                                 synchronized (mTestModeLock) {
214                                     if (!mTestModeEnabled) {
215                                         return;
216                                     }
217                                     for (String test : TEST_MODE_BEACONS) {
218                                         onScanResultInternal(
219                                                 0x1b,
220                                                 0x1,
221                                                 "DD:34:02:05:5C:4D",
222                                                 1,
223                                                 0,
224                                                 0xff,
225                                                 127,
226                                                 -54,
227                                                 0x0,
228                                                 HexEncoding.decode(test),
229                                                 "DD:34:02:05:5C:4E");
230                                     }
231                                     sendEmptyMessageDelayed(0, DateUtils.SECOND_IN_MILLIS);
232                                 }
233                             }
234                         };
235             }
236             if (enableTestMode == mTestModeEnabled) {
237                 return;
238             }
239             mTestModeEnabled = enableTestMode;
240             mTestModeHandler.removeMessages(0);
241             mTestModeHandler.sendEmptyMessageDelayed(
242                     0, enableTestMode ? DateUtils.SECOND_IN_MILLIS : 0);
243         }
244     }
245 
PendingIntentInfo( PendingIntent intent, ScanSettings settings, List<ScanFilter> filters, String callingPackage, int callingUid)246     record PendingIntentInfo(
247             PendingIntent intent,
248             ScanSettings settings,
249             List<ScanFilter> filters,
250             String callingPackage,
251             int callingUid) {
252         @Override
253         public boolean equals(Object other) {
254             if (!(other instanceof PendingIntentInfo)) {
255                 return false;
256             }
257             return intent.equals(((PendingIntentInfo) other).intent);
258         }
259 
260         @Override
261         public int hashCode() {
262             return intent == null ? 0 : intent.hashCode();
263         }
264     }
265 
266     /**************************************************************************
267      * Callback functions - CLIENT
268      *************************************************************************/
269 
270     // EN format defined here:
271     // https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
272     private static final byte[] EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE =
273             new byte[] {
274                 // size 2, flag field, flags byte (value is not important)
275                 (byte) 0x02, (byte) 0x01
276             };
277 
278     private static final int EXPOSURE_NOTIFICATION_FLAGS_LENGTH = 0x2 + 1;
279     private static final byte[] EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE =
280             new byte[] {
281                 // size 3, complete 16 bit UUID, EN UUID
282                 (byte) 0x03, (byte) 0x03, (byte) 0x6F, (byte) 0xFD,
283                 // size 23, data for 16 bit UUID, EN UUID
284                 (byte) 0x17, (byte) 0x16, (byte) 0x6F, (byte) 0xFD,
285                 // ...payload
286             };
287     private static final int EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH = 0x03 + 0x17 + 2;
288 
arrayStartsWith(byte[] array, byte[] prefix)289     private static boolean arrayStartsWith(byte[] array, byte[] prefix) {
290         if (array.length < prefix.length) {
291             return false;
292         }
293         for (int i = 0; i < prefix.length; i++) {
294             if (prefix[i] != array[i]) {
295                 return false;
296             }
297         }
298         return true;
299     }
300 
getSanitizedExposureNotification(ScanResult result)301     private static ScanResult getSanitizedExposureNotification(ScanResult result) {
302         ScanRecord record = result.getScanRecord();
303         // Remove the flags part of the payload, if present
304         if (record.getBytes().length > EXPOSURE_NOTIFICATION_FLAGS_LENGTH
305                 && arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE)) {
306             record =
307                     ScanRecord.parseFromBytes(
308                             Arrays.copyOfRange(
309                                     record.getBytes(),
310                                     EXPOSURE_NOTIFICATION_FLAGS_LENGTH,
311                                     record.getBytes().length));
312         }
313 
314         if (record.getBytes().length != EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH) {
315             return null;
316         }
317         if (!arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE)) {
318             return null;
319         }
320 
321         return new ScanResult(null, 0, 0, 0, 0, 0, result.getRssi(), 0, record, 0);
322     }
323 
324     /** Callback method for a scan result. */
onScanResult( int eventType, int addressType, String address, int primaryPhy, int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt, byte[] advData, String originalAddress)325     void onScanResult(
326             int eventType,
327             int addressType,
328             String address,
329             int primaryPhy,
330             int secondaryPhy,
331             int advertisingSid,
332             int txPower,
333             int rssi,
334             int periodicAdvInt,
335             byte[] advData,
336             String originalAddress) {
337         // When in testing mode, ignore all real-world events
338         if (mTestModeEnabled) return;
339 
340         AppScanStats.recordScanRadioResultCount();
341         onScanResultInternal(
342                 eventType,
343                 addressType,
344                 address,
345                 primaryPhy,
346                 secondaryPhy,
347                 advertisingSid,
348                 txPower,
349                 rssi,
350                 periodicAdvInt,
351                 advData,
352                 originalAddress);
353     }
354 
onScanResultInternal( int eventType, int addressType, String address, int primaryPhy, int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt, byte[] advData, String originalAddress)355     private void onScanResultInternal(
356             int eventType,
357             int addressType,
358             String address,
359             int primaryPhy,
360             int secondaryPhy,
361             int advertisingSid,
362             int txPower,
363             int rssi,
364             int periodicAdvInt,
365             byte[] advData,
366             String originalAddress) {
367         Log.v(
368                 TAG,
369                 "onScanResult() - eventType=0x"
370                         + Integer.toHexString(eventType)
371                         + ", addressType="
372                         + addressType
373                         + ", address="
374                         + BluetoothUtils.toAnonymizedAddress(address)
375                         + ", primaryPhy="
376                         + primaryPhy
377                         + ", secondaryPhy="
378                         + secondaryPhy
379                         + ", advertisingSid=0x"
380                         + Integer.toHexString(advertisingSid)
381                         + ", txPower="
382                         + txPower
383                         + ", rssi="
384                         + rssi
385                         + ", periodicAdvInt=0x"
386                         + Integer.toHexString(periodicAdvInt)
387                         + ", originalAddress="
388                         + originalAddress);
389 
390         // Retain the original behavior of returning bluetoothAddress when identityAddress is null
391         String identityAddress = Utils.getBrEdrAddress(address, mAdapterService);
392 
393         if (!address.equals(identityAddress)) {
394             Log.v(
395                     TAG,
396                     "found identityAddress of "
397                             + address
398                             + ", replace originalAddress as "
399                             + identityAddress);
400             originalAddress = identityAddress;
401         }
402 
403         byte[] legacyAdvData = Arrays.copyOfRange(advData, 0, 62);
404 
405         BluetoothDevice device = mAdapter.getRemoteLeDevice(address, addressType);
406 
407         for (ScanClient client : mScanManager.getRegularScanQueue()) {
408             ScannerMap.ScannerApp app = mScannerMap.getById(client.mScannerId);
409             if (app == null) {
410                 Log.v(TAG, "App is null; skip.");
411                 continue;
412             }
413 
414             final ScanSettings settings = client.mSettings;
415             final byte[] scanRecordData;
416             // This is for compatibility with applications that assume fixed size scan data.
417             if (settings.getLegacy()) {
418                 if ((eventType & ET_LEGACY_MASK) == 0) {
419                     // If this is legacy scan, but nonlegacy result - skip.
420                     Log.v(TAG, "Legacy scan, non legacy result; skip.");
421                     continue;
422                 } else {
423                     // Some apps are used to fixed-size advertise data.
424                     scanRecordData = legacyAdvData;
425                 }
426             } else {
427                 scanRecordData = advData;
428             }
429 
430             ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordData);
431             ScanResult result =
432                     new ScanResult(
433                             device,
434                             eventType,
435                             primaryPhy,
436                             secondaryPhy,
437                             advertisingSid,
438                             txPower,
439                             rssi,
440                             periodicAdvInt,
441                             scanRecord,
442                             SystemClock.elapsedRealtimeNanos());
443 
444             if (client.mHasDisavowedLocation) {
445                 if (mLocationDenylistPredicate.test(result)) {
446                     Log.i(TAG, "Skipping client " + client.mScannerId + " for location deny list");
447                     continue;
448                 }
449             }
450 
451             boolean hasPermission = hasScanResultPermission(client);
452             if (!hasPermission) {
453                 for (String associatedDevice : client.mAssociatedDevices) {
454                     if (associatedDevice.equalsIgnoreCase(address)) {
455                         hasPermission = true;
456                         break;
457                     }
458                 }
459             }
460             if (!hasPermission && client.mEligibleForSanitizedExposureNotification) {
461                 ScanResult sanitized = getSanitizedExposureNotification(result);
462                 if (sanitized != null) {
463                     hasPermission = true;
464                     result = sanitized;
465                 }
466             }
467             boolean matchResult = matchesFilters(client, result, originalAddress);
468             if (!hasPermission || !matchResult) {
469                 Log.v(
470                         TAG,
471                         "Skipping client: permission=" + hasPermission + " matches=" + matchResult);
472                 continue;
473             }
474 
475             int callbackType = settings.getCallbackType();
476             if (!(callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
477                     || callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH)) {
478                 Log.v(TAG, "Skipping client: Not CALLBACK_TYPE_ALL_MATCHES");
479                 continue;
480             }
481 
482             try {
483                 app.mAppScanStats.addResult(client.mScannerId);
484                 if (app.mCallback != null) {
485                     app.mCallback.onScanResult(result);
486                 } else {
487                     Log.v(TAG, "Callback is null, sending scan results by pendingIntent");
488                     List<ScanResult> results = new ArrayList<>(Arrays.asList(result));
489                     sendResultsByPendingIntent(
490                             app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
491                 }
492             } catch (RemoteException | PendingIntent.CanceledException e) {
493                 Log.e(TAG, "Exception: " + e);
494                 handleDeadScanClient(client);
495             }
496         }
497     }
498 
sendResultByPendingIntent( PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client)499     private void sendResultByPendingIntent(
500             PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client) {
501         List<ScanResult> results = new ArrayList<>(Arrays.asList(result));
502         try {
503             sendResultsByPendingIntent(pii, results, callbackType);
504         } catch (PendingIntent.CanceledException e) {
505             final long token = Binder.clearCallingIdentity();
506             try {
507                 stopScanInternal(client.mScannerId);
508                 unregisterScannerInternal(client.mScannerId);
509             } finally {
510                 Binder.restoreCallingIdentity(token);
511             }
512         }
513     }
514 
sendResultsByPendingIntent( PendingIntentInfo pii, List<ScanResult> results, int callbackType)515     private void sendResultsByPendingIntent(
516             PendingIntentInfo pii, List<ScanResult> results, int callbackType)
517             throws PendingIntent.CanceledException {
518         Intent extrasIntent = new Intent();
519         extrasIntent.putParcelableArrayListExtra(
520                 BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT, new ArrayList<>(results));
521         extrasIntent.putExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, callbackType);
522         pii.intent.send(mAdapterService, 0, extrasIntent);
523     }
524 
sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode)525     private void sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode)
526             throws PendingIntent.CanceledException {
527         Intent extrasIntent = new Intent();
528         extrasIntent.putExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, errorCode);
529         pii.intent.send(mAdapterService, 0, extrasIntent);
530     }
531 
532     /** Callback method for scanner registration. */
onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)533     void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)
534             throws RemoteException {
535         UUID uuid = new UUID(uuidMsb, uuidLsb);
536         Log.d(
537                 TAG,
538                 "onScannerRegistered() - UUID="
539                         + uuid
540                         + ", scannerId="
541                         + scannerId
542                         + ", status="
543                         + status);
544 
545         // First check the callback map
546         ScannerMap.ScannerApp cbApp = mScannerMap.getByUuid(uuid);
547         if (cbApp != null) {
548             if (status == 0) {
549                 cbApp.mId = scannerId;
550                 // If app is callback based, setup a death recipient. App will initiate the start.
551                 // Otherwise, if PendingIntent based, start the scan directly.
552                 if (cbApp.mCallback != null) {
553                     cbApp.linkToDeath(new ScannerDeathRecipient(scannerId, cbApp.mName));
554                 } else {
555                     continuePiStartScan(scannerId, cbApp);
556                 }
557             } else {
558                 mScannerMap.remove(scannerId);
559             }
560             if (cbApp.mCallback != null) {
561                 cbApp.mCallback.onScannerRegistered(status, scannerId);
562             }
563         }
564     }
565 
566     /** Determines if the given scan client has the appropriate permissions to receive callbacks. */
hasScanResultPermission(final ScanClient client)567     private boolean hasScanResultPermission(final ScanClient client) {
568         if (leaudioBassScanWithInternalScanController() && client.mIsInternalClient) {
569             // Bypass permission check for internal clients
570             return true;
571         }
572         if (client.mHasNetworkSettingsPermission
573                 || client.mHasNetworkSetupWizardPermission
574                 || client.mHasScanWithoutLocationPermission) {
575             return true;
576         }
577         if (client.mHasDisavowedLocation) {
578             return true;
579         }
580         return client.mHasLocationPermission
581                 && !Utils.blockedByLocationOff(mAdapterService, client.mUserHandle);
582     }
583 
permittedResults(final ScanClient client, Set<ScanResult> results)584     private List<ScanResult> permittedResults(final ScanClient client, Set<ScanResult> results) {
585         if (hasScanResultPermission(client)) {
586             return new ArrayList<>(results);
587         }
588 
589         List<ScanResult> permittedResults = new ArrayList<>();
590         for (ScanResult scanResult : results) {
591             for (String associatedDevice : client.mAssociatedDevices) {
592                 if (associatedDevice.equalsIgnoreCase(scanResult.getDevice().getAddress())) {
593                     permittedResults.add(scanResult);
594                 }
595             }
596         }
597         return permittedResults;
598     }
599 
600     // Check if a scan record matches a specific filters.
matchesFilters(ScanClient client, ScanResult scanResult)601     private static boolean matchesFilters(ScanClient client, ScanResult scanResult) {
602         return matchesFilters(client, scanResult, null);
603     }
604 
605     // Check if a scan record matches a specific filters or original address
matchesFilters( ScanClient client, ScanResult scanResult, String originalAddress)606     private static boolean matchesFilters(
607             ScanClient client, ScanResult scanResult, String originalAddress) {
608         if (client.mFilters == null || client.mFilters.isEmpty()) {
609             // TODO: Do we really wanna return true here?
610             return true;
611         }
612         for (ScanFilter filter : client.mFilters) {
613             // Need to check the filter matches, and the original address without changing the API
614             if (filter.matches(scanResult)) {
615                 return true;
616             }
617             if (originalAddress != null
618                     && originalAddress.equalsIgnoreCase(filter.getDeviceAddress())) {
619                 return true;
620             }
621         }
622         return false;
623     }
624 
handleDeadScanClient(ScanClient client)625     private void handleDeadScanClient(ScanClient client) {
626         if (client.mAppDied) {
627             Log.w(TAG, "Already dead client " + client.mScannerId);
628             return;
629         }
630         client.mAppDied = true;
631         if (client.mStats != null) {
632             client.mStats.isAppDead = true;
633         }
634         stopScanInternal(client.mScannerId);
635     }
636 
637     /** Callback method for scan filter enablement/disablement. */
onScanFilterEnableDisabled(int action, int status, int clientIf)638     void onScanFilterEnableDisabled(int action, int status, int clientIf) {
639         Log.d(
640                 TAG,
641                 "onScanFilterEnableDisabled() - clientIf="
642                         + clientIf
643                         + ", status="
644                         + status
645                         + ", action="
646                         + action);
647         mScanManager.callbackDone(clientIf, status);
648     }
649 
650     /** Callback method for configuration of scan filter params. */
onScanFilterParamsConfigured(int action, int status, int clientIf, int availableSpace)651     void onScanFilterParamsConfigured(int action, int status, int clientIf, int availableSpace) {
652         Log.d(
653                 TAG,
654                 "onScanFilterParamsConfigured() - clientIf="
655                         + clientIf
656                         + ", status="
657                         + status
658                         + ", action="
659                         + action
660                         + ", availableSpace="
661                         + availableSpace);
662         mScanManager.callbackDone(clientIf, status);
663     }
664 
665     /** Callback method for configuration of scan filter. */
onScanFilterConfig( int action, int status, int clientIf, int filterType, int availableSpace)666     void onScanFilterConfig(
667             int action, int status, int clientIf, int filterType, int availableSpace) {
668         Log.d(
669                 TAG,
670                 "onScanFilterConfig() - clientIf="
671                         + clientIf
672                         + ", action = "
673                         + action
674                         + " status = "
675                         + status
676                         + ", filterType="
677                         + filterType
678                         + ", availableSpace="
679                         + availableSpace);
680 
681         mScanManager.callbackDone(clientIf, status);
682     }
683 
684     /** Callback method for configuration of batch scan storage. */
onBatchScanStorageConfigured(int status, int clientIf)685     void onBatchScanStorageConfigured(int status, int clientIf) {
686         Log.d(TAG, "onBatchScanStorageConfigured() - clientIf=" + clientIf + ", status=" + status);
687         mScanManager.callbackDone(clientIf, status);
688     }
689 
690     /** Callback method for start/stop of batch scan. */
691     // TODO: split into two different callbacks : onBatchScanStarted and onBatchScanStopped.
onBatchScanStartStopped(int startStopAction, int status, int clientIf)692     void onBatchScanStartStopped(int startStopAction, int status, int clientIf) {
693         Log.d(
694                 TAG,
695                 "onBatchScanStartStopped() - clientIf="
696                         + clientIf
697                         + ", status="
698                         + status
699                         + ", startStopAction="
700                         + startStopAction);
701         mScanManager.callbackDone(clientIf, status);
702     }
703 
findBatchScanClientById(int scannerId)704     ScanClient findBatchScanClientById(int scannerId) {
705         for (ScanClient client : mScanManager.getBatchScanQueue()) {
706             if (client.mScannerId == scannerId) {
707                 return client;
708             }
709         }
710         return null;
711     }
712 
713     /** Callback method for batch scan reports */
onBatchScanReports( int status, int scannerId, int reportType, int numRecords, byte[] recordData)714     void onBatchScanReports(
715             int status, int scannerId, int reportType, int numRecords, byte[] recordData)
716             throws RemoteException {
717         // When in testing mode, ignore all real-world events
718         if (mTestModeEnabled) return;
719 
720         AppScanStats.recordBatchScanRadioResultCount(numRecords);
721         onBatchScanReportsInternal(status, scannerId, reportType, numRecords, recordData);
722     }
723 
724     @VisibleForTesting
onBatchScanReportsInternal( int status, int scannerId, int reportType, int numRecords, byte[] recordData)725     void onBatchScanReportsInternal(
726             int status, int scannerId, int reportType, int numRecords, byte[] recordData)
727             throws RemoteException {
728         Log.d(
729                 TAG,
730                 "onBatchScanReports() - scannerId="
731                         + scannerId
732                         + ", status="
733                         + status
734                         + ", reportType="
735                         + reportType
736                         + ", numRecords="
737                         + numRecords);
738 
739         Set<ScanResult> results = parseBatchScanResults(numRecords, reportType, recordData);
740         if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
741             // We only support single client for truncated mode.
742             ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
743             if (app == null) {
744                 return;
745             }
746 
747             ScanClient client = findBatchScanClientById(scannerId);
748             if (client == null) {
749                 return;
750             }
751 
752             List<ScanResult> permittedResults = permittedResults(client, results);
753 
754             if (client.mHasDisavowedLocation) {
755                 permittedResults.removeIf(mLocationDenylistPredicate);
756             }
757             if (permittedResults.isEmpty()) {
758                 mScanManager.callbackDone(scannerId, status);
759                 return;
760             }
761 
762             if (app.mCallback != null) {
763                 app.mCallback.onBatchScanResults(permittedResults);
764                 mScanManager.batchScanResultDelivered();
765             } else {
766                 // PendingIntent based
767                 try {
768                     sendResultsByPendingIntent(
769                             app.mInfo, permittedResults, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
770                 } catch (PendingIntent.CanceledException e) {
771                     Log.d(TAG, "Exception while sending result", e);
772                 }
773             }
774         } else {
775             for (ScanClient client : mScanManager.getFullBatchScanQueue()) {
776                 // Deliver results for each client.
777                 deliverBatchScan(client, results);
778             }
779         }
780         mScanManager.callbackDone(scannerId, status);
781     }
782 
sendBatchScanResults( ScannerMap.ScannerApp app, ScanClient client, List<ScanResult> results)783     private void sendBatchScanResults(
784             ScannerMap.ScannerApp app, ScanClient client, List<ScanResult> results) {
785         if (results.isEmpty()) {
786             return;
787         }
788         try {
789             if (app.mCallback != null) {
790                 if (mScanManager.isAutoBatchScanClientEnabled(client)) {
791                     Log.d(TAG, "sendBatchScanResults() to onScanResult()" + client);
792                     for (ScanResult result : results) {
793                         app.mAppScanStats.addResult(client.mScannerId);
794                         app.mCallback.onScanResult(result);
795                     }
796                 } else {
797                     Log.d(TAG, "sendBatchScanResults() to onBatchScanResults()" + client);
798                     app.mCallback.onBatchScanResults(results);
799                 }
800             } else {
801                 sendResultsByPendingIntent(
802                         app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
803             }
804         } catch (RemoteException | PendingIntent.CanceledException e) {
805             Log.e(TAG, "Exception: " + e);
806             handleDeadScanClient(client);
807         }
808         mScanManager.batchScanResultDelivered();
809     }
810 
811     // Check and deliver scan results for different scan clients.
deliverBatchScan(ScanClient client, Set<ScanResult> allResults)812     private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults)
813             throws RemoteException {
814         ScannerMap.ScannerApp app = mScannerMap.getById(client.mScannerId);
815         if (app == null) {
816             return;
817         }
818 
819         List<ScanResult> permittedResults = permittedResults(client, allResults);
820 
821         if (client.mFilters == null || client.mFilters.isEmpty()) {
822             sendBatchScanResults(app, client, permittedResults);
823             return;
824         }
825         // Reconstruct the scan results.
826         List<ScanResult> results = new ArrayList<>();
827         for (ScanResult scanResult : permittedResults) {
828             if (matchesFilters(client, scanResult)) {
829                 results.add(scanResult);
830             }
831         }
832         sendBatchScanResults(app, client, results);
833     }
834 
parseBatchScanResults( int numRecords, int reportType, byte[] batchRecord)835     private Set<ScanResult> parseBatchScanResults(
836             int numRecords, int reportType, byte[] batchRecord) {
837         if (numRecords == 0) {
838             return Collections.emptySet();
839         }
840         Log.d(TAG, "current time is " + SystemClock.elapsedRealtimeNanos());
841         if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
842             return parseTruncatedResults(numRecords, batchRecord);
843         } else {
844             return parseFullResults(numRecords, batchRecord);
845         }
846     }
847 
parseTruncatedResults(int numRecords, byte[] batchRecord)848     private Set<ScanResult> parseTruncatedResults(int numRecords, byte[] batchRecord) {
849         Log.d(TAG, "batch record " + Arrays.toString(batchRecord));
850         Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
851         long now = SystemClock.elapsedRealtimeNanos();
852         for (int i = 0; i < numRecords; ++i) {
853             byte[] record =
854                     extractBytes(batchRecord, i * TRUNCATED_RESULT_SIZE, TRUNCATED_RESULT_SIZE);
855             byte[] address = extractBytes(record, 0, 6);
856             Utils.reverse(address);
857             BluetoothDevice device = mAdapter.getRemoteDevice(address);
858             int rssi = record[8];
859             long timestampNanos = now - parseTimestampNanos(extractBytes(record, 9, 2));
860             results.add(
861                     new ScanResult(
862                             device, ScanRecord.parseFromBytes(new byte[0]), rssi, timestampNanos));
863         }
864         return results;
865     }
866 
867     @VisibleForTesting
parseTimestampNanos(byte[] data)868     long parseTimestampNanos(byte[] data) {
869         long timestampUnit = NumberUtils.littleEndianByteArrayToInt(data);
870         // Timestamp is in every 50 ms.
871         return TimeUnit.MILLISECONDS.toNanos(timestampUnit * 50);
872     }
873 
parseFullResults(int numRecords, byte[] batchRecord)874     private Set<ScanResult> parseFullResults(int numRecords, byte[] batchRecord) {
875         Log.d(TAG, "Batch record : " + Arrays.toString(batchRecord));
876         Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
877         int position = 0;
878         long now = SystemClock.elapsedRealtimeNanos();
879         while (position < batchRecord.length) {
880             byte[] address = extractBytes(batchRecord, position, 6);
881             // TODO: remove temp hack.
882             Utils.reverse(address);
883             BluetoothDevice device = mAdapter.getRemoteDevice(address);
884             position += 6;
885             // Skip address type.
886             position++;
887             // Skip tx power level.
888             position++;
889             int rssi = batchRecord[position++];
890             long timestampNanos = now - parseTimestampNanos(extractBytes(batchRecord, position, 2));
891             position += 2;
892 
893             // Combine advertise packet and scan response packet.
894             int advertisePacketLen = batchRecord[position++];
895             byte[] advertiseBytes = extractBytes(batchRecord, position, advertisePacketLen);
896             position += advertisePacketLen;
897             int scanResponsePacketLen = batchRecord[position++];
898             byte[] scanResponseBytes = extractBytes(batchRecord, position, scanResponsePacketLen);
899             position += scanResponsePacketLen;
900             byte[] scanRecord = new byte[advertisePacketLen + scanResponsePacketLen];
901             System.arraycopy(advertiseBytes, 0, scanRecord, 0, advertisePacketLen);
902             System.arraycopy(
903                     scanResponseBytes, 0, scanRecord, advertisePacketLen, scanResponsePacketLen);
904             Log.d(TAG, "ScanRecord : " + Arrays.toString(scanRecord));
905             results.add(
906                     new ScanResult(
907                             device, ScanRecord.parseFromBytes(scanRecord), rssi, timestampNanos));
908         }
909         return results;
910     }
911 
onBatchScanThresholdCrossed(int clientIf)912     void onBatchScanThresholdCrossed(int clientIf) {
913         Log.d(TAG, "onBatchScanThresholdCrossed() - clientIf=" + clientIf);
914         flushPendingBatchResultsInternal(clientIf);
915     }
916 
createOnTrackAdvFoundLostObject( int clientIf, int advPacketLen, byte[] advPacket, int scanResponseLen, byte[] scanResponse, int filtIndex, int advState, int advInfoPresent, String address, int addrType, int txPower, int rssiValue, int timeStamp)917     AdvtFilterOnFoundOnLostInfo createOnTrackAdvFoundLostObject(
918             int clientIf,
919             int advPacketLen,
920             byte[] advPacket,
921             int scanResponseLen,
922             byte[] scanResponse,
923             int filtIndex,
924             int advState,
925             int advInfoPresent,
926             String address,
927             int addrType,
928             int txPower,
929             int rssiValue,
930             int timeStamp) {
931         return new AdvtFilterOnFoundOnLostInfo(
932                 clientIf,
933                 advPacketLen,
934                 ByteString.copyFrom(advPacket),
935                 scanResponseLen,
936                 ByteString.copyFrom(scanResponse),
937                 filtIndex,
938                 advState,
939                 advInfoPresent,
940                 address,
941                 addrType,
942                 txPower,
943                 rssiValue,
944                 timeStamp);
945     }
946 
onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo)947     void onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo) throws RemoteException {
948         Log.d(
949                 TAG,
950                 "onTrackAdvFoundLost() - scannerId= "
951                         + trackingInfo.clientIf()
952                         + " address = "
953                         + trackingInfo.address()
954                         + " addressType = "
955                         + trackingInfo.addressType()
956                         + " adv_state = "
957                         + trackingInfo.advState());
958 
959         ScannerMap.ScannerApp app = mScannerMap.getById(trackingInfo.clientIf());
960         if (app == null) {
961             Log.e(TAG, "app is null");
962             return;
963         }
964 
965         BluetoothDevice device =
966                 mAdapter.getRemoteLeDevice(trackingInfo.address(), trackingInfo.addressType());
967         int advertiserState = trackingInfo.advState();
968         ScanResult result =
969                 new ScanResult(
970                         device,
971                         ScanRecord.parseFromBytes(trackingInfo.getResult()),
972                         trackingInfo.rssiValue(),
973                         SystemClock.elapsedRealtimeNanos());
974 
975         for (ScanClient client : mScanManager.getRegularScanQueue()) {
976             if (client.mScannerId == trackingInfo.clientIf()) {
977                 ScanSettings settings = client.mSettings;
978                 if ((advertiserState == ADVT_STATE_ONFOUND)
979                         && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
980                                 != 0)) {
981                     if (app.mCallback != null) {
982                         app.mCallback.onFoundOrLost(true, result);
983                     } else {
984                         sendResultByPendingIntent(
985                                 app.mInfo, result, ScanSettings.CALLBACK_TYPE_FIRST_MATCH, client);
986                     }
987                 } else if ((advertiserState == ADVT_STATE_ONLOST)
988                         && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST)
989                                 != 0)) {
990                     if (app.mCallback != null) {
991                         app.mCallback.onFoundOrLost(false, result);
992                     } else {
993                         sendResultByPendingIntent(
994                                 app.mInfo, result, ScanSettings.CALLBACK_TYPE_MATCH_LOST, client);
995                     }
996                 } else {
997                     Log.d(
998                             TAG,
999                             "Not reporting onlost/onfound : "
1000                                     + advertiserState
1001                                     + " scannerId = "
1002                                     + client.mScannerId
1003                                     + " callbackType "
1004                                     + settings.getCallbackType());
1005                 }
1006             }
1007         }
1008     }
1009 
1010     /** Callback method for configuration of scan parameters. */
onScanParamSetupCompleted(int status, int scannerId)1011     void onScanParamSetupCompleted(int status, int scannerId) {
1012         Log.d(TAG, "onScanParamSetupCompleted() - scannerId=" + scannerId + ", status=" + status);
1013         ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
1014         if (app == null || app.mCallback == null) {
1015             Log.e(TAG, "Advertise app or callback is null");
1016             return;
1017         }
1018     }
1019 
1020     // callback from ScanManager for dispatch of errors apps.
onScanManagerErrorCallback(int scannerId, int errorCode)1021     void onScanManagerErrorCallback(int scannerId, int errorCode) throws RemoteException {
1022         ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
1023         if (app == null) {
1024             Log.e(TAG, "App null");
1025             return;
1026         }
1027         if (app.mCallback != null) {
1028             app.mCallback.onScanManagerErrorCallback(errorCode);
1029         } else {
1030             try {
1031                 sendErrorByPendingIntent(app.mInfo, errorCode);
1032             } catch (PendingIntent.CanceledException e) {
1033                 Log.e(TAG, "Error sending error code via PendingIntent:" + e);
1034             }
1035         }
1036     }
1037 
msftMonitorHandleFromFilterIndex(int filterIndex)1038     int msftMonitorHandleFromFilterIndex(int filterIndex) {
1039         if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) {
1040             Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' does not exist");
1041             return -1;
1042         }
1043         return mFilterIndexToMsftAdvMonitorMap.get(filterIndex);
1044     }
1045 
onMsftAdvMonitorAdd(int filterIndex, int monitorHandle, int status)1046     void onMsftAdvMonitorAdd(int filterIndex, int monitorHandle, int status) {
1047         if (status != 0) {
1048             Log.e(
1049                     TAG,
1050                     "Error adding advertisement monitor with filter index '" + filterIndex + "'");
1051             return;
1052         }
1053         if (mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) {
1054             Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' already added");
1055             return;
1056         }
1057         mFilterIndexToMsftAdvMonitorMap.put(filterIndex, monitorHandle);
1058     }
1059 
onMsftAdvMonitorRemove(int filterIndex, int status)1060     void onMsftAdvMonitorRemove(int filterIndex, int status) {
1061         if (status != 0) {
1062             Log.e(
1063                     TAG,
1064                     "Error removing advertisement monitor with filter index '" + filterIndex + "'");
1065         }
1066         if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) {
1067             Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' does not exist");
1068             return;
1069         }
1070         mFilterIndexToMsftAdvMonitorMap.remove(filterIndex);
1071     }
1072 
onMsftAdvMonitorEnable(int status)1073     void onMsftAdvMonitorEnable(int status) {
1074         if (status != 0) {
1075             Log.e(TAG, "Error enabling advertisement monitor");
1076         }
1077     }
1078 
1079     /**************************************************************************
1080      * Scan functions - Shared CLIENT/SERVER
1081      *************************************************************************/
1082 
1083     @RequiresPermission(BLUETOOTH_SCAN)
registerScanner( IScannerCallback callback, WorkSource workSource, AttributionSource source)1084     void registerScanner(
1085             IScannerCallback callback, WorkSource workSource, AttributionSource source) {
1086         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "registerScanner")) {
1087             return;
1088         }
1089 
1090         enforceImpersonationPermissionIfNeeded(workSource);
1091 
1092         AppScanStats app = mScannerMap.getAppScanStatsByUid(Binder.getCallingUid());
1093         if (app != null
1094                 && app.isScanningTooFrequently()
1095                 && !Utils.checkCallerHasPrivilegedPermission(mAdapterService)) {
1096             Log.e(TAG, "App '" + app.mAppName + "' is scanning too frequently");
1097             try {
1098                 callback.onScannerRegistered(ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY, -1);
1099             } catch (RemoteException e) {
1100                 Log.e(TAG, "Exception: " + e);
1101             }
1102             return;
1103         }
1104         registerScannerInternal(callback, source, workSource);
1105     }
1106 
1107     /** Intended for internal use within the Bluetooth app. Bypass permission check */
registerScannerInternal( IScannerCallback callback, AttributionSource attrSource, WorkSource workSource)1108     public void registerScannerInternal(
1109             IScannerCallback callback, AttributionSource attrSource, WorkSource workSource) {
1110         UUID uuid = UUID.randomUUID();
1111         Log.d(TAG, "registerScanner() - UUID=" + uuid);
1112 
1113         mScannerMap.add(uuid, attrSource, workSource, callback, mAdapterService, this);
1114         mScanManager.registerScanner(uuid);
1115     }
1116 
1117     @RequiresPermission(BLUETOOTH_SCAN)
unregisterScanner(int scannerId, AttributionSource source)1118     void unregisterScanner(int scannerId, AttributionSource source) {
1119         if (!checkScanPermissionForDataDelivery(
1120                 mAdapterService, source, TAG, "unregisterScanner")) {
1121             return;
1122         }
1123 
1124         unregisterScannerInternal(scannerId);
1125     }
1126 
1127     /** Intended for internal use within the Bluetooth app. Bypass permission check */
unregisterScannerInternal(int scannerId)1128     public void unregisterScannerInternal(int scannerId) {
1129         Log.d(TAG, "unregisterScanner() - scannerId=" + scannerId);
1130         mScannerMap.remove(scannerId);
1131         mScanManager.unregisterScanner(scannerId);
1132     }
1133 
getAssociatedDevices(String callingPackage)1134     private List<String> getAssociatedDevices(String callingPackage) {
1135         if (mCompanionManager == null) {
1136             return Collections.emptyList();
1137         }
1138 
1139         final long identity = Binder.clearCallingIdentity();
1140         try {
1141             return mCompanionManager.getAllAssociations().stream()
1142                     .filter(
1143                             info ->
1144                                     info.getPackageName().equals(callingPackage)
1145                                             && !info.isSelfManaged()
1146                                             && info.getDeviceMacAddress() != null)
1147                     .map(info -> info.getDeviceMacAddress().toString())
1148                     .collect(Collectors.toList());
1149         } catch (SecurityException se) {
1150             // Not an app with associated devices
1151         } catch (Exception e) {
1152             Log.e(TAG, "Cannot check device associations for " + callingPackage, e);
1153         } finally {
1154             Binder.restoreCallingIdentity(identity);
1155         }
1156         return Collections.emptyList();
1157     }
1158 
1159     @RequiresPermission(BLUETOOTH_SCAN)
startScan( int scannerId, ScanSettings settings, List<ScanFilter> filters, AttributionSource source)1160     void startScan(
1161             int scannerId,
1162             ScanSettings settings,
1163             List<ScanFilter> filters,
1164             AttributionSource source) {
1165         Log.d(TAG, "Start scan with filters");
1166         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "startScan")) {
1167             return;
1168         }
1169 
1170         enforcePrivilegedPermissionIfNeeded(settings);
1171         String callingPackage = source.getPackageName();
1172         settings = enforceReportDelayFloor(settings);
1173         enforcePrivilegedPermissionIfNeeded(filters);
1174         final ScanClient scanClient = new ScanClient(scannerId, settings, filters);
1175         scanClient.mUserHandle = Binder.getCallingUserHandle();
1176         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
1177         scanClient.mEligibleForSanitizedExposureNotification =
1178                 callingPackage.equals(mExposureNotificationPackage);
1179 
1180         scanClient.mHasDisavowedLocation =
1181                 Utils.hasDisavowedLocationForScan(mAdapterService, source, mTestModeEnabled);
1182 
1183         scanClient.mIsQApp =
1184                 checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q);
1185         if (!scanClient.mHasDisavowedLocation) {
1186             if (scanClient.mIsQApp) {
1187                 scanClient.mHasLocationPermission =
1188                         Utils.checkCallerHasFineLocation(
1189                                 mAdapterService, source, scanClient.mUserHandle);
1190             } else {
1191                 scanClient.mHasLocationPermission =
1192                         Utils.checkCallerHasCoarseOrFineLocation(
1193                                 mAdapterService, source, scanClient.mUserHandle);
1194             }
1195         }
1196         scanClient.mHasNetworkSettingsPermission =
1197                 Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
1198         scanClient.mHasNetworkSetupWizardPermission =
1199                 Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
1200         scanClient.mHasScanWithoutLocationPermission =
1201                 Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
1202         scanClient.mAssociatedDevices = getAssociatedDevices(callingPackage);
1203 
1204         startScan(scannerId, settings, filters, scanClient);
1205     }
1206 
1207     /** Intended for internal use within the Bluetooth app. Bypass permission check */
startScanInternal(int scannerId, ScanSettings settings, List<ScanFilter> filters)1208     public void startScanInternal(int scannerId, ScanSettings settings, List<ScanFilter> filters) {
1209         final ScanClient scanClient = new ScanClient(scannerId, settings, filters);
1210         scanClient.mIsInternalClient = true;
1211         scanClient.mUserHandle = Binder.getCallingUserHandle();
1212         scanClient.mEligibleForSanitizedExposureNotification = false;
1213         scanClient.mHasDisavowedLocation = false;
1214         scanClient.mIsQApp = true;
1215         scanClient.mHasNetworkSettingsPermission =
1216                 Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
1217         scanClient.mHasNetworkSetupWizardPermission =
1218                 Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
1219         scanClient.mHasScanWithoutLocationPermission =
1220                 Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
1221         scanClient.mAssociatedDevices = Collections.emptyList();
1222 
1223         startScan(scannerId, settings, filters, scanClient);
1224     }
1225 
startScan( int scannerId, ScanSettings settings, List<ScanFilter> filters, ScanClient scanClient)1226     private void startScan(
1227             int scannerId, ScanSettings settings, List<ScanFilter> filters, ScanClient scanClient) {
1228         AppScanStats app = mScannerMap.getAppScanStatsById(scannerId);
1229         if (app != null) {
1230             scanClient.mStats = app;
1231             mScanManager.fetchAppForegroundState(scanClient);
1232             boolean isFilteredScan = (filters != null) && !filters.isEmpty();
1233             boolean isCallbackScan = false;
1234 
1235             ScannerMap.ScannerApp cbApp = mScannerMap.getById(scannerId);
1236             if (cbApp != null) {
1237                 isCallbackScan = cbApp.mCallback != null;
1238             }
1239             app.recordScanStart(
1240                     settings,
1241                     filters,
1242                     isFilteredScan,
1243                     isCallbackScan,
1244                     scannerId,
1245                     cbApp == null ? null : cbApp.mAttributionTag);
1246         }
1247 
1248         mScanManager.startScan(scanClient);
1249     }
1250 
1251     @RequiresPermission(BLUETOOTH_SCAN)
registerPiAndStartScan( PendingIntent pendingIntent, ScanSettings settings, List<ScanFilter> filters, AttributionSource source)1252     void registerPiAndStartScan(
1253             PendingIntent pendingIntent,
1254             ScanSettings settings,
1255             List<ScanFilter> filters,
1256             AttributionSource source) {
1257         Log.d(TAG, "Start scan with filters, for PendingIntent");
1258         if (!checkScanPermissionForDataDelivery(
1259                 mAdapterService, source, TAG, "registerPiAndStartScan")) {
1260             return;
1261         }
1262 
1263         enforcePrivilegedPermissionIfNeeded(settings);
1264         settings = enforceReportDelayFloor(settings);
1265         enforcePrivilegedPermissionIfNeeded(filters);
1266         UUID uuid = UUID.randomUUID();
1267         String callingPackage = source.getPackageName();
1268         int callingUid = source.getUid();
1269         PendingIntentInfo piInfo =
1270                 new PendingIntentInfo(pendingIntent, settings, filters, callingPackage, callingUid);
1271         Log.d(
1272                 TAG,
1273                 "startScan(PI) -"
1274                         + (" UUID=" + uuid)
1275                         + (" Package=" + callingPackage)
1276                         + (" UID=" + callingUid));
1277 
1278         // Don't start scan if the Pi scan already in mScannerMap.
1279         if (mScannerMap.getByPendingIntentInfo(pendingIntent) != null) {
1280             Log.d(TAG, "Don't startScan(PI) since the same Pi scan already in mScannerMap.");
1281             return;
1282         }
1283 
1284         ScannerMap.ScannerApp app = mScannerMap.add(uuid, source, piInfo, mAdapterService, this);
1285 
1286         app.mUserHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid());
1287         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
1288         app.mEligibleForSanitizedExposureNotification =
1289                 callingPackage.equals(mExposureNotificationPackage);
1290 
1291         app.mHasDisavowedLocation =
1292                 Utils.hasDisavowedLocationForScan(mAdapterService, source, mTestModeEnabled);
1293 
1294         if (!app.mHasDisavowedLocation) {
1295             try {
1296                 if (checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q)) {
1297                     app.mHasLocationPermission =
1298                             Utils.checkCallerHasFineLocation(
1299                                     mAdapterService, source, app.mUserHandle);
1300                 } else {
1301                     app.mHasLocationPermission =
1302                             Utils.checkCallerHasCoarseOrFineLocation(
1303                                     mAdapterService, source, app.mUserHandle);
1304                 }
1305             } catch (SecurityException se) {
1306                 // No need to throw here. Just mark as not granted.
1307                 app.mHasLocationPermission = false;
1308             }
1309         }
1310         app.mHasNetworkSettingsPermission =
1311                 Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
1312         app.mHasNetworkSetupWizardPermission =
1313                 Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
1314         app.mHasScanWithoutLocationPermission =
1315                 Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
1316         app.mAssociatedDevices = getAssociatedDevices(callingPackage);
1317         mScanManager.registerScanner(uuid);
1318 
1319         // If this fails, we should stop the scan immediately.
1320         if (!pendingIntent.addCancelListener(Runnable::run, mScanIntentCancelListener)) {
1321             Log.d(TAG, "scanning PendingIntent is already cancelled, stopping scan.");
1322             stopScan(pendingIntent, source);
1323         }
1324     }
1325 
1326     /** Start a scan with pending intent. */
1327     @VisibleForTesting
continuePiStartScan(int scannerId, ScannerMap.ScannerApp app)1328     void continuePiStartScan(int scannerId, ScannerMap.ScannerApp app) {
1329         final PendingIntentInfo piInfo = app.mInfo;
1330         final ScanClient scanClient =
1331                 new ScanClient(scannerId, piInfo.settings, piInfo.filters, piInfo.callingUid);
1332         scanClient.mHasLocationPermission = app.mHasLocationPermission;
1333         scanClient.mUserHandle = app.mUserHandle;
1334         scanClient.mIsQApp =
1335                 checkCallerTargetSdk(mAdapterService, app.mName, Build.VERSION_CODES.Q);
1336         scanClient.mEligibleForSanitizedExposureNotification =
1337                 app.mEligibleForSanitizedExposureNotification;
1338         scanClient.mHasNetworkSettingsPermission = app.mHasNetworkSettingsPermission;
1339         scanClient.mHasNetworkSetupWizardPermission = app.mHasNetworkSetupWizardPermission;
1340         scanClient.mHasScanWithoutLocationPermission = app.mHasScanWithoutLocationPermission;
1341         scanClient.mAssociatedDevices = app.mAssociatedDevices;
1342         scanClient.mHasDisavowedLocation = app.mHasDisavowedLocation;
1343 
1344         AppScanStats scanStats = mScannerMap.getAppScanStatsById(scannerId);
1345         if (scanStats != null) {
1346             scanClient.mStats = scanStats;
1347             mScanManager.fetchAppForegroundState(scanClient);
1348             boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty();
1349             scanStats.recordScanStart(
1350                     piInfo.settings,
1351                     piInfo.filters,
1352                     isFilteredScan,
1353                     false,
1354                     scannerId,
1355                     app.mAttributionTag);
1356         }
1357 
1358         mScanManager.startScan(scanClient);
1359     }
1360 
1361     @RequiresPermission(BLUETOOTH_SCAN)
flushPendingBatchResults(int scannerId, AttributionSource source)1362     void flushPendingBatchResults(int scannerId, AttributionSource source) {
1363         if (!checkScanPermissionForDataDelivery(
1364                 mAdapterService, source, TAG, "flushPendingBatchResults")) {
1365             return;
1366         }
1367         flushPendingBatchResultsInternal(scannerId);
1368     }
1369 
flushPendingBatchResultsInternal(int scannerId)1370     private void flushPendingBatchResultsInternal(int scannerId) {
1371         Log.d(TAG, "flushPendingBatchResultsInternal - scannerId=" + scannerId);
1372         mScanManager.flushBatchScanResults(new ScanClient(scannerId));
1373     }
1374 
1375     @RequiresPermission(BLUETOOTH_SCAN)
stopScan(int scannerId, AttributionSource source)1376     void stopScan(int scannerId, AttributionSource source) {
1377         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "stopScan")) {
1378             return;
1379         }
1380         stopScanInternal(scannerId);
1381     }
1382 
1383     /** Intended for internal use within the Bluetooth app. Bypass permission check */
stopScanInternal(int scannerId)1384     public void stopScanInternal(int scannerId) {
1385         int scanQueueSize =
1386                 mScanManager.getBatchScanQueue().size() + mScanManager.getRegularScanQueue().size();
1387         Log.d(TAG, "stopScan() - queue size =" + scanQueueSize);
1388 
1389         AppScanStats app = mScannerMap.getAppScanStatsById(scannerId);
1390         if (app != null) {
1391             app.recordScanStop(scannerId);
1392         }
1393 
1394         mScanManager.stopScan(scannerId);
1395     }
1396 
1397     @RequiresPermission(BLUETOOTH_SCAN)
stopScan(PendingIntent intent, AttributionSource source)1398     void stopScan(PendingIntent intent, AttributionSource source) {
1399         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "stopScan")) {
1400             return;
1401         }
1402         stopScanInternal(intent);
1403     }
1404 
1405     /** Intended for internal use within the Bluetooth app. Bypass permission check */
stopScanInternal(PendingIntent intent)1406     private void stopScanInternal(PendingIntent intent) {
1407         ScannerMap.ScannerApp app = mScannerMap.getByPendingIntentInfo(intent);
1408         Log.v(TAG, "stopScan(PendingIntent): app found = " + app);
1409         if (app != null) {
1410             intent.removeCancelListener(mScanIntentCancelListener);
1411             final int scannerId = app.mId;
1412             stopScanInternal(scannerId);
1413             // Also unregister the scanner
1414             unregisterScannerInternal(scannerId);
1415         }
1416     }
1417 
1418     /**************************************************************************
1419      * PERIODIC SCANNING
1420      *************************************************************************/
1421     @RequiresPermission(BLUETOOTH_SCAN)
registerSync( ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback, AttributionSource source)1422     void registerSync(
1423             ScanResult scanResult,
1424             int skip,
1425             int timeout,
1426             IPeriodicAdvertisingCallback callback,
1427             AttributionSource source) {
1428         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "registerSync")) {
1429             return;
1430         }
1431         mPeriodicScanManager.startSync(scanResult, skip, timeout, callback);
1432     }
1433 
1434     @RequiresPermission(BLUETOOTH_SCAN)
unregisterSync(IPeriodicAdvertisingCallback callback, AttributionSource source)1435     void unregisterSync(IPeriodicAdvertisingCallback callback, AttributionSource source) {
1436         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "unregisterSync")) {
1437             return;
1438         }
1439         mPeriodicScanManager.stopSync(callback);
1440     }
1441 
1442     @RequiresPermission(BLUETOOTH_SCAN)
transferSync( BluetoothDevice bda, int serviceData, int syncHandle, AttributionSource source)1443     void transferSync(
1444             BluetoothDevice bda, int serviceData, int syncHandle, AttributionSource source) {
1445         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "transferSync")) {
1446             return;
1447         }
1448         mPeriodicScanManager.transferSync(bda, serviceData, syncHandle);
1449     }
1450 
1451     @RequiresPermission(BLUETOOTH_SCAN)
transferSetInfo( BluetoothDevice bda, int serviceData, int advHandle, IPeriodicAdvertisingCallback callback, AttributionSource source)1452     void transferSetInfo(
1453             BluetoothDevice bda,
1454             int serviceData,
1455             int advHandle,
1456             IPeriodicAdvertisingCallback callback,
1457             AttributionSource source) {
1458         if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "transferSetInfo")) {
1459             return;
1460         }
1461         mPeriodicScanManager.transferSetInfo(bda, serviceData, advHandle, callback);
1462     }
1463 
1464     @RequiresPermission(BLUETOOTH_SCAN)
numHwTrackFiltersAvailable(AttributionSource source)1465     int numHwTrackFiltersAvailable(AttributionSource source) {
1466         if (!checkScanPermissionForDataDelivery(
1467                 mAdapterService, source, TAG, "numHwTrackFiltersAvailable")) {
1468             return 0;
1469         }
1470         return (mAdapterService.getTotalNumOfTrackableAdvertisements()
1471                 - mScanManager.getCurrentUsedTrackingAdvertisement());
1472     }
1473 
1474     /**
1475      * DeathRecipient handler used to unregister applications that disconnect ungracefully (ie.
1476      * crash or forced close).
1477      */
1478     class ScannerDeathRecipient implements IBinder.DeathRecipient {
1479         int mScannerId;
1480         private final String mPackageName;
1481 
ScannerDeathRecipient(int scannerId, String packageName)1482         ScannerDeathRecipient(int scannerId, String packageName) {
1483             mScannerId = scannerId;
1484             mPackageName = packageName;
1485         }
1486 
1487         @Override
binderDied()1488         public void binderDied() {
1489             Log.d(
1490                     TAG,
1491                     "Binder is dead - unregistering scanner ("
1492                             + mPackageName
1493                             + " "
1494                             + mScannerId
1495                             + ")!");
1496 
1497             ScanClient client = getScanClient(mScannerId);
1498             if (client != null) {
1499                 handleDeadScanClient(client);
1500             }
1501         }
1502 
getScanClient(int clientIf)1503         private ScanClient getScanClient(int clientIf) {
1504             for (ScanClient client : mScanManager.getRegularScanQueue()) {
1505                 if (client.mScannerId == clientIf) {
1506                     return client;
1507                 }
1508             }
1509             for (ScanClient client : mScanManager.getBatchScanQueue()) {
1510                 if (client.mScannerId == clientIf) {
1511                     return client;
1512                 }
1513             }
1514             return null;
1515         }
1516     }
1517 
needsPrivilegedPermissionForScan(ScanSettings settings)1518     private boolean needsPrivilegedPermissionForScan(ScanSettings settings) {
1519         // BLE scan only mode needs special permission.
1520         if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
1521             return true;
1522         }
1523 
1524         // Regular scan, no special permission.
1525         if (settings == null) {
1526             return false;
1527         }
1528 
1529         // Ambient discovery mode, needs privileged permission.
1530         if (settings.getScanMode() == ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY) {
1531             return true;
1532         }
1533 
1534         // Regular scan, no special permission.
1535         if (settings.getReportDelayMillis() == 0) {
1536             return false;
1537         }
1538 
1539         // Batch scan, truncated mode needs permission.
1540         return settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_ABBREVIATED;
1541     }
1542 
1543     /*
1544      * The ScanFilter#setDeviceAddress API overloads are @SystemApi access methods.  This
1545      * requires that the permissions be BLUETOOTH_PRIVILEGED.
1546      */
1547     @SuppressLint("AndroidFrameworkRequiresPermission")
enforcePrivilegedPermissionIfNeeded(List<ScanFilter> filters)1548     private void enforcePrivilegedPermissionIfNeeded(List<ScanFilter> filters) {
1549         Log.d(TAG, "enforcePrivilegedPermissionIfNeeded(" + filters + ")");
1550         // Some 3p API cases may have null filters, need to allow
1551         if (filters != null) {
1552             for (ScanFilter filter : filters) {
1553                 // The only case to enforce here is if there is an address
1554                 // If there is an address, enforce if the correct combination criteria is met.
1555                 if (filter.getDeviceAddress() != null) {
1556                     // At this point we have an address, that means a caller used the
1557                     // setDeviceAddress(address) public API for the ScanFilter
1558                     // We don't want to enforce if the type is PUBLIC and the IRK is null
1559                     // However, if we have a different type that means the caller used a new
1560                     // @SystemApi such as setDeviceAddress(address, type) or
1561                     // setDeviceAddress(address, type, irk) which are both @SystemApi and require
1562                     // permissions to be enforced
1563                     if (filter.getAddressType() == BluetoothDevice.ADDRESS_TYPE_PUBLIC
1564                             && filter.getIrk() == null) {
1565                         // Do not enforce
1566                     } else {
1567                         mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
1568                     }
1569                 }
1570             }
1571         }
1572     }
1573 
1574     @SuppressLint("AndroidFrameworkRequiresPermission")
enforcePrivilegedPermissionIfNeeded(ScanSettings settings)1575     private void enforcePrivilegedPermissionIfNeeded(ScanSettings settings) {
1576         if (needsPrivilegedPermissionForScan(settings)) {
1577             mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
1578         }
1579     }
1580 
1581     // Enforce caller has UPDATE_DEVICE_STATS permission, which allows the caller to blame other
1582     // apps for Bluetooth usage. A {@link SecurityException} will be thrown if the caller app does
1583     // not have UPDATE_DEVICE_STATS permission.
1584     @RequiresPermission(UPDATE_DEVICE_STATS)
enforceImpersonationPermission()1585     private void enforceImpersonationPermission() {
1586         mAdapterService.enforceCallingOrSelfPermission(
1587                 UPDATE_DEVICE_STATS, "Need UPDATE_DEVICE_STATS permission");
1588     }
1589 
1590     @SuppressLint("AndroidFrameworkRequiresPermission")
enforceImpersonationPermissionIfNeeded(WorkSource workSource)1591     private void enforceImpersonationPermissionIfNeeded(WorkSource workSource) {
1592         if (workSource != null) {
1593             enforceImpersonationPermission();
1594         }
1595     }
1596 
1597     /**
1598      * Ensures the report delay is either 0 or at least the floor value (5000ms)
1599      *
1600      * @param settings are the scan settings passed into a request to start le scanning
1601      * @return the passed in ScanSettings object if the report delay is 0 or above the floor value;
1602      *     a new ScanSettings object with the report delay being the floor value if the original
1603      *     report delay was between 0 and the floor value (exclusive of both)
1604      */
1605     @VisibleForTesting
enforceReportDelayFloor(ScanSettings settings)1606     ScanSettings enforceReportDelayFloor(ScanSettings settings) {
1607         if (settings.getReportDelayMillis() == 0) {
1608             return settings;
1609         }
1610 
1611         // Need to clear identity to pass device config permission check
1612         final long callerToken = Binder.clearCallingIdentity();
1613         try {
1614             long floor =
1615                     DeviceConfig.getLong(
1616                             DeviceConfig.NAMESPACE_BLUETOOTH,
1617                             "report_delay",
1618                             DEFAULT_REPORT_DELAY_FLOOR);
1619 
1620             if (settings.getReportDelayMillis() > floor) {
1621                 return settings;
1622             } else {
1623                 return new ScanSettings.Builder()
1624                         .setCallbackType(settings.getCallbackType())
1625                         .setLegacy(settings.getLegacy())
1626                         .setMatchMode(settings.getMatchMode())
1627                         .setNumOfMatches(settings.getNumOfMatches())
1628                         .setPhy(settings.getPhy())
1629                         .setReportDelay(floor)
1630                         .setScanMode(settings.getScanMode())
1631                         .setScanResultType(settings.getScanResultType())
1632                         .build();
1633             }
1634         } finally {
1635             Binder.restoreCallingIdentity(callerToken);
1636         }
1637     }
1638 
dumpRegisterId(StringBuilder sb)1639     public void dumpRegisterId(StringBuilder sb) {
1640         sb.append("  Scanner:\n");
1641         mScannerMap.dumpApps(sb, ProfileService::println);
1642     }
1643 
dump(StringBuilder sb)1644     public void dump(StringBuilder sb) {
1645         sb.append("GATT Scanner Map\n");
1646         mScannerMap.dump(sb);
1647     }
1648 }
1649