blob: b39458b15fb5b9d348493721f16bb3a0e76d3654 [file] [log] [blame]
/*
* 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 android.bluetooth;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothUtils.callService;
import static android.bluetooth.BluetoothUtils.logRemoteException;
import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.AttributionSource;
import android.os.RemoteException;
import com.android.bluetooth.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* This class provides APIs to control a remote AICS (Audio Input Control Service)
*
* <p>Each {@link AudioInputControl} object represents an instance of the Audio Input Control
* Service (AICS) on the remote device. A device may have multiple instances of the AICS, as
* described in the <a href="https://www.bluetooth.com/specifications/specs/aics-1-0/">Audio Input
* Control Service Specification (AICS 1.0)</a>.
*
* @see BluetoothVolumeControl#getAudioInputControlServices
* @hide
*/
@FlaggedApi(Flags.FLAG_AICS_API)
@SystemApi
public class AudioInputControl {
private static final String TAG = AudioInputControl.class.getSimpleName();
/** Unspecified Input */
public static final int AUDIO_INPUT_TYPE_UNSPECIFIED =
bluetooth.constants.AudioInputType.UNSPECIFIED;
/** Bluetooth Audio Stream */
public static final int AUDIO_INPUT_TYPE_BLUETOOTH =
bluetooth.constants.AudioInputType.BLUETOOTH;
/** Microphone */
public static final int AUDIO_INPUT_TYPE_MICROPHONE =
bluetooth.constants.AudioInputType.MICROPHONE;
/** Analog Interface */
public static final int AUDIO_INPUT_TYPE_ANALOG = bluetooth.constants.AudioInputType.ANALOG;
/** Digital Interface */
public static final int AUDIO_INPUT_TYPE_DIGITAL = bluetooth.constants.AudioInputType.DIGITAL;
/** AM/FM/XM/etc. */
public static final int AUDIO_INPUT_TYPE_RADIO = bluetooth.constants.AudioInputType.RADIO;
/** Streaming Audio Source */
public static final int AUDIO_INPUT_TYPE_STREAMING =
bluetooth.constants.AudioInputType.STREAMING;
/** Transparency/Pass-through */
public static final int AUDIO_INPUT_TYPE_AMBIENT = bluetooth.constants.AudioInputType.AMBIENT;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = {"AUDIO_INPUT_TYPE_"},
value = {
AUDIO_INPUT_TYPE_UNSPECIFIED,
AUDIO_INPUT_TYPE_BLUETOOTH,
AUDIO_INPUT_TYPE_MICROPHONE,
AUDIO_INPUT_TYPE_ANALOG,
AUDIO_INPUT_TYPE_DIGITAL,
AUDIO_INPUT_TYPE_RADIO,
AUDIO_INPUT_TYPE_STREAMING,
AUDIO_INPUT_TYPE_AMBIENT,
})
public @interface AudioInputType {}
/** Inactive */
public static final int AUDIO_INPUT_STATUS_INACTIVE =
bluetooth.constants.aics.AudioInputStatus.INACTIVE;
/** Active */
public static final int AUDIO_INPUT_STATUS_ACTIVE =
bluetooth.constants.aics.AudioInputStatus.ACTIVE;
/**
* Status is none of {@link #AUDIO_INPUT_STATUS_ACTIVE}, {@link #AUDIO_INPUT_STATUS_INACTIVE}.
* This fallback value will be used for forward compatibility, if the 3.4. Audio Input Status
* field extend its definition.
*/
public static final int AUDIO_INPUT_STATUS_UNKNOWN = -1;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = {"AUDIO_INPUT_STATUS_"},
value = {
AUDIO_INPUT_STATUS_INACTIVE,
AUDIO_INPUT_STATUS_ACTIVE,
AUDIO_INPUT_STATUS_UNKNOWN,
})
public @interface AudioInputStatus {}
/** Not Muted */
public static final int MUTE_NOT_MUTED = bluetooth.constants.aics.Mute.NOT_MUTED;
/** Muted */
public static final int MUTE_MUTED = bluetooth.constants.aics.Mute.MUTED;
/**
* Disabled
*
* <p>Mute command are disabled by the server. For example with a local privacy switch.
*/
public static final int MUTE_DISABLED = bluetooth.constants.aics.Mute.DISABLED;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = {"MUTE_"},
value = {
MUTE_NOT_MUTED,
MUTE_MUTED,
MUTE_DISABLED,
})
public @interface Mute {}
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = {"MUTE_"},
value = {
MUTE_NOT_MUTED,
MUTE_MUTED,
})
public @interface MuteSettable {}
/**
* Manual Only
*
* <p>Gain adjustments are made manually through {@link #setGainSetting}.
*
* <p>The server cannot be switched to automatic gain.
*/
public static final int GAIN_MODE_MANUAL_ONLY = bluetooth.constants.aics.GainMode.MANUAL_ONLY;
/**
* Automatic Only
*
* <p>Gain adjustments are automatic and calls to {@link #setGainSetting} are ignored.
*
* <p>The server cannot be switched to manual gain.
*/
public static final int GAIN_MODE_AUTOMATIC_ONLY =
bluetooth.constants.aics.GainMode.AUTOMATIC_ONLY;
/**
* Manual
*
* <p>Gain adjustments are made manually through {@link #setGainSetting}.
*
* <p>The server supports switching between manual and automatic gain mode.
*/
public static final int GAIN_MODE_MANUAL = bluetooth.constants.aics.GainMode.MANUAL;
/**
* Automatic
*
* <p>Gain adjustments are automatic and calls to {@link #setGainSetting} are ignored.
*
* <p>The server supports switching between manual and automatic gain mode.
*/
public static final int GAIN_MODE_AUTOMATIC = bluetooth.constants.aics.GainMode.AUTOMATIC;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = {"GAIN_MODE_"},
value = {
GAIN_MODE_MANUAL_ONLY,
GAIN_MODE_AUTOMATIC_ONLY,
GAIN_MODE_MANUAL,
GAIN_MODE_AUTOMATIC,
})
public @interface GainMode {}
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = {"GAIN_MODE_"},
value = {
GAIN_MODE_MANUAL,
GAIN_MODE_AUTOMATIC,
})
public @interface GainModeSettable {}
/** Local identifier of the AICS */
private final int mInstanceId;
private final IBluetoothVolumeControl mService;
private final BluetoothDevice mDevice;
private final AttributionSource mAttributionSource;
private final CallbackWrapper<AudioInputCallback, IBluetoothVolumeControl> mCallbackWrapper;
AudioInputControl(
@NonNull BluetoothDevice device,
int id,
@NonNull IBluetoothVolumeControl service,
@NonNull AttributionSource source) {
mDevice = requireNonNull(device);
mInstanceId = id;
mService = requireNonNull(service);
mAttributionSource = requireNonNull(source);
mCallbackWrapper =
new CallbackWrapper<AudioInputCallback, IBluetoothVolumeControl>(
this::registerCallbackFn, this::unregisterCallbackFn);
}
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
private void registerCallbackFn(IBluetoothVolumeControl vcs) {
try {
vcs.registerAudioInputControlCallback(
mAttributionSource, mDevice, mInstanceId, mCallback);
} catch (RemoteException e) {
logRemoteException(TAG, e);
}
}
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
private void unregisterCallbackFn(IBluetoothVolumeControl vcs) {
try {
vcs.unregisterAudioInputControlCallback(
mAttributionSource, mDevice, mInstanceId, mCallback);
} catch (RemoteException e) {
logRemoteException(TAG, e);
}
}
private final IAudioInputCallback mCallback =
new IAudioInputCallback.Stub() {
@Override
@RequiresNoPermission
public void onDescriptionChanged(String description) {
mCallbackWrapper.forEach(cb -> cb.onDescriptionChanged(description));
}
@Override
@RequiresNoPermission
public void onStatusChanged(int status) {
mCallbackWrapper.forEach(cb -> cb.onAudioInputStatusChanged(status));
}
@Override
@RequiresNoPermission
public void onStateChanged(int gainSetting, int mute, int gainMode) {
mCallbackWrapper.forEach(cb -> cb.onGainSettingChanged(gainSetting));
mCallbackWrapper.forEach(cb -> cb.onMuteChanged(mute));
mCallbackWrapper.forEach(cb -> cb.onGainModeChanged(gainMode));
}
@Override
@RequiresNoPermission
public void onSetGainSettingFailed() {
mCallbackWrapper.forEach(cb -> cb.onSetGainSettingFailed());
}
@Override
@RequiresNoPermission
public void onSetGainModeFailed() {
mCallbackWrapper.forEach(cb -> cb.onSetGainModeFailed());
}
@Override
@RequiresNoPermission
public void onSetMuteFailed() {
mCallbackWrapper.forEach(cb -> cb.onSetMuteFailed());
}
};
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
static List<AudioInputControl> getAudioInputControlServices(
@NonNull IBluetoothVolumeControl service,
@NonNull AttributionSource source,
@NonNull BluetoothDevice device) {
requireNonNull(service);
requireNonNull(source);
requireNonNull(device);
int numberOfAics = 0;
try {
numberOfAics = service.getNumberOfAudioInputControlServices(source, device);
} catch (RemoteException e) {
logRemoteException(TAG, e);
}
return IntStream.range(0, numberOfAics)
.mapToObj(i -> new AudioInputControl(device, i, service, source))
.collect(Collectors.toList());
}
/**
* This callback is invoked when the remote AICS notifies a local client of a value changed. It
* can also be invoked when a locally initiated operation failed.
*/
public interface AudioInputCallback {
/** see {@link #setDescription(String)} */
default void onDescriptionChanged(@NonNull String description) {}
/** see {@link #getStatus()} */
default void onAudioInputStatusChanged(@AudioInputStatus int status) {}
/** see {@link #setGainSetting(int)} */
default void onGainSettingChanged(int gainSetting) {}
/** see {@link #setGainSetting(int)} */
default void onSetGainSettingFailed() {}
/** see {@link #setMute(int)} */
default void onMuteChanged(@Mute int mute) {}
/** see {@link #setMute(int)} */
default void onSetMuteFailed() {}
/** see {@link #setGainMode(int)} */
default void onGainModeChanged(@GainMode int gainMode) {}
/** see {@link #setGainMode(int)} */
default void onSetGainModeFailed() {}
}
/**
* Register an {@link AudioInputCallback} to receive callbacks when the state of the AICS on the
* remote device changes.
*
* <p>Repeated registration of the same callback object will have no effect after the first call
* to this method, even when the executor is different. API caller must call {@link
* #unregisterCallback(AudioInputCallback)} with the same callback object before registering it
* again.
*
* <p>Callbacks are automatically unregistered when the application process goes away.
*
* @param executor an {@link Executor} to execute given callback
* @param callback user implementation of the {@link AudioInputCallback}
* @throws IllegalArgumentException if callback is already registered
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public void registerCallback(
@NonNull @CallbackExecutor Executor executor, @NonNull AudioInputCallback callback) {
mCallbackWrapper.registerCallback(mService, callback, executor);
}
/**
* Unregister the {@link AudioInputCallback}.
*
* <p>The same {@link AudioInputCallback} object used when calling {@link
* #registerCallback(Executor, AudioInputCallback)} must be used.
*
* <p>Callbacks are automatically unregistered when the application process goes away.
*
* @param callback user implementation of the {@link AudioInputCallback}
* @throws IllegalArgumentException when no callback is registered
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public void unregisterCallback(@NonNull AudioInputCallback callback) {
mCallbackWrapper.unregisterCallback(mService, callback);
}
/**
* Gets the Audio Input Type.
*
* <p>This reflects the source of audio for the audio input described by this AICS. The
* description may optionally further describe the Audio Input Type with values such as
* Microphone, HDMI, etc…
*
* @return The Audio Input Type as defined in AICS 1.0 - 3.3.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @AudioInputType int getAudioInputType() {
return callService(
mService,
s -> s.getAudioInputType(mAttributionSource, mDevice, mInstanceId),
bluetooth.constants.AudioInputType.UNSPECIFIED);
}
/**
* Gets the unit of the gain setting.
*
* <p>It reflects the size of a single increment or decrement of {@link #setGainSetting} in 0.1
* decibel units.
*
* @return The Gain Setting Units as defined in AICS 1.0 - 3.2.1.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @IntRange(from = 0, to = 0xFF) int getGainSettingUnit() {
return callService(
mService,
s -> s.getAudioInputGainSettingUnit(mAttributionSource, mDevice, mInstanceId),
0);
}
/**
* Gets the minimum value for the gain setting.
*
* <p>The value return is relative to {@link #getGainSettingUnit} in 0.1 decibel units.
*
* @return The minimum Gain Setting as defined in AICS 1.0 - 3.2.2.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @IntRange(from = -128, to = 127) int getGainSettingMin() {
return callService(
mService,
s -> s.getAudioInputGainSettingMin(mAttributionSource, mDevice, mInstanceId),
0);
}
/**
* Gets the maximum value for the gain setting.
*
* <p>The value return is relative to {@link #getGainSettingUnit} in 0.1 decibel units.
*
* @return The maximum Gain Setting as defined in AICS 1.0 - 3.2.3.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @IntRange(from = -128, to = 127) int getGainSettingMax() {
return callService(
mService,
s -> s.getAudioInputGainSettingMax(mAttributionSource, mDevice, mInstanceId),
0);
}
/**
* Gets the description.
*
* <p>Register an {@link AudioInputCallback} to be notified via {@link
* AudioInputCallback#onDescriptionChanged} when the description changes.
*
* <p>This describes the AICS. For example, if a device instantiated a service for both
* “Bluetooth” and “Line In” audio inputs, then the description value would be set to
* “Bluetooth” on one service and “Line In” on the other service. If multiple Bluetooth audio
* inputs are represented, the server may set the Audio Input Description to the remote source’s
* name, a string representing the content type, the content control server name, etc. The value
* is a UTF-8 string of zero or more characters.
*
* @return The description as defined in AICS 1.0 - 3.6.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @NonNull String getDescription() {
return callService(
mService,
s -> s.getAudioInputDescription(mAttributionSource, mDevice, mInstanceId),
"");
}
/**
* Checks whether the description is writable as defined in AICS 1.0 - 3.6.
*
* @return true if the description can be written to, false otherwise.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public boolean isDescriptionWritable() {
return callService(
mService,
s -> s.isAudioInputDescriptionWritable(mAttributionSource, mDevice, mInstanceId),
false);
}
/**
* Sets the description as defined in AICS 1.0 - 3.6.
*
* <p>The operation will fail if the description is not writable. This can be verified with
* {@link #isDescriptionWritable}
*
* <p>Register an {@link AudioInputCallback} to be notified via {@link
* AudioInputCallback#onDescriptionChanged} when the description change is applied on remote
* device.
*
* @param description The description of the AICS.
* @return true if the operation is successfully initiated, false otherwise.
* @throws IllegalStateException if the description is not writable
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public boolean setDescription(@NonNull String description) {
requireNonNull(description);
return callService(
mService,
s ->
s.setAudioInputDescription(
mAttributionSource, mDevice, mInstanceId, description),
false);
}
/**
* Gets the Audio Input Status.
*
* <p>Register an {@link AudioInputCallback} to be notified via {@link
* AudioInputCallback#onAudioInputStatusChanged} when the status changes.
*
* @return The Audio Input Status as defined in AICS 1.0 - 3.4.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @AudioInputStatus int getAudioInputStatus() {
return callService(
mService,
s -> s.getAudioInputStatus(mAttributionSource, mDevice, mInstanceId),
(int) bluetooth.constants.aics.AudioInputStatus.INACTIVE);
}
/**
* Gets the gain setting.
*
* <p>Register an {@link AudioInputCallback} to be notified via {@link
* AudioInputCallback#onGainSettingChanged} when the gain setting changes.
*
* <p>The value return is relative to {@link #getGainSettingUnit} in 0.1 decibel units.
*
* @return The current gain setting as defined in AICS 1.0 - 2.2.1.1.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @IntRange(from = -128, to = 127) int getGainSetting() {
return callService(
mService,
s -> s.getAudioInputGainSetting(mAttributionSource, mDevice, mInstanceId),
0);
}
/**
* Sets the gain setting as defined in AICS 1.0 - 3.5.2.1.
*
* <p>The operation will fail if the current gain mode is {@link #AUTOMATIC} or {@link
* #AUTOMATIC_ONLY}.
*
* <p>Register an {@link AudioInputControl.AudioInputCallback} to be notified via
*
* <ul>
* <li>{@link AudioInputCallback#onGainSettingChanged()} when the gain setting is changed by
* the remote device.
* <li>{@link AudioInputCallback#onSetGainSettingFailed} if the gain setting cannot be set.
* </ul>
*
* The gain setting is a signed value for which a single increment or decrement should result in
* a corresponding increase or decrease of the input amplitude by the value of the gain setting
* unit (see {@link #getGainSettingUnit()}). A gain setting value of 0 should result in no
* change to the input’s original amplitude.
*
* @param gainSetting The desired gain setting value. Refer to {@link #getGainSettingMin()} and
* {@link #getGainSettingMax()} for the allowed range. Refer to {@link
* #getGainSettingUnit()} to knows how much decibel this represents.
* @return true if the operation is successfully initiated, false otherwise. The callback {@link
* AudioInputCallback#onSetGainSettingFailed()} will not be call if false is returned
* @throws IllegalStateException if the gain mode is {@link #AUTOMATIC} or {@link
* #AUTOMATIC_ONLY}
* @throws IllegalArgumentException if the gain setting is not in range
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public boolean setGainSetting(@IntRange(from = -128, to = 127) int gainSetting) {
return callService(
mService,
s ->
s.setAudioInputGainSetting(
mAttributionSource, mDevice, mInstanceId, gainSetting),
false);
}
/**
* Gets the gain mode.
*
* <p>Register an {@link AudioInputCallback} to be notified via {@link
* AudioInputCallback#onGainModeChanged} when the gain mode changes.
*
* @return The current gain mode as defined in AICS 1.0 - 2.2.1.3.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @GainMode int getGainMode() {
return callService(
mService,
s -> s.getAudioInputGainMode(mAttributionSource, mDevice, mInstanceId),
(int) bluetooth.constants.aics.GainMode.AUTOMATIC_ONLY);
}
/**
* Sets the gain mode as defined in AICS 1.0 - 3.5.2.4/5.
*
* <p>The operation will fail if the current gain mode is {@link #MANUAL_ONLY} or {@link
* #AUTOMATIC_ONLY}.
*
* <p>Register an {@link AudioInputControl.AudioInputCallback} to be notified via
*
* <ul>
* <li>{@link AudioInputCallback#onGainModeChanged()} when the gain setting is changed by the
* remote device.
* <li>{@link AudioInputCallback#onSetGainModeFailed} if the gain mode cannot be set.
* </ul>
*
* @param gainMode The desired gain mode
* @return true if the operation is successfully initiated, false otherwise. The callback {@link
* AudioInputCallback#onSetGainModeFailed()} will not be call if false is returned
* @throws IllegalStateException if the gain mode is {@link #MANUAL_ONLY} or {@link
* #AUTOMATIC_ONLY}
* @throws IllegalArgumentException if the gain mode value is invalid.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public boolean setGainMode(@GainModeSettable int gainMode) {
if (gainMode != GAIN_MODE_MANUAL && gainMode != GAIN_MODE_AUTOMATIC) {
throw new IllegalArgumentException("Illegal GainMode value: " + gainMode);
}
return callService(
mService,
s -> s.setAudioInputGainMode(mAttributionSource, mDevice, mInstanceId, gainMode),
false);
}
/**
* Gets the mute state.
*
* <p>Register an {@link AudioInputCallback} to be notified via {@link
* AudioInputCallback#onMuteChanged} when the mute state changes.
*
* @return The current mute state as defined in AICS 1.0 - 2.2.1.2.
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @Mute int getMute() {
return callService(
mService,
s -> s.getAudioInputMute(mAttributionSource, mDevice, mInstanceId),
(int) bluetooth.constants.aics.Mute.DISABLED);
}
/**
* Sets the mute state as defined in AICS 1.0 - 3.5.2.2/3.
*
* <p>The operation will fail if the current mute state is {@link #MUTE_DISABLED}.
*
* <p>Register an {@link AudioInputControl.AudioInputCallback} to be notified via
*
* <ul>
* <li>{@link AudioInputCallback#onMuteChanged()} when the mute state is changed by the remote
* device.
* <li>{@link AudioInputCallback#onSetMuteFailed} if the mute state cannot be set.
* </ul>
*
* @param mute the new mute state.
* @return true on success, false otherwise.
* @throws IllegalStateException if the mute state is {@link #MUTE_DISABLED}
* @throws IllegalArgumentException if the provided {@code mute} is not valid
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public boolean setMute(@MuteSettable int mute) {
if (mute != MUTE_NOT_MUTED && mute != MUTE_MUTED) {
throw new IllegalArgumentException("Illegal mute value: " + mute);
}
return callService(
mService,
s -> s.setAudioInputMute(mAttributionSource, mDevice, mInstanceId, mute),
false);
}
}