Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c50d7fb2 authored by Oli Lan's avatar Oli Lan
Browse files

Check BLUETOOTH_SCAN permission when performing scans from GattService.

This adds checks for the new BLUETOOTH_SCAN permission in GattService.

The app op is noted when the location app op was noted previously,
i.e. in the startScan and registerPiAndStartScan methods. Hence
checkForDataDelivery is used there, and checkPreFlight is used
elsewhere.

Checks of the BLUETOOTH permission will be updated in a future CL.

Bug: 183203469
Test: atest GattServiceTest
Change-Id: I8696009c4bedaf42e3ca96d91c5c4d42d879a802
parent ba3e8e9a
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.bluetooth;

import static android.Manifest.permission.BLUETOOTH_SCAN;
import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +31,7 @@ import android.companion.CompanionDeviceManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.location.LocationManager;
@@ -383,6 +388,38 @@ public final class Utils {
                "Need DUMP permission");
    }

    /**
     * Returns true if the BLUETOOTH_SCAN permission is granted for the calling app. Returns false
     * if the result is a soft denial. Throws SecurityException if the result is a hard denial.
     *
     * <p>Should be used in situations where the app op should not be noted.
     */
    public static boolean checkScanPermissionForPreflight(Context context) {
        int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight(
                context, BLUETOOTH_SCAN);
        if (permissionCheckResult == PERMISSION_HARD_DENIED) {
            throw new SecurityException("Need BLUETOOTH_SCAN permission");
        }
        return permissionCheckResult == PERMISSION_GRANTED;
    }

    /**
     * Returns true if the BLUETOOTH_SCAN permission is granted for the calling app. Returns false
     * if the result is a soft denial. Throws SecurityException if the result is a hard denial.
     *
     * <p>Should be used in situations where data will be delivered and hence the app op should
     * be noted.
     */
    public static boolean checkScanPermissionForDataDelivery(
            Context context, String callingPackage, String callingAttributionTag, String message) {
        int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(
                context, BLUETOOTH_SCAN, callingPackage, callingAttributionTag, message);
        if (permissionCheckResult == PERMISSION_HARD_DENIED) {
            throw new SecurityException("Need BLUETOOTH_SCAN permission");
        }
        return permissionCheckResult == PERMISSION_GRANTED;
    }

    public static boolean callerIsSystemOrActiveUser(String tag, String method) {
        if (!checkCaller()) {
          Log.w(TAG, method + "() - Not allowed for non-active user and non-system user");
+3 −0
Original line number Diff line number Diff line
@@ -107,6 +107,9 @@ import java.util.UUID;
        /** Whether the calling app has the network setup wizard permission */
        boolean mHasScanWithoutLocationPermission;

        /** Whether the calling app has disavowed the use of bluetooth for location */
        boolean mHasDisavowedLocation;

        boolean mEligibleForSanitizedExposureNotification;

        public List<String> mAssociatedDevices;
+55 −36
Original line number Diff line number Diff line
@@ -1237,6 +1237,9 @@ public class GattService extends ProfileService {
                || client.hasScanWithoutLocationPermission) {
            return true;
        }
        if (client.hasDisavowedLocation) {
            return true;
        }
        return client.hasLocationPermission && !Utils.blockedByLocationOff(this, client.userHandle);
    }

@@ -2166,8 +2169,12 @@ public class GattService extends ProfileService {
        if (DBG) {
            Log.d(TAG, "start scan with filters");
        }
        UserHandle callingUser = UserHandle.of(UserHandle.getCallingUserId());
        enforceAdminPermission();

        if (!Utils.checkScanPermissionForDataDelivery(
                this, callingPackage, callingFeatureId, "Starting GATT scan.")) {
            return;
        }

        if (needsPrivilegedPermissionForScan(settings)) {
            enforcePrivilegedPermission();
        }
@@ -2176,7 +2183,11 @@ public class GattService extends ProfileService {
        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
        scanClient.eligibleForSanitizedExposureNotification =
                callingPackage.equals(mExposureNotificationPackage);

        // TODO: set hasDisavowedLocation

        scanClient.isQApp = Utils.isQApp(this, callingPackage);
        if (!scanClient.hasDisavowedLocation) {
            if (scanClient.isQApp) {
                scanClient.hasLocationPermission = Utils.checkCallerHasFineLocation(this, mAppOps,
                        callingPackage, callingFeatureId, scanClient.userHandle);
@@ -2184,6 +2195,7 @@ public class GattService extends ProfileService {
                scanClient.hasLocationPermission = Utils.checkCallerHasCoarseOrFineLocation(this,
                        mAppOps, callingPackage, callingFeatureId, scanClient.userHandle);
            }
        }
        scanClient.hasNetworkSettingsPermission =
                Utils.checkCallerHasNetworkSettingsPermission(this);
        scanClient.hasNetworkSetupWizardPermission =
@@ -2212,7 +2224,12 @@ public class GattService extends ProfileService {
        if (DBG) {
            Log.d(TAG, "start scan with filters, for PendingIntent");
        }
        enforceAdminPermission();

        if (!Utils.checkScanPermissionForDataDelivery(
                this, callingPackage, callingFeatureId, "Starting GATT scan.")) {
            return;
        }

        if (needsPrivilegedPermissionForScan(settings)) {
            enforcePrivilegedPermission();
        }
@@ -2238,7 +2255,11 @@ public class GattService extends ProfileService {
        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
        app.mEligibleForSanitizedExposureNotification =
                callingPackage.equals(mExposureNotificationPackage);

        // TODO: set hasDisavowedLocation

        app.mIsQApp = Utils.isQApp(this, callingPackage);
        if (!app.mHasDisavowedLocation) {
            try {
                if (app.mIsQApp) {
                    app.hasLocationPermission = Utils.checkCallerHasFineLocation(
@@ -2251,6 +2272,7 @@ public class GattService extends ProfileService {
                // No need to throw here. Just mark as not granted.
                app.hasLocationPermission = false;
            }
        }
        app.mHasNetworkSettingsPermission =
                Utils.checkCallerHasNetworkSettingsPermission(this);
        app.mHasNetworkSetupWizardPermission =
@@ -2274,6 +2296,7 @@ public class GattService extends ProfileService {
        scanClient.hasNetworkSetupWizardPermission = app.mHasNetworkSetupWizardPermission;
        scanClient.hasScanWithoutLocationPermission = app.mHasScanWithoutLocationPermission;
        scanClient.associatedDevices = app.mAssociatedDevices;
        scanClient.hasDisavowedLocation = app.mHasDisavowedLocation;

        AppScanStats scanStats = mScannerMap.getAppScanStatsById(scannerId);
        if (scanStats != null) {
@@ -2294,7 +2317,7 @@ public class GattService extends ProfileService {
    }

    void stopScan(int scannerId) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        int scanQueueSize =
                mScanManager.getBatchScanQueue().size() + mScanManager.getRegularScanQueue().size();
        if (DBG) {
@@ -2311,7 +2334,7 @@ public class GattService extends ProfileService {
    }

    void stopScan(PendingIntent intent, String callingPackage) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        PendingIntentInfo pii = new PendingIntentInfo();
        pii.intent = intent;
        ScannerMap.App app = mScannerMap.getByContextInfo(pii);
@@ -2354,12 +2377,12 @@ public class GattService extends ProfileService {
     *************************************************************************/
    void registerSync(ScanResult scanResult, int skip, int timeout,
            IPeriodicAdvertisingCallback callback) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mPeriodicScanManager.startSync(scanResult, skip, timeout, callback);
    }

    void unregisterSync(IPeriodicAdvertisingCallback callback) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mPeriodicScanManager.stopSync(callback);
    }

@@ -2370,13 +2393,13 @@ public class GattService extends ProfileService {
            AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters,
            AdvertiseData periodicData, int duration, int maxExtAdvEvents,
            IAdvertisingSetCallback callback) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.startAdvertisingSet(parameters, advertiseData, scanResponse,
                periodicParameters, periodicData, duration, maxExtAdvEvents, callback);
    }

    void stopAdvertisingSet(IAdvertisingSetCallback callback) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.stopAdvertisingSet(callback);
    }

@@ -2386,38 +2409,38 @@ public class GattService extends ProfileService {
    }

    void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents);
    }

    void setAdvertisingData(int advertiserId, AdvertiseData data) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.setAdvertisingData(advertiserId, data);
    }

    void setScanResponseData(int advertiserId, AdvertiseData data) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.setScanResponseData(advertiserId, data);
    }

    void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.setAdvertisingParameters(advertiserId, parameters);
    }

    void setPeriodicAdvertisingParameters(int advertiserId,
            PeriodicAdvertisingParameters parameters) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.setPeriodicAdvertisingParameters(advertiserId, parameters);
    }

    void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.setPeriodicAdvertisingData(advertiserId, data);
    }

    void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) {
        enforceAdminPermission();
        Utils.checkScanPermissionForPreflight(this);
        mAdvertiseManager.setPeriodicAdvertisingEnable(advertiserId, enable);
    }

@@ -3301,10 +3324,6 @@ public class GattService extends ProfileService {
        return type;
    }

    private void enforceAdminPermission() {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    }

    private boolean needsPrivilegedPermissionForScan(ScanSettings settings) {
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        // BLE scan only mode needs special permission.