1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.audiopolicy; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.media.AudioAttributes; 23 import android.media.AudioSystem; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.util.Log; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.internal.util.Preconditions; 30 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.List; 34 import java.util.Objects; 35 36 /** 37 * A class to create the association between different playback attributes 38 * (e.g. media, mapping direction) to a single volume control. 39 * @hide 40 */ 41 @SystemApi 42 public final class AudioVolumeGroup implements Parcelable { 43 private static final String TAG = "AudioVolumeGroup"; 44 /** 45 * Volume group value to use when introspection API fails. 46 */ 47 public static final int DEFAULT_VOLUME_GROUP = -1; 48 49 /** 50 * Unique identifier of a volume group. 51 */ 52 private int mId; 53 /** 54 * human-readable name of this volume group. 55 */ 56 private final @NonNull String mName; 57 58 private final AudioAttributes[] mAudioAttributes; 59 private int[] mLegacyStreamTypes; 60 61 private static final Object sLock = new Object(); 62 63 @GuardedBy("sLock") 64 private static List<AudioVolumeGroup> sAudioVolumeGroups; 65 66 /** 67 * @hide 68 * @return the List of AudioVolumeGroup discovered from platform configuration file. 69 */ 70 @NonNull getAudioVolumeGroups()71 public static List<AudioVolumeGroup> getAudioVolumeGroups() { 72 if (sAudioVolumeGroups == null) { 73 synchronized (sLock) { 74 if (sAudioVolumeGroups == null) { 75 sAudioVolumeGroups = initializeAudioVolumeGroups(); 76 } 77 } 78 } 79 return sAudioVolumeGroups; 80 } 81 initializeAudioVolumeGroups()82 private static List<AudioVolumeGroup> initializeAudioVolumeGroups() { 83 ArrayList<AudioVolumeGroup> avgList = new ArrayList<>(); 84 int status = native_list_audio_volume_groups(avgList); 85 if (status != AudioSystem.SUCCESS) { 86 Log.w(TAG, ": listAudioVolumeGroups failed"); 87 } 88 return avgList; 89 } 90 native_list_audio_volume_groups( ArrayList<AudioVolumeGroup> groups)91 private static native int native_list_audio_volume_groups( 92 ArrayList<AudioVolumeGroup> groups); 93 94 /** 95 * @param name of the volume group 96 * @param id of the volume group 97 * @param legacyStreamTypes of volume group 98 */ AudioVolumeGroup(@onNull String name, int id, @NonNull AudioAttributes[] audioAttributes, @NonNull int[] legacyStreamTypes)99 AudioVolumeGroup(@NonNull String name, int id, 100 @NonNull AudioAttributes[] audioAttributes, 101 @NonNull int[] legacyStreamTypes) { 102 Preconditions.checkNotNull(name, "name must not be null"); 103 Preconditions.checkNotNull(audioAttributes, "audioAttributes must not be null"); 104 Preconditions.checkNotNull(legacyStreamTypes, "legacyStreamTypes must not be null"); 105 mName = name; 106 mId = id; 107 mAudioAttributes = audioAttributes; 108 mLegacyStreamTypes = legacyStreamTypes; 109 } 110 111 @Override equals(@ullable Object o)112 public boolean equals(@Nullable Object o) { 113 if (this == o) return true; 114 if (o == null || getClass() != o.getClass()) return false; 115 116 AudioVolumeGroup thatAvg = (AudioVolumeGroup) o; 117 118 return mName.equals(thatAvg.mName) && mId == thatAvg.mId 119 && Arrays.equals(mAudioAttributes, thatAvg.mAudioAttributes); 120 } 121 122 @Override hashCode()123 public int hashCode() { 124 return Objects.hash(mName, mId, 125 Arrays.hashCode(mAudioAttributes), Arrays.hashCode(mLegacyStreamTypes)); 126 } 127 128 /** 129 * @return List of {@link AudioAttributes} involved in this {@link AudioVolumeGroup}. 130 */ getAudioAttributes()131 public @NonNull List<AudioAttributes> getAudioAttributes() { 132 return Arrays.asList(mAudioAttributes); 133 } 134 135 /** 136 * @return the stream types involved in this {@link AudioVolumeGroup}. 137 */ getLegacyStreamTypes()138 public @NonNull int[] getLegacyStreamTypes() { 139 return mLegacyStreamTypes; 140 } 141 142 /** 143 * @return human-readable name of this volume group. 144 */ name()145 public @NonNull String name() { 146 return mName; 147 } 148 149 /** 150 * @return the volume group unique identifier id. 151 */ getId()152 public int getId() { 153 return mId; 154 } 155 156 @Override describeContents()157 public int describeContents() { 158 return 0; 159 } 160 161 @Override writeToParcel(@onNull Parcel dest, int flags)162 public void writeToParcel(@NonNull Parcel dest, int flags) { 163 dest.writeString(mName); 164 dest.writeInt(mId); 165 dest.writeInt(mAudioAttributes.length); 166 for (AudioAttributes attributes : mAudioAttributes) { 167 attributes.writeToParcel(dest, flags | AudioAttributes.FLATTEN_TAGS/*flags*/); 168 } 169 dest.writeInt(mLegacyStreamTypes.length); 170 for (int streamType : mLegacyStreamTypes) { 171 dest.writeInt(streamType); 172 } 173 } 174 175 public static final Parcelable.Creator<AudioVolumeGroup> CREATOR = 176 new Parcelable.Creator<AudioVolumeGroup>() { 177 @Override 178 public @NonNull AudioVolumeGroup createFromParcel(@NonNull Parcel in) { 179 Preconditions.checkNotNull(in, "in Parcel must not be null"); 180 String name = in.readString(); 181 int id = in.readInt(); 182 int nbAttributes = in.readInt(); 183 AudioAttributes[] audioAttributes = new AudioAttributes[nbAttributes]; 184 for (int index = 0; index < nbAttributes; index++) { 185 audioAttributes[index] = AudioAttributes.CREATOR.createFromParcel(in); 186 } 187 int nbStreamTypes = in.readInt(); 188 int[] streamTypes = new int[nbStreamTypes]; 189 for (int index = 0; index < nbStreamTypes; index++) { 190 streamTypes[index] = in.readInt(); 191 } 192 return new AudioVolumeGroup(name, id, audioAttributes, streamTypes); 193 } 194 195 @Override 196 public @NonNull AudioVolumeGroup[] newArray(int size) { 197 return new AudioVolumeGroup[size]; 198 } 199 }; 200 201 @Override toString()202 public @NonNull String toString() { 203 StringBuilder s = new StringBuilder(); 204 s.append("\n Name: "); 205 s.append(mName); 206 s.append(" Id: "); 207 s.append(Integer.toString(mId)); 208 209 s.append("\n Supported Audio Attributes:"); 210 for (AudioAttributes attribute : mAudioAttributes) { 211 s.append("\n -"); 212 s.append(attribute.toString()); 213 } 214 s.append("\n Supported Legacy Stream Types: { "); 215 for (int legacyStreamType : mLegacyStreamTypes) { 216 s.append(Integer.toString(legacyStreamType)); 217 s.append(" "); 218 } 219 s.append("}"); 220 return s.toString(); 221 } 222 } 223