/* * Copyright (C) 2023 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.car.audio.hal; import static android.media.audio.common.AudioDeviceDescription.CONNECTION_BUS; import static android.media.audio.common.AudioDeviceType.IN_DEVICE; import static android.media.audio.common.AudioDeviceType.OUT_DEVICE; import static android.media.audio.common.AudioGainMode.JOINT; import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; import android.annotation.NonNull; import android.media.audio.common.AudioDevice; import android.media.audio.common.AudioGain; import android.media.audio.common.AudioPort; import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; import com.android.internal.util.Preconditions; import java.util.Objects; /** * Audio Device info received from HAL as part of dynamic gain stage configration */ public final class HalAudioDeviceInfo { private final int mId; private final String mName; private final AudioGain mAudioGain; private final int mType; private final String mConnection; private final String mAddress; private static final int AUDIO_PORT_EXT_DEVICE = 1; public HalAudioDeviceInfo(AudioPort port) { Objects.requireNonNull(port, "Audio port can not be null"); Preconditions.checkArgument(port.ext.getTag() == AUDIO_PORT_EXT_DEVICE, "Invalid audio port ext setting: %d", port.ext.getTag()); AudioDevice device = Objects.requireNonNull(port.ext.getDevice().device, "Audio device can not be null"); checkIfAudioDeviceIsValidOutputBus(device); mId = port.id; mName = port.name; mAudioGain = getAudioGain(port.gains); mType = device.type.type; mConnection = device.type.connection; mAddress = device.address.getId(); } public int getId() { return mId; } public String getName() { return mName; } public int getGainMinValue() { return mAudioGain.minValue; } public int getGainMaxValue() { return mAudioGain.maxValue; } public int getGainDefaultValue() { return mAudioGain.defaultValue; } public int getGainStepValue() { return mAudioGain.stepValue; } public int getType() { return mType; } public String getConnection() { return mConnection; } public String getAddress() { return mAddress; } public boolean isOutputDevice() { return mType == OUT_DEVICE; } public boolean isInputDevice() { return mType == IN_DEVICE; } @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) @Override public String toString() { return new StringBuilder() .append("{mId: ").append(mId).append(", mName: ").append(mName) .append(", mAudioGain: ").append(Objects.toString(mAudioGain)) .append(", mType: ").append(mType).append(", mConnection: ").append(mConnection) .append(", mAddress: ").append(mAddress).append("}").toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof HalAudioDeviceInfo)) { return false; } HalAudioDeviceInfo rhs = (HalAudioDeviceInfo) o; // mId is not reliable until Audio HAL migrates to AIDL return mType == rhs.mType && mName.equals(rhs.mName) && mConnection.equals(rhs.mConnection) && mAddress.equals(rhs.mAddress) && Objects.equals(mAudioGain, rhs.mAudioGain); } @Override public int hashCode() { // mId is not reliable until Audio HAL migrates to AIDL return Objects.hash(mName, mAudioGain, mType, mConnection, mAddress); } private void checkIfAudioDeviceIsValidOutputBus(AudioDevice device) { Preconditions.checkArgument((device.type.type == OUT_DEVICE) || (device.type.type == IN_DEVICE), "Invalid audio device type (expecting IN/OUT_DEVICE): %d", device.type.type); Preconditions.checkArgument(device.type.connection.equals(CONNECTION_BUS), "Invalid audio device connection (expecting CONNECTION_BUS): %s", device.type.connection); Preconditions.checkStringNotEmpty(device.address.getId(), "Audio device address cannot be empty"); } private static AudioGain getAudioGain(@NonNull AudioGain[] gains) { Objects.requireNonNull(gains, "Audio gains can not be null"); Preconditions.checkArgument(gains.length > 0, "Audio port must have gains defined"); for (int index = 0; index < gains.length; index++) { AudioGain gain = Objects.requireNonNull(gains[index], "Audio gain can not be null"); if (gain.mode == JOINT) { return checkAudioGainConfiguration(gain); } } throw new IllegalStateException("Audio port does not have a valid audio gain"); } private static AudioGain checkAudioGainConfiguration(AudioGain gain) { Preconditions.checkArgument(gain.maxValue >= gain.minValue, "Max gain %d is lower than min gain %d", gain.maxValue, gain.minValue); Preconditions.checkArgument((gain.defaultValue >= gain.minValue) && (gain.defaultValue <= gain.maxValue), "Default gain %d not in range (%d,%d)", gain.defaultValue, gain.minValue, gain.maxValue); Preconditions.checkArgument(gain.stepValue > 0, "Gain step value must be greater than zero: %d", gain.stepValue); Preconditions.checkArgument( ((gain.maxValue - gain.minValue) % gain.stepValue) == 0, "Gain step value %d greater than min gain to max gain range %d", gain.stepValue, gain.maxValue - gain.minValue); Preconditions.checkArgument( ((gain.defaultValue - gain.minValue) % gain.stepValue) == 0, "Gain step value %d greater than min gain to default gain range %d", gain.stepValue, gain.defaultValue - gain.minValue); return gain; } }