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

Commit 24df283a authored by Eric Laurent's avatar Eric Laurent
Browse files

Audio policy: anonymize Bluetooth MAC addresses

Make sure APIs returning audio device descriptors from the native
audioserver anonymize the Bluetooth MAC addresses because those are considered
privacy sensitive.
Only expose the full MAC address to system and apps with BLUETOOTH_CONNECT
permission.

APIs modified: listAudioPorts, listAudioPatches, getAudioPort
APIs that can only be called from system server or only convey port IDs
are not modified.

Bug: 285588444
Test: atest AudioManagerTest
Test: atest RoutingTest
Test: atest AudioCommunicationDeviceTest
Change-Id: Ia6bac184f5f39ed9d538f762ebb89bcceb44ae50
parent b2cb48a6
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -21,6 +21,15 @@ Enable configurable pre-scale absolute volume."
    bug: "302553525"
}

flag {
    name: "bluetooth_mac_address_anonymization"
    namespace: "media_audio"
    description: "\
Enable Bluetooth MAC address anonymization when reporting \
audio device descriptors to non privileged apps."
    bug: "285588444"
}

flag {
    name: "mutex_priority_inheritance"
    namespace: "media_audio"
+18 −2
Original line number Diff line number Diff line
@@ -1069,6 +1069,13 @@ AudioDeviceAddress::Tag suggestDeviceAddressTag(const AudioDeviceDescription& de
            if (mac.size() != 6) return BAD_VALUE;
            snprintf(addressBuffer, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%02X:%02X:%02X:%02X:%02X:%02X",
                    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
            // special case for anonymized mac address:
            // change anonymized bytes back from FD:FF:FF:FF to XX:XX:XX:XX
            std::string address(addressBuffer);
            if (address.compare(0, strlen("FD:FF:FF:FF"), "FD:FF:FF:FF") == 0) {
                address.replace(0, strlen("FD:FF:FF:FF"), "XX:XX:XX:XX");
            }
            strcpy(addressBuffer, address.c_str());
        } break;
        case Tag::ipv4: {
            const std::vector<uint8_t>& ipv4 = aidl.address.get<AudioDeviceAddress::ipv4>();
@@ -1125,11 +1132,20 @@ legacy2aidl_audio_device_AudioDevice(
    if (!legacyAddress.empty()) {
        switch (suggestDeviceAddressTag(aidl.type)) {
            case Tag::mac: {
                // special case for anonymized mac address:
                // change anonymized bytes so that they can be scanned as HEX bytes
                // Use '01' for LSB bits 0 and 1 as Bluetooth MAC addresses are never multicast
                // and universaly administered
                std::string address = legacyAddress;
                if (address.compare(0, strlen("XX:XX:XX:XX"), "XX:XX:XX:XX") == 0) {
                    address.replace(0, strlen("XX:XX:XX:XX"), "FD:FF:FF:FF");
                }

                std::vector<uint8_t> mac(6);
                int status = sscanf(legacyAddress.c_str(), "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX",
                int status = sscanf(address.c_str(), "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX",
                        &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
                if (status != mac.size()) {
                    ALOGE("%s: malformed MAC address: \"%s\"", __func__, legacyAddress.c_str());
                    ALOGE("%s: malformed MAC address: \"%s\"", __func__, address.c_str());
                    return unexpected(BAD_VALUE);
                }
                aidl.address = AudioDeviceAddress::make<AudioDeviceAddress::mac>(std::move(mac));
+20 −0
Original line number Diff line number Diff line
@@ -481,8 +481,28 @@ INSTANTIATE_TEST_SUITE_P(
                                 AudioDeviceAddress::make<AudioDeviceAddress::Tag::alsa>(
                                         std::vector<int32_t>{1, 2}))));

TEST(AnonymizedBluetoothAddressRoundTripTest, Legacy2Aidl2Legacy) {
    const std::vector<uint8_t> sAnonymizedAidlAddress =
            std::vector<uint8_t>{0xFD, 0xFF, 0xFF, 0xFF, 0xAB, 0xCD};
    const std::string sAnonymizedLegacyAddress = std::string("XX:XX:XX:XX:AB:CD");
    auto device = legacy2aidl_audio_device_AudioDevice(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
                                                       sAnonymizedLegacyAddress);
    ASSERT_TRUE(device.ok());
    ASSERT_EQ(AudioDeviceAddress::Tag::mac, device.value().address.getTag());
    ASSERT_EQ(sAnonymizedAidlAddress, device.value().address.get<AudioDeviceAddress::mac>());

    audio_devices_t legacyType;
    std::string legacyAddress;
    status_t status =
            aidl2legacy_AudioDevice_audio_device(device.value(), &legacyType, &legacyAddress);
    ASSERT_EQ(OK, status);
    EXPECT_EQ(legacyType, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
    EXPECT_EQ(sAnonymizedLegacyAddress, legacyAddress);
}

class AudioFormatDescriptionRoundTripTest : public testing::TestWithParam<AudioFormatDescription> {
};

TEST_P(AudioFormatDescriptionRoundTripTest, Aidl2Legacy2Aidl) {
    const auto initial = GetParam();
    auto conv = aidl2legacy_AudioFormatDescription_audio_format_t(initial);
+1 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ cc_library {
        "liblog",
        "libpermission",
        "libutils",
        "aconfig_audio_flags_c_lib",
        "android.hardware.graphics.bufferqueue@1.0",
        "android.hidl.token@1.0-utils",
        "packagemanager_aidl-cpp",
+48 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <media/AidlConversionUtil.h>
#include <android/content/AttributionSourceState.h>

#include <com_android_media_audio_flags.h>
#include <iterator>
#include <algorithm>
#include <pwd.h>
@@ -46,6 +47,7 @@ static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_A
static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE");
static const String16 sModifyAudioRouting("android.permission.MODIFY_AUDIO_ROUTING");
static const String16 sCallAudioInterception("android.permission.CALL_AUDIO_INTERCEPTION");
static const String16 sAndroidPermissionBluetoothConnect("android.permission.BLUETOOTH_CONNECT");

static String16 resolveCallingPackage(PermissionController& permissionController,
        const std::optional<String16> opPackageName, uid_t uid) {
@@ -374,6 +376,52 @@ status_t checkIMemory(const sp<IMemory>& iMemory)
    return NO_ERROR;
}

/**
 * Determines if the MAC address in Bluetooth device descriptors returned by APIs of
 * a native audio service (audio flinger, audio policy) must be anonymized.
 * MAC addresses returned to system server or apps with BLUETOOTH_CONNECT permission
 * are not anonymized.
 *
 * @param attributionSource The attribution source of the calling app.
 * @param caller string identifying the caller for logging.
 * @return true if the MAC addresses must be anonymized, false otherwise.
 */
bool mustAnonymizeBluetoothAddress(
        const AttributionSourceState& attributionSource, const String16& caller) {
    if (!com::android::media::audio::flags::bluetooth_mac_address_anonymization()) {
        return false;
    }

    uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
    if (isAudioServerOrSystemServerUid(uid)) {
        return false;
    }
    const std::optional<AttributionSourceState> resolvedAttributionSource =
            resolveAttributionSource(attributionSource);
    if (!resolvedAttributionSource.has_value()) {
        return true;
    }
    permission::PermissionChecker permissionChecker;
    return permissionChecker.checkPermissionForPreflightFromDatasource(
            sAndroidPermissionBluetoothConnect, resolvedAttributionSource.value(), caller,
            AppOpsManager::OP_BLUETOOTH_CONNECT)
                != permission::PermissionChecker::PERMISSION_GRANTED;
}

/**
 * Modifies the passed MAC address string in place for consumption by unprivileged clients.
 * the string is assumed to have a valid MAC address format.
 * the anonymzation must be kept in sync with toAnonymizedAddress() in BluetoothUtils.java
 *
 * @param address input/output the char string contining the MAC address to anonymize.
 */
void anonymizeBluetoothAddress(char *address) {
    if (address == nullptr || strlen(address) != strlen("AA:BB:CC:DD:EE:FF")) {
        return;
    }
    memcpy(address, "XX:XX:XX:XX", strlen("XX:XX:XX:XX"));
}

sp<content::pm::IPackageManagerNative> MediaPackageManager::retrievePackageManager() {
    const sp<IServiceManager> sm = defaultServiceManager();
    if (sm == nullptr) {
Loading