• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.car.audio;
17 
18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
19 
20 import android.annotation.NonNull;
21 import android.car.media.CarAudioManager;
22 import android.media.AudioDeviceAttributes;
23 import android.media.AudioDeviceInfo;
24 import android.media.AudioPlaybackConfiguration;
25 import android.util.IndentingPrintWriter;
26 import android.util.Slog;
27 
28 import com.android.car.CarLog;
29 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
30 import com.android.internal.util.Preconditions;
31 
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Objects;
37 import java.util.Set;
38 
39 /**
40  * A class encapsulates an audio zone in car.
41  *
42  * An audio zone can contain multiple {@link CarVolumeGroup}s, and each zone has its own
43  * {@link CarAudioFocus} instance. Additionally, there may be dedicated hardware volume keys
44  * attached to each zone.
45  *
46  * See also the unified car_audio_configuration.xml
47  */
48 /* package */ class CarAudioZone {
49 
50     private final int mId;
51     private final String mName;
52     private final List<CarVolumeGroup> mVolumeGroups;
53     private final Set<String> mDeviceAddresses;
54     private List<AudioDeviceAttributes> mInputAudioDevice;
55 
CarAudioZone(int id, String name)56     CarAudioZone(int id, String name) {
57         mId = id;
58         mName = name;
59         mVolumeGroups = new ArrayList<>();
60         mInputAudioDevice = new ArrayList<>();
61         mDeviceAddresses = new HashSet<>();
62     }
63 
getId()64     int getId() {
65         return mId;
66     }
67 
getName()68     String getName() {
69         return mName;
70     }
71 
isPrimaryZone()72     boolean isPrimaryZone() {
73         return mId == CarAudioManager.PRIMARY_AUDIO_ZONE;
74     }
75 
addVolumeGroup(CarVolumeGroup volumeGroup)76     void addVolumeGroup(CarVolumeGroup volumeGroup) {
77         mVolumeGroups.add(volumeGroup);
78         mDeviceAddresses.addAll(volumeGroup.getAddresses());
79     }
80 
getVolumeGroup(int groupId)81     CarVolumeGroup getVolumeGroup(int groupId) {
82         Preconditions.checkArgumentInRange(groupId, 0, mVolumeGroups.size() - 1,
83                 "groupId(" + groupId + ") is out of range");
84         return mVolumeGroups.get(groupId);
85     }
86 
87     /**
88      * @return Snapshot of available {@link AudioDeviceInfo}s in List.
89      */
getAudioDeviceInfos()90     List<AudioDeviceInfo> getAudioDeviceInfos() {
91         final List<AudioDeviceInfo> devices = new ArrayList<>();
92         for (CarVolumeGroup group : mVolumeGroups) {
93             for (String address : group.getAddresses()) {
94                 devices.add(group.getCarAudioDeviceInfoForAddress(address).getAudioDeviceInfo());
95             }
96         }
97         return devices;
98     }
99 
getVolumeGroupCount()100     int getVolumeGroupCount() {
101         return mVolumeGroups.size();
102     }
103 
104     /**
105      * @return Snapshot of available {@link CarVolumeGroup}s in array.
106      */
getVolumeGroups()107     CarVolumeGroup[] getVolumeGroups() {
108         return mVolumeGroups.toArray(new CarVolumeGroup[0]);
109     }
110 
111     /**
112      * Constraints applied here:
113      *
114      * - One context should not appear in two groups
115      * - All contexts are assigned
116      * - One device should not appear in two groups
117      * - All gain controllers in the same group have same step value
118      *
119      * Note that it is fine that there are devices which do not appear in any group. Those devices
120      * may be reserved for other purposes.
121      * Step value validation is done in
122      * {@link CarVolumeGroup.Builder#setDeviceInfoForContext(int, CarAudioDeviceInfo)}
123      */
validateVolumeGroups()124     boolean validateVolumeGroups() {
125         Set<Integer> contexts = new HashSet<>();
126         Set<String> addresses = new HashSet<>();
127         for (int index = 0; index <  mVolumeGroups.size(); index++) {
128             CarVolumeGroup group = mVolumeGroups.get(index);
129             // One context should not appear in two groups
130             for (int context : group.getContexts()) {
131                 if (!contexts.add(context)) {
132                     Slog.e(CarLog.TAG_AUDIO, "Context appears in two groups: " + context);
133                     return false;
134                 }
135             }
136 
137             // One address should not appear in two groups
138             for (String address : group.getAddresses()) {
139                 if (!addresses.add(address)) {
140                     Slog.e(CarLog.TAG_AUDIO, "Address appears in two groups: " + address);
141                     return false;
142                 }
143             }
144         }
145 
146         // All contexts are assigned
147         if (contexts.size() != CarAudioContext.CONTEXTS.length) {
148             Slog.e(CarLog.TAG_AUDIO, "Some contexts are not assigned to group");
149             Slog.e(CarLog.TAG_AUDIO, "Assigned contexts " + contexts);
150             Slog.e(CarLog.TAG_AUDIO,
151                     "All contexts " + Arrays.toString(CarAudioContext.CONTEXTS));
152             return false;
153         }
154         return true;
155     }
156 
synchronizeCurrentGainIndex()157     void synchronizeCurrentGainIndex() {
158         for (CarVolumeGroup group : mVolumeGroups) {
159             group.setCurrentGainIndex(group.getCurrentGainIndex());
160         }
161     }
162 
163     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)164     void dump(IndentingPrintWriter writer) {
165         writer.printf("CarAudioZone(%s:%d) isPrimary? %b\n", mName, mId, isPrimaryZone());
166         writer.increaseIndent();
167         for (CarVolumeGroup group : mVolumeGroups) {
168             group.dump(writer);
169         }
170 
171         writer.printf("Input Audio Device Addresses\n");
172         writer.increaseIndent();
173         for (AudioDeviceAttributes audioDevice : mInputAudioDevice) {
174             writer.printf("Device Address(%s)\n", audioDevice.getAddress());
175         }
176         writer.decreaseIndent();
177         writer.println();
178         writer.decreaseIndent();
179     }
180 
getAddressForContext(int audioContext)181     String getAddressForContext(int audioContext) {
182         CarAudioContext.preconditionCheckAudioContext(audioContext);
183         String deviceAddress = null;
184         for (CarVolumeGroup volumeGroup : getVolumeGroups()) {
185             deviceAddress = volumeGroup.getAddressForContext(audioContext);
186             if (deviceAddress != null) {
187                 return deviceAddress;
188             }
189         }
190         // This should not happen unless something went wrong.
191         // Device address are unique per zone and all contexts are assigned in a zone.
192         throw new IllegalStateException("Could not find output device in zone " + mId
193                 + " for audio context " + audioContext);
194     }
195 
196     /**
197      * Update the volume groups for the new user
198      * @param userId user id to update to
199      */
updateVolumeGroupsSettingsForUser(int userId)200     public void updateVolumeGroupsSettingsForUser(int userId) {
201         for (CarVolumeGroup group : mVolumeGroups) {
202             group.loadVolumesSettingsForUser(userId);
203         }
204     }
205 
addInputAudioDevice(AudioDeviceAttributes device)206     void addInputAudioDevice(AudioDeviceAttributes device) {
207         mInputAudioDevice.add(device);
208     }
209 
getInputAudioDevices()210     List<AudioDeviceAttributes> getInputAudioDevices() {
211         return mInputAudioDevice;
212     }
213 
findActiveContextsFromPlaybackConfigurations( @onNull List<AudioPlaybackConfiguration> configurations)214     public @NonNull List<Integer> findActiveContextsFromPlaybackConfigurations(
215             @NonNull List<AudioPlaybackConfiguration> configurations) {
216         Objects.requireNonNull(configurations);
217         List<Integer> activeContexts = new ArrayList<>();
218         for (int index = 0; index < configurations.size(); index++) {
219             AudioPlaybackConfiguration configuration = configurations.get(index);
220             if (configuration.isActive()) {
221                 if (isAudioDeviceInfoValidForZone(configuration.getAudioDeviceInfo())) {
222                     // Note that address's context and the context actually supplied could be
223                     // different
224                     activeContexts.add(CarAudioContext.getContextForUsage(
225                             configuration.getAudioAttributes().getSystemUsage()));
226                 }
227             }
228         }
229         return activeContexts;
230     }
231 
isAudioDeviceInfoValidForZone(AudioDeviceInfo info)232     boolean isAudioDeviceInfoValidForZone(AudioDeviceInfo info) {
233         return info != null
234                 && info.getAddress() != null
235                 && !info.getAddress().isEmpty()
236                 && containsDeviceAddress(info.getAddress());
237     }
238 
containsDeviceAddress(String deviceAddress)239     private boolean containsDeviceAddress(String deviceAddress) {
240         return mDeviceAddresses.contains(deviceAddress);
241     }
242 }
243