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

Commit 2e76b5f3 authored by William Escande's avatar William Escande
Browse files

AICS: callback registration

Bug: 372328699
Flag: com.android.bluetooth.flags.aics_api
Test: atest CtsBluetoothTestCases
Change-Id: I16f88f5843073fda10c59a414d24c04b1e2181b1
parent 88ba6914
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ filegroup {
    name: "Bluetooth-binder-aidl",
    srcs: [
        ":framework-bluetooth-updatable-exported-aidl-sources",
        "android/bluetooth/IAudioInputCallback.aidl",
        "android/bluetooth/IBluetooth.aidl",
        "android/bluetooth/IBluetoothA2dp.aidl",
        "android/bluetooth/IBluetoothA2dpSink.aidl",
+25 −0
Original line number Diff line number Diff line
/*
 *  Copyright 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 android.bluetooth;

/**
 * Callback definitions for interacting with @see AudioInputControl
 *
 * @hide
 */
oneway interface IAudioInputCallback {
}
+9 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
package android.bluetooth;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.IAudioInputCallback;
import android.bluetooth.IBluetoothVolumeControlCallback;
import android.content.AttributionSource;

@@ -72,4 +73,12 @@ interface IBluetoothVolumeControl {
    void unregisterCallback(in IBluetoothVolumeControlCallback callback, in AttributionSource attributionSource);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    void notifyNewRegisteredCallback(in IBluetoothVolumeControlCallback callback, in AttributionSource attributionSource);

    // ---------------------
    // AICS related methods:

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    void registerAudioInputControlCallback(in AttributionSource attributionSource, in BluetoothDevice device, int instanceId, in IAudioInputCallback callback);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    void unregisterAudioInputControlCallback(in AttributionSource attributionSource, in BluetoothDevice device, int instanceId, in IAudioInputCallback callback);
}
+36 −0
Original line number Diff line number Diff line
@@ -16,9 +16,13 @@

package com.android.bluetooth.vc;

import static com.android.bluetooth.Utils.RemoteExceptionIgnoringConsumer;

import static java.util.Objects.requireNonNull;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.IAudioInputCallback;
import android.os.RemoteCallbackList;
import android.util.Log;

import com.android.bluetooth.btservice.ProfileService;
@@ -71,6 +75,28 @@ class VolumeControlInputDescriptor {
        int mGainSettingsMin = 0;

        String mDescription = "";

        private final RemoteCallbackList<IAudioInputCallback> mCallbacks =
                new RemoteCallbackList<>();

        void registerCallback(IAudioInputCallback callback) {
            mCallbacks.register(callback);
        }

        void unregisterCallback(IAudioInputCallback callback) {
            mCallbacks.unregister(callback);
        }

        @SuppressWarnings("UnusedMethod")
        synchronized void broadcast(
                String logAction, RemoteExceptionIgnoringConsumer<IAudioInputCallback> action) {
            final int itemCount = mCallbacks.beginBroadcast();
            Log.d(TAG, "Broadcasting " + logAction + "() to " + itemCount + " receivers.");
            for (int i = 0; i < itemCount; i++) {
                action.accept(mCallbacks.getBroadcastItem(i));
            }
            mCallbacks.finishBroadcast();
        }
    }

    int size() {
@@ -85,6 +111,16 @@ class VolumeControlInputDescriptor {
        return true;
    }

    void registerCallback(int id, IAudioInputCallback callback) {
        if (!isValidId(id)) return;
        mVolumeInputs[id].registerCallback(callback);
    }

    void unregisterCallback(int id, IAudioInputCallback callback) {
        if (!isValidId(id)) return;
        mVolumeInputs[id].unregisterCallback(callback);
    }

    void setStatus(int id, int status) {
        if (!isValidId(id)) return;
        mVolumeInputs[id].mStatus = status;
+79 −0
Original line number Diff line number Diff line
@@ -32,9 +32,11 @@ import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElseGet;

import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IAudioInputCallback;
import android.bluetooth.IBluetoothCsipSetCoordinator;
import android.bluetooth.IBluetoothLeAudio;
import android.bluetooth.IBluetoothVolumeControl;
@@ -80,6 +82,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;

public class VolumeControlService extends ProfileService {
    private static final String TAG = VolumeControlService.class.getSimpleName();
@@ -1684,6 +1687,82 @@ public class VolumeControlService extends ProfileService {
            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
            postAndWait(service.mHandler, () -> service.notifyNewRegisteredCallback(callback));
        }

        private void validateBluetoothDevice(BluetoothDevice device) {
            requireNonNull(device);
            String address = device.getAddress();
            if (!BluetoothAdapter.checkBluetoothAddress(address)) {
                throw new IllegalArgumentException("Invalid device address: " + address);
            }
        }

        @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
        private <R> R aicsWrapper(
                AttributionSource source,
                BluetoothDevice device,
                Function<VolumeControlInputDescriptor, R> fn,
                R defaultValue) {
            validateBluetoothDevice(device);

            VolumeControlService service = getService(source);
            if (service == null) {
                return defaultValue;
            }

            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);

            VolumeControlInputDescriptor inputs = service.mAudioInputs.get(device);
            if (inputs == null) {
                Log.w(TAG, "No audio inputs for " + device);
                return defaultValue;
            }

            return fn.apply(inputs);
        }

        @Override
        public void registerAudioInputControlCallback(
                AttributionSource source,
                BluetoothDevice device,
                int instanceId,
                IAudioInputCallback callback) {
            requireNonNull(callback);
            Log.d(
                    TAG,
                    "registerAudioInputControlCallback("
                            + (device + ", " + instanceId + ", " + callback)
                            + ")");
            aicsWrapper(
                    source,
                    device,
                    i -> {
                        i.registerCallback(instanceId, callback);
                        return null;
                    },
                    null);
        }

        @Override
        public void unregisterAudioInputControlCallback(
                AttributionSource source,
                BluetoothDevice device,
                int instanceId,
                IAudioInputCallback callback) {
            requireNonNull(callback);
            Log.d(
                    TAG,
                    "unregisterAudioInputControlCallback("
                            + (device + ", " + instanceId + ", " + callback)
                            + ")");
            aicsWrapper(
                    source,
                    device,
                    i -> {
                        i.unregisterCallback(instanceId, callback);
                        return null;
                    },
                    null);
        }
    }

    @Override