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

Commit dff912f6 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Enable DeviceConfig tuning of scan throttling.

Internally, AppScanStats supports throttling abusive apps which are
scanning aggressively.  This change preserves the current default
values, but enables updating them via DeviceConfig.

Bug: 183625640
Test: atest BluetoothInstrumentationTests
Change-Id: I142c3cf9393fb9eb9b332c243dd72a10da4842b0
parent 5ff7d863
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.bluetooth.btservice;

import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;

import static com.android.bluetooth.Utils.addressToBytes;
import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
import static com.android.bluetooth.Utils.callerIsSystemOrActiveUser;
@@ -3609,6 +3612,13 @@ public class AdapterService extends Service {
    @GuardedBy("mDeviceConfigLock")
    private Predicate<byte[]> mLocationDenylistAdvertisingData = (v) -> false;

    @GuardedBy("mDeviceConfigLock")
    private int mScanQuotaCount = DeviceConfigListener.DEFAULT_SCAN_QUOTA_COUNT;
    @GuardedBy("mDeviceConfigLock")
    private long mScanQuotaWindowMillis = DeviceConfigListener.DEFAULT_SCAN_QUOTA_WINDOW_MILLIS;
    @GuardedBy("mDeviceConfigLock")
    private long mScanTimeoutMillis = DeviceConfigListener.DEFAULT_SCAN_TIMEOUT_MILLIS;

    public @NonNull Predicate<String> getLocationDenylistName() {
        synchronized (mDeviceConfigLock) {
            return mLocationDenylistName;
@@ -3627,6 +3637,24 @@ public class AdapterService extends Service {
        }
    }

    public int getScanQuotaCount() {
        synchronized (mDeviceConfigLock) {
            return mScanQuotaCount;
        }
    }

    public long getScanQuotaWindowMillis() {
        synchronized (mDeviceConfigLock) {
            return mScanQuotaWindowMillis;
        }
    }

    public long getScanTimeoutMillis() {
        synchronized (mDeviceConfigLock) {
            return mScanTimeoutMillis;
        }
    }

    private final DeviceConfigListener mDeviceConfigListener = new DeviceConfigListener();

    private class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
@@ -3636,6 +3664,12 @@ public class AdapterService extends Service {
                "location_denylist_mac";
        private static final String LOCATION_DENYLIST_ADVERTISING_DATA =
                "location_denylist_advertising_data";
        private static final String SCAN_QUOTA_COUNT =
                "scan_quota_count";
        private static final String SCAN_QUOTA_WINDOW_MILLIS =
                "scan_quota_window_millis";
        private static final String SCAN_TIMEOUT_MILLIS =
                "scan_timeout_millis";

        /**
         * Default denylist which matches Eddystone and iBeacon payloads.
@@ -3643,6 +3677,10 @@ public class AdapterService extends Service {
        private static final String DEFAULT_LOCATION_DENYLIST_ADVERTISING_DATA =
                "⊆0016AAFE/00FFFFFF,⊆00FF4C0002/00FFFFFFFF";

        private static final int DEFAULT_SCAN_QUOTA_COUNT = 5;
        private static final long DEFAULT_SCAN_QUOTA_WINDOW_MILLIS = 30 * SECOND_IN_MILLIS;
        private static final long DEFAULT_SCAN_TIMEOUT_MILLIS = 30 * MINUTE_IN_MILLIS;

        public void start() {
            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BLUETOOTH,
                    BackgroundThread.getExecutor(), this);
@@ -3661,6 +3699,12 @@ public class AdapterService extends Service {
                mLocationDenylistAdvertisingData = BytesMatcher
                        .decode(properties.getString(LOCATION_DENYLIST_ADVERTISING_DATA,
                                DEFAULT_LOCATION_DENYLIST_ADVERTISING_DATA));
                mScanQuotaCount = properties.getInt(SCAN_QUOTA_COUNT,
                        DEFAULT_SCAN_QUOTA_COUNT);
                mScanQuotaWindowMillis = properties.getLong(SCAN_QUOTA_WINDOW_MILLIS,
                        DEFAULT_SCAN_QUOTA_WINDOW_MILLIS);
                mScanTimeoutMillis = properties.getLong(SCAN_TIMEOUT_MILLIS,
                        DEFAULT_SCAN_TIMEOUT_MILLIS);
            }
        }
    }
+15 −8
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.os.WorkSource;

import com.android.bluetooth.BluetoothMetricsProto;
import com.android.bluetooth.BluetoothStatsLog;
import com.android.bluetooth.btservice.AdapterService;
import com.android.internal.app.IBatteryStats;

import java.text.DateFormat;
@@ -101,16 +102,22 @@ import java.util.Objects;
        }
    }

    static final int NUM_SCAN_DURATIONS_KEPT = 5;
    static int getNumScanDurationsKept() {
        return AdapterService.getAdapterService().getScanQuotaCount();
    }

    // This constant defines the time window an app can scan multiple times.
    // Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during
    // this window. Once they reach this limit, they must wait until their
    // earliest recorded scan exits this window.
    static final long EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000;
    static long getExcessiveScanningPeriodMillis() {
        return AdapterService.getAdapterService().getScanQuotaWindowMillis();
    }

    // Maximum msec before scan gets downgraded to opportunistic
    static final int SCAN_TIMEOUT_MS = 30 * 60 * 1000;
    static long getScanTimeoutMillis() {
        return AdapterService.getAdapterService().getScanTimeoutMillis();
    }

    public String appName;
    public WorkSource mWorkSource; // Used for BatteryStats and BluetoothStatsLog
@@ -131,7 +138,7 @@ import java.util.Objects;
    private int mBalancedScan = 0;
    private int mLowLantencyScan = 0;
    private int mAmbientDiscoveryScan = 0;
    private List<LastScan> mLastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT);
    private List<LastScan> mLastScans = new ArrayList<LastScan>();
    private HashMap<Integer, LastScan> mOngoingScans = new HashMap<Integer, LastScan>();
    public long startTime = 0;
    public long stopTime = 0;
@@ -263,7 +270,7 @@ import java.util.Objects;
            mTotalSuspendTime += suspendDuration;
        }
        mOngoingScans.remove(scannerId);
        if (mLastScans.size() >= NUM_SCAN_DURATIONS_KEPT) {
        if (mLastScans.size() >= getNumScanDurationsKept()) {
            mLastScans.remove(0);
        }
        mLastScans.add(scan);
@@ -349,19 +356,19 @@ import java.util.Objects;
    }

    synchronized boolean isScanningTooFrequently() {
        if (mLastScans.size() < NUM_SCAN_DURATIONS_KEPT) {
        if (mLastScans.size() < getNumScanDurationsKept()) {
            return false;
        }

        return (SystemClock.elapsedRealtime() - mLastScans.get(0).timestamp)
                < EXCESSIVE_SCANNING_PERIOD_MS;
                < getExcessiveScanningPeriodMillis();
    }

    synchronized boolean isScanningTooLong() {
        if (!isScanning()) {
            return false;
        }
        return (SystemClock.elapsedRealtime() - mScanStartTime) > SCAN_TIMEOUT_MS;
        return (SystemClock.elapsedRealtime() - mScanStartTime) > getScanTimeoutMillis();
    }

    // This function truncates the app name for privacy reasons. Apps with
+1 −1
Original line number Diff line number Diff line
@@ -357,7 +357,7 @@ public class ScanManager {
                        Message msg = obtainMessage(MSG_SCAN_TIMEOUT);
                        msg.obj = client;
                        // Only one timeout message should exist at any time
                        sendMessageDelayed(msg, AppScanStats.SCAN_TIMEOUT_MS);
                        sendMessageDelayed(msg, AppScanStats.getScanTimeoutMillis());
                    }
                }
            }
+2 −2
Original line number Diff line number Diff line
@@ -314,8 +314,6 @@ public class BluetoothInCallService extends InCallService {

    public BluetoothInCallService() {
        Log.i(TAG, "BluetoothInCallService is created");
        BluetoothAdapter.getDefaultAdapter()
                .getProfileProxy(this, mProfileListener, BluetoothProfile.HEADSET);
        sInstance = this;
    }

@@ -535,6 +533,8 @@ public class BluetoothInCallService extends InCallService {
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
        BluetoothAdapter.getDefaultAdapter()
                .getProfileProxy(this, mProfileListener, BluetoothProfile.HEADSET);
        mBluetoothAdapterReceiver = new BluetoothAdapterReceiver();
        IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        registerReceiver(mBluetoothAdapterReceiver, intentFilter);