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.SystemApi;
21 import android.media.AudioAttributes;
22 import android.media.AudioSystem;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.util.Log;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.internal.util.Preconditions;
29 
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 
34 /**
35  * A class to create the association between different playback attributes
36  * (e.g. media, mapping direction) to a single volume control.
37  * @hide
38  */
39 @SystemApi
40 public final class AudioVolumeGroup implements Parcelable {
41     private static final String TAG = "AudioVolumeGroup";
42     /**
43      * Volume group value to use when introspection API fails.
44      */
45     public static final int DEFAULT_VOLUME_GROUP = -1;
46 
47     /**
48      * Unique identifier of a volume group.
49      */
50     private int mId;
51     /**
52      * human-readable name of this volume group.
53      */
54     private final String mName;
55 
56     private final AudioAttributes[] mAudioAttributes;
57     private int[] mLegacyStreamTypes;
58 
59     private static final Object sLock = new Object();
60 
61     @GuardedBy("sLock")
62     private static List<AudioVolumeGroup> sAudioVolumeGroups;
63 
64     /**
65      * @hide
66      * @return the List of AudioVolumeGroup discovered from platform configuration file.
67      */
68     @NonNull
getAudioVolumeGroups()69     public static List<AudioVolumeGroup> getAudioVolumeGroups() {
70         if (sAudioVolumeGroups == null) {
71             synchronized (sLock) {
72                 if (sAudioVolumeGroups == null) {
73                     sAudioVolumeGroups = initializeAudioVolumeGroups();
74                 }
75             }
76         }
77         return sAudioVolumeGroups;
78     }
79 
initializeAudioVolumeGroups()80     private static List<AudioVolumeGroup> initializeAudioVolumeGroups() {
81         ArrayList<AudioVolumeGroup> avgList = new ArrayList<>();
82         int status = native_list_audio_volume_groups(avgList);
83         if (status != AudioSystem.SUCCESS) {
84             Log.w(TAG, ": listAudioVolumeGroups failed");
85         }
86         return avgList;
87     }
88 
native_list_audio_volume_groups( ArrayList<AudioVolumeGroup> groups)89     private static native int native_list_audio_volume_groups(
90             ArrayList<AudioVolumeGroup> groups);
91 
92     /**
93      * @param name of the volume group
94      * @param id of the volume group
95      * @param legacyStreamTypes of volume group
96      */
AudioVolumeGroup(@onNull String name, int id, @NonNull AudioAttributes[] audioAttributes, @NonNull int[] legacyStreamTypes)97     AudioVolumeGroup(@NonNull String name, int id,
98                      @NonNull AudioAttributes[] audioAttributes,
99                      @NonNull int[] legacyStreamTypes) {
100         Preconditions.checkNotNull(name, "name must not be null");
101         Preconditions.checkNotNull(audioAttributes, "audioAttributes must not be null");
102         Preconditions.checkNotNull(legacyStreamTypes, "legacyStreamTypes must not be null");
103         mName = name;
104         mId = id;
105         mAudioAttributes = audioAttributes;
106         mLegacyStreamTypes = legacyStreamTypes;
107     }
108 
109     @Override
equals(@onNull Object o)110     public boolean equals(@NonNull Object o) {
111         if (this == o) return true;
112         if (o == null || getClass() != o.getClass()) return false;
113 
114         AudioVolumeGroup thatAvg = (AudioVolumeGroup) o;
115 
116         return mName == thatAvg.mName && mId == thatAvg.mId
117                 && mAudioAttributes.equals(thatAvg.mAudioAttributes);
118     }
119 
120     /**
121      * @return List of {@link AudioAttributes} involved in this {@link AudioVolumeGroup}.
122      */
getAudioAttributes()123     public @NonNull List<AudioAttributes> getAudioAttributes() {
124         return Arrays.asList(mAudioAttributes);
125     }
126 
127     /**
128      * @return the stream types involved in this {@link AudioVolumeGroup}.
129      */
getLegacyStreamTypes()130     public @NonNull int[] getLegacyStreamTypes() {
131         return mLegacyStreamTypes;
132     }
133 
134     /**
135      * @return human-readable name of this volume group.
136      */
name()137     public @NonNull String name() {
138         return mName;
139     }
140 
141     /**
142      * @return the volume group unique identifier id.
143      */
getId()144     public int getId() {
145         return mId;
146     }
147 
148     @Override
describeContents()149     public int describeContents() {
150         return 0;
151     }
152 
153     @Override
writeToParcel(@onNull Parcel dest, int flags)154     public void writeToParcel(@NonNull Parcel dest, int flags) {
155         dest.writeString(mName);
156         dest.writeInt(mId);
157         dest.writeInt(mAudioAttributes.length);
158         for (AudioAttributes attributes : mAudioAttributes) {
159             attributes.writeToParcel(dest, flags | AudioAttributes.FLATTEN_TAGS/*flags*/);
160         }
161         dest.writeInt(mLegacyStreamTypes.length);
162         for (int streamType : mLegacyStreamTypes) {
163             dest.writeInt(streamType);
164         }
165     }
166 
167     public static final Parcelable.Creator<AudioVolumeGroup> CREATOR =
168             new Parcelable.Creator<AudioVolumeGroup>() {
169                 @Override
170                 public @NonNull AudioVolumeGroup createFromParcel(@NonNull Parcel in) {
171                     Preconditions.checkNotNull(in, "in Parcel must not be null");
172                     String name = in.readString();
173                     int id = in.readInt();
174                     int nbAttributes = in.readInt();
175                     AudioAttributes[] audioAttributes = new AudioAttributes[nbAttributes];
176                     for (int index = 0; index < nbAttributes; index++) {
177                         audioAttributes[index] = AudioAttributes.CREATOR.createFromParcel(in);
178                     }
179                     int nbStreamTypes = in.readInt();
180                     int[] streamTypes = new int[nbStreamTypes];
181                     for (int index = 0; index < nbStreamTypes; index++) {
182                         streamTypes[index] = in.readInt();
183                     }
184                     return new AudioVolumeGroup(name, id, audioAttributes, streamTypes);
185                 }
186 
187                 @Override
188                 public @NonNull AudioVolumeGroup[] newArray(int size) {
189                     return new AudioVolumeGroup[size];
190                 }
191             };
192 
193     @Override
toString()194     public @NonNull String toString() {
195         StringBuilder s = new StringBuilder();
196         s.append("\n Name: ");
197         s.append(mName);
198         s.append(" Id: ");
199         s.append(Integer.toString(mId));
200 
201         s.append("\n     Supported Audio Attributes:");
202         for (AudioAttributes attribute : mAudioAttributes) {
203             s.append("\n       -");
204             s.append(attribute.toString());
205         }
206         s.append("\n     Supported Legacy Stream Types: { ");
207         for (int legacyStreamType : mLegacyStreamTypes) {
208             s.append(Integer.toString(legacyStreamType));
209             s.append(" ");
210         }
211         s.append("}");
212         return s.toString();
213     }
214 }
215