blob: 98b6d97ec9923e721ee43a55fc4066be81d13ecf [file] [log] [blame]
/*
* Copyright (C) 2018 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.media.audiopolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @hide
* A class to encapsulate a collection of {@link AudioProductStrategy}.
* Provides helper functions to easily retrieve the {@link AudioAttributes} for a given product
* strategy or legacy stream type.
*/
@SystemApi
public final class AudioProductStrategies implements Iterable<AudioProductStrategy>, Parcelable {
private final ArrayList<AudioProductStrategy> mAudioProductStrategyList;
private static final String TAG = "AudioProductStrategies";
public AudioProductStrategies() {
ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>();
int status = native_list_audio_product_strategies(apsList);
if (status != AudioSystem.SUCCESS) {
Log.w(TAG, ": createAudioProductStrategies failed");
}
mAudioProductStrategyList = apsList;
}
private AudioProductStrategies(ArrayList<AudioProductStrategy> audioProductStrategy) {
mAudioProductStrategyList = audioProductStrategy;
}
/**
* @hide
* @return number of {@link AudioProductStrategy} objects
*/
@SystemApi
public int size() {
return mAudioProductStrategyList.size();
}
/**
* @hide
* @return the matching {@link AudioProductStrategy} objects with the given id,
* null object if not found.
*/
@SystemApi
public @Nullable AudioProductStrategy getById(int productStrategyId) {
for (final AudioProductStrategy avg : this) {
if (avg.getId() == productStrategyId) {
return avg;
}
}
Log.e(TAG, ": invalid product strategy id: " + productStrategyId + " requested");
return null;
}
/**
* Returns an {@link Iterator}
*/
@Override
public Iterator<AudioProductStrategy> iterator() {
return mAudioProductStrategyList.iterator();
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AudioProductStrategies that = (AudioProductStrategies) o;
return mAudioProductStrategyList.equals(that.mAudioProductStrategyList);
}
/**
* @hide
* @param aps {@link AudioProductStrategy} (which is the generalisation of Car Audio Usage /
* legacy routing_strategy linked to {@link AudioAttributes#getUsage()} )
* @return the {@link AudioAttributes} relevant for the given product strategy.
* If none is found, it builds the default attributes.
* TODO: shall the helper collection be able to identify the platform default?
*/
@SystemApi
@NonNull
public AudioAttributes getAudioAttributesForProductStrategy(@NonNull AudioProductStrategy aps) {
Preconditions.checkNotNull(aps, "AudioProductStrategy must not be null");
for (final AudioProductStrategy audioProductStrategy : this) {
if (audioProductStrategy.equals(aps)) {
return audioProductStrategy.getAudioAttributes();
}
}
return new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.setUsage(AudioAttributes.USAGE_UNKNOWN).build();
}
/**
* @hide
* @param streamType legacy stream type used for volume operation only
* @return the {@link AudioAttributes} relevant for the given streamType.
* If none is found, it builds the default attributes.
*/
@SystemApi
public @NonNull AudioAttributes getAudioAttributesForLegacyStreamType(int streamType) {
for (final AudioProductStrategy productStrategy : this) {
AudioAttributes aa = productStrategy.getAudioAttributesForLegacyStreamType(streamType);
if (aa != null) {
return aa;
}
}
return new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.setUsage(AudioAttributes.USAGE_UNKNOWN).build();
}
/**
* @hide
* @param aa the {@link AudioAttributes} for which stream type is requested
* @return the legacy stream type relevant for the given {@link AudioAttributes}.
* If the product strategy is not associated to any stream, it returns
* {@link AudioSystem#STREAM_MUSIC}.
* If no product strategy supports the stream type, it returns
* {@link AudioSystem#STREAM_MUSIC}.
*/
@SystemApi
public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioProductStrategy productStrategy : this) {
if (productStrategy.supportsAudioAttributes(aa)) {
int streamType = productStrategy.getLegacyStreamTypeForAudioAttributes(aa);
if (streamType == AudioSystem.STREAM_DEFAULT) {
Log.w(TAG, "Attributes " + aa.toString() + " ported by strategy "
+ productStrategy.name() + " has no stream type associated, "
+ "DO NOT USE STREAM TO CONTROL THE VOLUME");
return AudioSystem.STREAM_MUSIC;
}
return streamType;
}
}
return AudioSystem.STREAM_MUSIC;
}
/**
* @hide
* @param aa the {@link AudioAttributes} to be considered
* @return {@link AudioProductStrategy} supporting the given {@link AudioAttributes}.
* null is returned if no match with given attributes.
*/
@SystemApi
@Nullable
public AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "attributes must not be null");
int productStrategyId = native_get_product_strategies_from_audio_attributes(aa);
if (productStrategyId < 0) {
Log.w(TAG, "no strategy found for Attributes " + aa.toString());
return null;
}
return getById(productStrategyId);
}
/**
* @hide
* @param attributes the {@link AudioAttributes} to be considered
* @return volume group associated to the given {@link AudioAttributes}.
* If no group supports the given {@link AudioAttributes}, it returns the volume group
* for the default attributes.
* If no group supports the default attributes, it returns {@link #DEFAULT_VOLUME_GROUP}
*/
@SystemApi
public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
Preconditions.checkNotNull(attributes, "attributes must not be null");
int volumeGroupId = getVolumeGroupIdForAttributesInt(attributes);
if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
return volumeGroupId;
}
// The default volume group is the one hosted by default product strategy, i.e.
// supporting Default Attributes
return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
}
/**
* @hide
* @param streamType to be considered
* @return volume group associated to the given stream type.
*/
@SystemApi
public int getVolumeGroupIdForLegacyStreamType(int streamType) {
for (final AudioProductStrategy productStrategy : this) {
int volumeGroupId = productStrategy.getVolumeGroupIdForLegacyStreamType(streamType);
if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
return volumeGroupId;
}
}
// The default volume group is the one hosted by default product strategy, i.e.
// supporting Default Attributes
return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(size());
for (final AudioProductStrategy productStrategy : this) {
productStrategy.writeToParcel(dest, flags);
}
}
/**
* @param attributes to be considered
* @return volume group associated to the given {@link AudioAttributes}.
*/
private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
Preconditions.checkNotNull(attributes, "attributes must not be null");
for (final AudioProductStrategy productStrategy : this) {
int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes);
if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
return volumeGroupId;
}
}
return AudioVolumeGroups.DEFAULT_VOLUME_GROUP;
}
public static final @android.annotation.NonNull Parcelable.Creator<AudioProductStrategies> CREATOR =
new Parcelable.Creator<AudioProductStrategies>() {
@Override
public AudioProductStrategies createFromParcel(@NonNull Parcel in) {
ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>();
int size = in.readInt();
for (int index = 0; index < size; index++) {
apsList.add(AudioProductStrategy.CREATOR.createFromParcel(in));
}
return new AudioProductStrategies(apsList);
}
@Override
public @NonNull AudioProductStrategies[] newArray(int size) {
return new AudioProductStrategies[size];
}
};
private static native int native_list_audio_product_strategies(
ArrayList<AudioProductStrategy> strategies);
private static native int native_get_product_strategies_from_audio_attributes(
AudioAttributes attributes);
}