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

Commit 18289b7c authored by Sarvesh Kalwit's avatar Sarvesh Kalwit Committed by Gerrit Code Review
Browse files

Merge "Implement a APCF->MSFT converter utility" into main

parents c3a654f6 04410c9d
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -16,7 +16,28 @@

package com.android.bluetooth.le_scan;

import android.bluetooth.le.ScanFilter;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.UUID;

/** Helper class used to manage MSFT Advertisement Monitors. */
class MsftAdvMonitor {
    /* Only pattern filtering is supported currently */
    // private static final int MSFT_CONDITION_TYPE_ALL = 0x00;
    private static final int MSFT_CONDITION_TYPE_PATTERNS = 0x01;
    // private static final int MSFT_CONDITION_TYPE_UUID = 0x02;
    // private static final int MSFT_CONDITION_TYPE_IRK = 0x03;
    // private static final int MSFT_CONDITION_TYPE_ADDRESS = 0x04;

    // Hardcoded values taken from CrOS defaults
    private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191
    private static final byte RSSI_THRESHOLD_LOW = (byte) 0xB0; // 176
    private static final byte RSSI_THRESHOLD_LOW_TIME_INTERVAL = (byte) 0x28; // 40s
    private static final byte RSSI_SAMPLING_PERIOD = (byte) 0x05; // 500ms
    private static final int FILTER_PATTERN_START_POSITION = (byte) 0x00;

    static class Monitor {
        public byte rssi_threshold_high;
        public byte rssi_threshold_low;
@@ -35,4 +56,60 @@ class MsftAdvMonitor {
        byte addr_type;
        String bd_addr;
    }

    private final Monitor mMonitor = new Monitor();
    private final ArrayList<Pattern> mPatterns = new ArrayList<>();
    private final Address mAddress = new Address();

    // Constructor that converts an APCF-friendly filter to an MSFT-friendly format
    public MsftAdvMonitor(ScanFilter filter) {
        // Hardcoded values taken from CrOS defaults
        mMonitor.rssi_threshold_high = RSSI_THRESHOLD_HIGH;
        mMonitor.rssi_threshold_low = RSSI_THRESHOLD_LOW;
        mMonitor.rssi_threshold_low_time_interval = RSSI_THRESHOLD_LOW_TIME_INTERVAL;
        mMonitor.rssi_sampling_period = RSSI_SAMPLING_PERIOD;
        mMonitor.condition_type = MSFT_CONDITION_TYPE_PATTERNS;

        if (filter.getServiceDataUuid() != null && filter.getServiceDataMask() == null) {
            Pattern pattern = new Pattern();
            pattern.ad_type = (byte) 0x16; // Bluetooth Core Spec Part A, Section 1
            pattern.start_byte = FILTER_PATTERN_START_POSITION;

            // Extract the 16-bit UUID (third and fourth bytes) from the 128-bit
            // UUID in reverse endianness
            UUID uuid = filter.getServiceDataUuid().getUuid();
            ByteBuffer bb = ByteBuffer.allocate(16); // 16 byte (128 bit) UUID
            bb.putLong(uuid.getMostSignificantBits());
            bb.putLong(uuid.getLeastSignificantBits());
            pattern.pattern = new byte[] {bb.get(3), bb.get(2)};

            mPatterns.add(pattern);
        } else if (filter.getAdvertisingData() != null
                && filter.getAdvertisingData().length != 0
                && (filter.getAdvertisingDataMask() == null
                        || filter.getAdvertisingDataMask().length == 0)) {
            Pattern pattern = new Pattern();
            pattern.ad_type = (byte) filter.getAdvertisingDataType();
            pattern.start_byte = FILTER_PATTERN_START_POSITION;
            pattern.pattern = filter.getAdvertisingData();
            mPatterns.add(pattern);
        }

        if (filter.getDeviceAddress() != null) {
            mAddress.addr_type = (byte) filter.getAddressType();
            mAddress.bd_addr = filter.getDeviceAddress();
        }
    }

    Monitor getMonitor() {
        return mMonitor;
    }

    Pattern[] getPatterns() {
        return mPatterns.toArray(new Pattern[mPatterns.size()]);
    }

    Address getAddress() {
        return mAddress;
    }
}
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.bluetooth.le_scan;

import static com.google.common.truth.Truth.assertThat;

import android.bluetooth.le.ScanFilter;
import android.os.ParcelUuid;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class MsftAdvMonitorTest {
    private static final String TAG = MsftAdvMonitorTest.class.getSimpleName();
    // Hardcoded values taken from CrOS defaults
    private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191
    private static final byte RSSI_THRESHOLD_LOW = (byte) 0xB0; // 176
    private static final byte RSSI_THRESHOLD_LOW_TIME_INTERVAL = (byte) 0x28; // 40s
    private static final byte RSSI_SAMPLING_PERIOD = (byte) 0x05; // 500ms
    private static final byte CONDITION_TYPE = (byte) 0x01; // MSFT condition type - patterns
    private static final byte FILTER_PATTERN_START_POSITION = (byte) 0x00;

    // Retrieved from real Fastpair filter data
    private static final String FAST_PAIR_UUID = "0000fe2c-0000-1000-8000-00805f9b34fb";
    private static final ParcelUuid FAST_PAIR_SERVICE_DATA_UUID =
            ParcelUuid.fromString(FAST_PAIR_UUID);
    private static final byte[] FAST_PAIR_SERVICE_DATA =
            new byte[] {(byte) 0xfc, (byte) 0x12, (byte) 0x8e};

    private void assertMonitorConstants(MsftAdvMonitor monitor) {
        MsftAdvMonitor.Monitor mMonitor = monitor.getMonitor();
        assertThat(mMonitor.rssi_threshold_high).isEqualTo(RSSI_THRESHOLD_HIGH);
        assertThat(mMonitor.rssi_threshold_low).isEqualTo(RSSI_THRESHOLD_LOW);
        assertThat(mMonitor.rssi_threshold_low_time_interval)
                .isEqualTo(RSSI_THRESHOLD_LOW_TIME_INTERVAL);
        assertThat(mMonitor.rssi_sampling_period).isEqualTo(RSSI_SAMPLING_PERIOD);
        assertThat(mMonitor.condition_type).isEqualTo(CONDITION_TYPE);
    }

    @Test
    public void testFastPairScanFilter() {
        ScanFilter filter =
                new ScanFilter.Builder()
                        .setServiceData(FAST_PAIR_SERVICE_DATA_UUID, FAST_PAIR_SERVICE_DATA)
                        .build();
        MsftAdvMonitor monitor = new MsftAdvMonitor(filter);

        assertMonitorConstants(monitor);
        assertThat(monitor.getPatterns()).hasLength(1);
        MsftAdvMonitor.Pattern mPattern = monitor.getPatterns()[0];
        assertThat(mPattern.ad_type)
                .isEqualTo((byte) 0x16); // Bluetooth Core Spec Part A, Section 1
        assertThat(mPattern.start_byte).isEqualTo(FILTER_PATTERN_START_POSITION);
        assertThat(mPattern.pattern).isEqualTo(new byte[] {(byte) 0x2c, (byte) 0xfe});
    }
}