• 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 android.car.media.CarVolumeGroupEvent.EVENT_TYPE_ATTENUATION_CHANGED;
19 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED;
20 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_BLOCKED_CHANGED;
21 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED;
22 
23 import static com.android.car.audio.hal.HalAudioGainCallback.reasonToString;
24 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
25 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.UserIdInt;
30 import android.car.builtin.util.Slogf;
31 import android.car.media.CarVolumeGroupInfo;
32 import android.media.AudioAttributes;
33 import android.media.AudioDeviceInfo;
34 import android.media.AudioManager;
35 import android.os.UserHandle;
36 import android.util.ArrayMap;
37 import android.util.SparseArray;
38 
39 import com.android.car.CarLog;
40 import com.android.car.audio.CarAudioContext.AudioContext;
41 import com.android.car.audio.hal.HalAudioDeviceInfo;
42 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
43 import com.android.car.internal.util.IndentingPrintWriter;
44 import com.android.internal.annotations.GuardedBy;
45 import com.android.internal.util.Preconditions;
46 
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.List;
50 import java.util.Objects;
51 
52 /**
53  * A class encapsulates a volume group in car.
54  *
55  * Interface holding volume interface APIs and also common code for:
56  *
57  * -volume groups using {@link AudioManager#setAudioPortGain} to control the volume
58  * while the audioserver resource config_useFixedVolume is set.
59  *
60  * -volume groups relying on audioserver to control the volume and access using
61  * {@link AudioManager#setVolumeIndexForAttributes(AudioAttributes, int, int)} and all other
62  * related volume APIs.
63  * Gain may either be controlled on hardware amplifier using Audio HAL setaudioPortConfig if the
64  * correlated audio device port defines a gain controller with attribute name="useForVolume" set
65  * or in software using the port id in Audio flinger.
66  * Gains are set only when activity is detected on the given audio device port (Mixer thread, or
67  * {@link android.media.HwAudioSource} realized through a software bridge or hardware bridge.
68  *
69  */
70 /* package */ abstract class CarVolumeGroup {
71     public static final int UNINITIALIZED = -1;
72 
73     private final boolean mUseCarVolumeGroupMute;
74     private final boolean mHasCriticalAudioContexts;
75     private final CarAudioSettings mSettingsManager;
76     protected final int mId;
77     private final String mName;
78     protected final int mZoneId;
79     protected final int mConfigId;
80     protected final SparseArray<String> mContextToAddress;
81     protected final ArrayMap<String, CarAudioDeviceInfo> mAddressToCarAudioDeviceInfo;
82 
83     protected final Object mLock = new Object();
84     private final CarAudioContext mCarAudioContext;
85 
86     @GuardedBy("mLock")
87     protected int mStoredGainIndex;
88 
89     @GuardedBy("mLock")
90     protected int mCurrentGainIndex = UNINITIALIZED;
91 
92     /**
93      * Mute state for requests coming from clients. See {@link #mIsHalMuted} for state of requests
94      * coming from HAL.
95      */
96     @GuardedBy("mLock")
97     protected boolean mIsMuted;
98     @GuardedBy("mLock")
99     protected @UserIdInt int mUserId = UserHandle.CURRENT.getIdentifier();
100 
101     /**
102      * Attenuated gain is set to {@link #UNINITIALIZED} till attenuation explicitly reported by
103      * {@link com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or
104      * more {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason is cleared,
105      * it returns back to {@link #UNINITIALIZED}.
106      */
107     @GuardedBy("mLock")
108     protected int mAttenuatedGainIndex = UNINITIALIZED;
109 
110     /**
111      * Limitation gain is set to max gain value till limitation explicitly reported by {@link
112      * com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or more
113      * {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason is cleared, it
114      * returns back to max.
115      */
116     @GuardedBy("mLock")
117     protected int mLimitedGainIndex;
118 
119     /**
120      * Blocked gain is set to {@link #UNINITIALIZED} till blocking case explicitly reported by
121      * {@link com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or
122      * more {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason is cleared,
123      * it returns back to {@link #UNINITIALIZED}.
124      */
125     @GuardedBy("mLock")
126     protected int mBlockedGainIndex = UNINITIALIZED;
127 
128     /**
129      * The default state of HAL mute is {@code false} until HAL explicitly reports through
130      * {@link com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or
131      * more {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason
132      * is cleared, it is reset. See {@link #mIsMuted} for state of requests coming from clients.
133      */
134     @GuardedBy("mLock")
135     private boolean mIsHalMuted = false;
136 
137     /**
138      * Reasons list currently reported for this port by {@link
139      * com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged}.
140      */
141     protected List<Integer> mReasons = new ArrayList<>();
142 
CarVolumeGroup(CarAudioContext carAudioContext, CarAudioSettings settingsManager, SparseArray<String> contextToAddress, ArrayMap<String, CarAudioDeviceInfo> addressToCarAudioDeviceInfo, int zoneId, int configId, int volumeGroupId, String name, boolean useCarVolumeGroupMute)143     protected CarVolumeGroup(CarAudioContext carAudioContext, CarAudioSettings settingsManager,
144             SparseArray<String> contextToAddress, ArrayMap<String,
145             CarAudioDeviceInfo> addressToCarAudioDeviceInfo, int zoneId, int configId,
146             int volumeGroupId, String name, boolean useCarVolumeGroupMute) {
147         mSettingsManager = settingsManager;
148         mContextToAddress = contextToAddress;
149         mAddressToCarAudioDeviceInfo = addressToCarAudioDeviceInfo;
150         mCarAudioContext = carAudioContext;
151         mZoneId = zoneId;
152         mConfigId = configId;
153         mId = volumeGroupId;
154         mName = Objects.requireNonNull(name, "Volume group name cannot be null");
155         mUseCarVolumeGroupMute = useCarVolumeGroupMute;
156         List<AudioAttributes> volumeAttributes = new ArrayList<>();
157         for (int index = 0; index <  contextToAddress.size(); index++) {
158             int context = contextToAddress.keyAt(index);
159             List<AudioAttributes> audioAttributes =
160                     Arrays.asList(mCarAudioContext.getAudioAttributesForContext(context));
161             volumeAttributes.addAll(audioAttributes);
162         }
163 
164         mHasCriticalAudioContexts = containsCriticalAttributes(volumeAttributes);
165     }
166 
init()167     void init() {
168         synchronized (mLock) {
169             mStoredGainIndex = mSettingsManager.getStoredVolumeGainIndexForUser(
170                     mUserId, mZoneId, mConfigId, mId);
171             updateCurrentGainIndexLocked();
172         }
173     }
174 
175     @GuardedBy("mLock")
hasPendingAttenuationReasonsLocked()176     protected boolean hasPendingAttenuationReasonsLocked() {
177         return !mReasons.isEmpty();
178     }
179 
180     @GuardedBy("mLock")
setBlockedLocked(int blockedIndex)181     protected void setBlockedLocked(int blockedIndex) {
182         mBlockedGainIndex = blockedIndex;
183     }
184 
185     @GuardedBy("mLock")
resetBlockedLocked()186     protected void resetBlockedLocked() {
187         setBlockedLocked(UNINITIALIZED);
188     }
189 
190     @GuardedBy("mLock")
isBlockedLocked()191     protected boolean isBlockedLocked() {
192         return mBlockedGainIndex != UNINITIALIZED;
193     }
194 
195     @GuardedBy("mLock")
setLimitLocked(int limitIndex)196     protected void setLimitLocked(int limitIndex) {
197         mLimitedGainIndex = limitIndex;
198     }
199 
200     @GuardedBy("mLock")
resetLimitLocked()201     protected void resetLimitLocked() {
202         setLimitLocked(getMaxGainIndex());
203     }
204 
205     @GuardedBy("mLock")
isLimitedLocked()206     protected boolean isLimitedLocked() {
207         return mLimitedGainIndex != getMaxGainIndex();
208     }
209 
210     @GuardedBy("mLock")
isOverLimitLocked()211     protected boolean isOverLimitLocked() {
212         return isOverLimitLocked(mCurrentGainIndex);
213     }
214 
215     @GuardedBy("mLock")
isOverLimitLocked(int index)216     protected boolean isOverLimitLocked(int index) {
217         return isLimitedLocked() && (index > mLimitedGainIndex);
218     }
219 
220     @GuardedBy("mLock")
setAttenuatedGainLocked(int attenuatedGainIndex)221     protected void setAttenuatedGainLocked(int attenuatedGainIndex) {
222         mAttenuatedGainIndex = attenuatedGainIndex;
223     }
224 
225     @GuardedBy("mLock")
resetAttenuationLocked()226     protected void resetAttenuationLocked() {
227         setAttenuatedGainLocked(UNINITIALIZED);
228     }
229 
230     @GuardedBy("mLock")
isAttenuatedLocked()231     protected boolean isAttenuatedLocked() {
232         return mAttenuatedGainIndex != UNINITIALIZED;
233     }
234 
235     @GuardedBy("mLock")
setHalMuteLocked(boolean mute)236     private void setHalMuteLocked(boolean mute) {
237         mIsHalMuted = mute;
238     }
239 
240     @GuardedBy("mLock")
isHalMutedLocked()241     protected boolean isHalMutedLocked() {
242         return mIsHalMuted;
243     }
244 
setBlocked(int blockedIndex)245     void setBlocked(int blockedIndex) {
246         synchronized (mLock) {
247             setBlockedLocked(blockedIndex);
248         }
249     }
250 
resetBlocked()251     void resetBlocked() {
252         synchronized (mLock) {
253             resetBlockedLocked();
254         }
255     }
256 
isBlocked()257     boolean isBlocked() {
258         synchronized (mLock) {
259             return isBlockedLocked();
260         }
261     }
262 
setLimit(int limitIndex)263     void setLimit(int limitIndex) {
264         synchronized (mLock) {
265             setLimitLocked(limitIndex);
266         }
267     }
268 
resetLimit()269     void resetLimit() {
270         synchronized (mLock) {
271             resetLimitLocked();
272         }
273     }
274 
isLimited()275     boolean isLimited() {
276         synchronized (mLock) {
277             return isLimitedLocked();
278         }
279     }
280 
isOverLimit()281     boolean isOverLimit() {
282         synchronized (mLock) {
283             return isOverLimitLocked();
284         }
285     }
286 
setAttenuatedGain(int attenuatedGainIndex)287     void setAttenuatedGain(int attenuatedGainIndex) {
288         synchronized (mLock) {
289             setAttenuatedGainLocked(attenuatedGainIndex);
290         }
291     }
292 
resetAttenuation()293     void resetAttenuation() {
294         synchronized (mLock) {
295             resetAttenuationLocked();
296         }
297     }
298 
isAttenuated()299     boolean isAttenuated() {
300         synchronized (mLock) {
301             return isAttenuatedLocked();
302         }
303     }
304 
305     @Nullable
getCarAudioDeviceInfoForAddress(String address)306     CarAudioDeviceInfo getCarAudioDeviceInfoForAddress(String address) {
307         return mAddressToCarAudioDeviceInfo.get(address);
308     }
309 
310     @AudioContext
getContexts()311     int[] getContexts() {
312         final int[] carAudioContexts = new int[mContextToAddress.size()];
313         for (int i = 0; i < carAudioContexts.length; i++) {
314             carAudioContexts[i] = mContextToAddress.keyAt(i);
315         }
316         return carAudioContexts;
317     }
318 
319     /**
320      * Returns the id of the volume group.
321      * <p> Note that all clients are already developed in the way that when they get the number of
322      * volume group, they will then address a given volume group using its id as if the id was the
323      * index of the array of group (aka 0 to length - 1).
324      */
getId()325     int getId() {
326         return mId;
327     }
328 
getName()329     String getName() {
330         return mName;
331     }
332 
333     /**
334      * Returns the devices address for the given context
335      * or {@code null} if the context does not exist in the volume group
336      */
337     @Nullable
getAddressForContext(int audioContext)338     String getAddressForContext(int audioContext) {
339         return mContextToAddress.get(audioContext);
340     }
341 
342     /**
343      * Returns the audio devices for the given context
344      * or {@code null} if the context does not exist in the volume group
345      */
346     @Nullable
getAudioDeviceForContext(int audioContext)347     AudioDeviceInfo getAudioDeviceForContext(int audioContext) {
348         String address = getAddressForContext(audioContext);
349         if (address == null) {
350             return null;
351         }
352 
353         CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.get(address);
354         if (info == null) {
355             return null;
356         }
357 
358         return info.getAudioDeviceInfo();
359     }
360 
361     @AudioContext
getContextsForAddress(@onNull String address)362     List<Integer> getContextsForAddress(@NonNull String address) {
363         List<Integer> carAudioContexts = new ArrayList<>();
364         for (int i = 0; i < mContextToAddress.size(); i++) {
365             String value = mContextToAddress.valueAt(i);
366             if (address.equals(value)) {
367                 carAudioContexts.add(mContextToAddress.keyAt(i));
368             }
369         }
370         return carAudioContexts;
371     }
372 
getAddresses()373     List<String> getAddresses() {
374         return new ArrayList<>(mAddressToCarAudioDeviceInfo.keySet());
375     }
376 
getAllSupportedUsagesForAddress(@onNull String address)377     List<Integer> getAllSupportedUsagesForAddress(@NonNull String address) {
378         List<Integer> supportedUsagesForAddress = new ArrayList<>();
379         List<Integer> contextsForAddress = getContextsForAddress(address);
380         for (int contextIndex = 0; contextIndex < contextsForAddress.size(); contextIndex++) {
381             int contextId = contextsForAddress.get(contextIndex);
382             AudioAttributes[] attributes =
383                     mCarAudioContext.getAudioAttributesForContext(contextId);
384             for (int attrIndex = 0; attrIndex < attributes.length; attrIndex++) {
385                 int usage = attributes[attrIndex].getSystemUsage();
386                 if (!supportedUsagesForAddress.contains(usage)) {
387                     supportedUsagesForAddress.add(usage);
388                 }
389             }
390         }
391         return supportedUsagesForAddress;
392     }
393 
getMaxGainIndex()394     abstract int getMaxGainIndex();
395 
getMinGainIndex()396     abstract int getMinGainIndex();
397 
getCurrentGainIndex()398     int getCurrentGainIndex() {
399         synchronized (mLock) {
400             if (isMutedLocked()) {
401                 return getMinGainIndex();
402             }
403 
404             return getRestrictedGainForIndexLocked(getCurrentGainIndexLocked());
405         }
406     }
407 
408     @GuardedBy("mLock")
getCurrentGainIndexLocked()409     protected int getCurrentGainIndexLocked() {
410         return mCurrentGainIndex;
411     }
412 
413     @GuardedBy("mLock")
getRestrictedGainForIndexLocked(int index)414     protected int getRestrictedGainForIndexLocked(int index) {
415         if (isBlockedLocked()) {
416             return mBlockedGainIndex;
417         }
418         if (isOverLimitLocked()) {
419             return mLimitedGainIndex;
420         }
421         if (isAttenuatedLocked()) {
422             // Need to figure out if attenuation shall be hidden to end user
423             // as while ducked from IAudioControl
424             // TODO(b/) clarify in case of volume adjustment if the reference index is the
425             // ducked index or the current index. Taking current may lead to gap of index > 1.
426             return mAttenuatedGainIndex;
427         }
428         return index;
429     }
430 
431     /**
432      * Sets the gain on this group, gain will be set on all devices within volume group.
433      */
setCurrentGainIndex(int gainIndex)434     void setCurrentGainIndex(int gainIndex) {
435         synchronized (mLock) {
436             int currentgainIndex = gainIndex;
437             Preconditions.checkArgument(isValidGainIndexLocked(gainIndex),
438                     "Gain out of range (%d:%d) index %d", getMinGainIndex(), getMaxGainIndex(),
439                     gainIndex);
440             if (isBlockedLocked()) {
441                 // prevent any volume change while {@link IAudioGainCallback} reported block event.
442                 return;
443             }
444             if (isOverLimitLocked(currentgainIndex)) {
445                 currentgainIndex = mLimitedGainIndex;
446             }
447             if (isAttenuatedLocked()) {
448                 resetAttenuationLocked();
449             }
450             // In case of attenuation/Limitation, requested index is now the new reference for
451             // cached current index.
452             mCurrentGainIndex = currentgainIndex;
453 
454             if (mIsMuted) {
455                 setMuteLocked(false);
456             }
457             setCurrentGainIndexLocked(mCurrentGainIndex);
458         }
459     }
460 
461     @GuardedBy("mLock")
setCurrentGainIndexLocked(int gainIndex)462     protected void setCurrentGainIndexLocked(int gainIndex) {
463         storeGainIndexForUserLocked(gainIndex, mUserId);
464     }
465 
hasCriticalAudioContexts()466     boolean hasCriticalAudioContexts() {
467         return mHasCriticalAudioContexts;
468     }
469 
470     @Override
471     @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
toString()472     public String toString() {
473         synchronized (mLock) {
474             return "CarVolumeGroup id: " + mId
475                     + " currentGainIndex: " + mCurrentGainIndex
476                     + " contexts: " + Arrays.toString(getContexts())
477                     + " addresses: " + String.join(", ", getAddresses());
478         }
479     }
480 
481     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpLocked(IndentingPrintWriter writer)482     protected abstract void dumpLocked(IndentingPrintWriter writer);
483 
484     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)485     void dump(IndentingPrintWriter writer) {
486         synchronized (mLock) {
487             writer.printf("CarVolumeGroup(%d)\n", mId);
488             writer.increaseIndent();
489             writer.printf("Name(%s)\n", mName);
490             writer.printf("Zone Id(%d)\n", mZoneId);
491             writer.printf("Configuration Id(%d)\n", mConfigId);
492             writer.printf("Is Muted(%b)\n", isMutedLocked());
493             writer.printf("UserId(%d)\n", mUserId);
494             writer.printf("Persist Volume Group Mute(%b)\n",
495                     mSettingsManager.isPersistVolumeGroupMuteEnabled(mUserId));
496             dumpLocked(writer);
497             writer.printf("Gain indexes (min / max / default / current): %d %d %d %d\n",
498                     getMinGainIndex(), getMaxGainIndex(), getDefaultGainIndex(),
499                     mCurrentGainIndex);
500             for (int i = 0; i < mContextToAddress.size(); i++) {
501                 writer.printf("Context: %s -> Address: %s\n",
502                         mCarAudioContext.toString(mContextToAddress.keyAt(i)),
503                         mContextToAddress.valueAt(i));
504             }
505             for (int i = 0; i < mAddressToCarAudioDeviceInfo.size(); i++) {
506                 String address = mAddressToCarAudioDeviceInfo.keyAt(i);
507                 CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.get(address);
508                 info.dump(writer);
509             }
510             writer.printf("Reported reasons:\n");
511             writer.increaseIndent();
512             for (int index = 0; index < mReasons.size(); index++) {
513                 int reason = mReasons.get(index);
514                 writer.printf("%s\n", reasonToString(reason));
515             }
516             writer.decreaseIndent();
517             writer.printf("Gain infos:\n");
518             writer.increaseIndent();
519             writer.printf(
520                     "Blocked: %b%s\n",
521                     isBlockedLocked(),
522                     (isBlockedLocked() ? " (at: " + mBlockedGainIndex + ")" : ""));
523             writer.printf(
524                     "Limited: %b%s\n",
525                     isLimitedLocked(),
526                     (isLimitedLocked() ? " (at: " + mLimitedGainIndex + ")" : ""));
527             writer.printf(
528                     "Attenuated: %b%s\n",
529                     isAttenuatedLocked(),
530                     (isAttenuatedLocked() ? " (at: " + mAttenuatedGainIndex + ")" : ""));
531             writer.printf("Muted by HAL: %b\n", isHalMutedLocked());
532             writer.decreaseIndent();
533             // Empty line for comfortable reading
534             writer.println();
535             writer.decreaseIndent();
536         }
537     }
538 
loadVolumesSettingsForUser(@serIdInt int userId)539     void loadVolumesSettingsForUser(@UserIdInt int userId) {
540         synchronized (mLock) {
541             //Update the volume for the new user
542             updateUserIdLocked(userId);
543             //Update the current gain index
544             updateCurrentGainIndexLocked();
545             setCurrentGainIndexLocked(getCurrentGainIndexLocked());
546             //Reset devices with current gain index
547             updateGroupMuteLocked();
548         }
549     }
550 
551     /**
552      * Set the mute state of the Volume Group
553      *
554      * @param mute state requested
555      * @return true if mute state has changed, false otherwiser (already set or change not allowed)
556      */
setMute(boolean mute)557     boolean setMute(boolean mute) {
558         synchronized (mLock) {
559             // if hal muted the audio devices, then do not allow other incoming requests
560             // to perform unmute.
561             if (!mute && isHalMutedLocked()) {
562                 Slogf.e(CarLog.TAG_AUDIO, "Un-mute request cannot be processed due to active "
563                         + "hal mute restriction!");
564                 return false;
565             }
566             applyMuteLocked(mute);
567             return setMuteLocked(mute);
568         }
569     }
570 
571     @GuardedBy("mLock")
setMuteLocked(boolean mute)572     protected boolean setMuteLocked(boolean mute) {
573         boolean hasChanged = mIsMuted != mute;
574         mIsMuted = mute;
575         if (mSettingsManager.isPersistVolumeGroupMuteEnabled(mUserId)) {
576             mSettingsManager.storeVolumeGroupMuteForUser(mUserId, mZoneId, mConfigId, mId, mute);
577         }
578         return hasChanged;
579     }
580 
581     @GuardedBy("mLock")
applyMuteLocked(boolean mute)582     protected void applyMuteLocked(boolean mute) {
583     }
584 
isMuted()585     boolean isMuted() {
586         synchronized (mLock) {
587             return isMutedLocked();
588         }
589     }
590 
591     @GuardedBy("mLock")
isMutedLocked()592     protected boolean isMutedLocked() {
593         // if either of the mute states is set, it results in group being muted.
594         return isUserMutedLocked() || isHalMutedLocked();
595     }
596 
597     @GuardedBy("mLock")
isUserMutedLocked()598     protected boolean isUserMutedLocked() {
599         return mIsMuted;
600     }
601 
602     @GuardedBy("mLock")
isFullyMutedLocked()603     protected boolean isFullyMutedLocked() {
604         return isUserMutedLocked() || isHalMutedLocked() || isBlockedLocked();
605     }
606 
containsCriticalAttributes(List<AudioAttributes> volumeAttributes)607     private static boolean containsCriticalAttributes(List<AudioAttributes> volumeAttributes) {
608         for (int index = 0; index < volumeAttributes.size(); index++) {
609             if (CarAudioContext.isCriticalAudioAudioAttribute(volumeAttributes.get(index))) {
610                 return true;
611             }
612         }
613         return false;
614     }
615 
616     @GuardedBy("mLock")
updateUserIdLocked(@serIdInt int userId)617     private void updateUserIdLocked(@UserIdInt int userId) {
618         mUserId = userId;
619         mStoredGainIndex = getCurrentGainIndexForUserLocked();
620     }
621 
622     @GuardedBy("mLock")
getCurrentGainIndexForUserLocked()623     private int getCurrentGainIndexForUserLocked() {
624         int gainIndexForUser = mSettingsManager.getStoredVolumeGainIndexForUser(mUserId, mZoneId,
625                 mConfigId, mId);
626         Slogf.i(CarLog.TAG_AUDIO, "updateUserId userId " + mUserId
627                 + " gainIndexForUser " + gainIndexForUser);
628         return gainIndexForUser;
629     }
630 
631     /**
632      * Update the current gain index based on the stored gain index
633      */
634     @GuardedBy("mLock")
updateCurrentGainIndexLocked()635     private void updateCurrentGainIndexLocked() {
636         if (isValidGainIndexLocked(mStoredGainIndex)) {
637             mCurrentGainIndex = mStoredGainIndex;
638         } else {
639             mCurrentGainIndex = getDefaultGainIndex();
640         }
641     }
642 
isValidGainIndex(int gainIndex)643     protected boolean isValidGainIndex(int gainIndex) {
644         synchronized (mLock) {
645             return isValidGainIndexLocked(gainIndex);
646         }
647     }
isValidGainIndexLocked(int gainIndex)648     protected abstract boolean isValidGainIndexLocked(int gainIndex);
649 
getDefaultGainIndex()650     protected abstract int getDefaultGainIndex();
651 
652     @GuardedBy("mLock")
storeGainIndexForUserLocked(int gainIndex, @UserIdInt int userId)653     private void storeGainIndexForUserLocked(int gainIndex, @UserIdInt int userId) {
654         mSettingsManager.storeVolumeGainIndexForUser(userId,
655                 mZoneId, mConfigId, mId, gainIndex);
656     }
657 
658     @GuardedBy("mLock")
updateGroupMuteLocked()659     private void updateGroupMuteLocked() {
660         if (!mUseCarVolumeGroupMute) {
661             return;
662         }
663         if (!mSettingsManager.isPersistVolumeGroupMuteEnabled(mUserId)) {
664             mIsMuted = false;
665             return;
666         }
667         mIsMuted = mSettingsManager.getVolumeGroupMuteForUser(mUserId, mZoneId, mConfigId, mId);
668         applyMuteLocked(isFullyMutedLocked());
669     }
670 
671     /**
672      * Updates volume group states (index, mute, blocked etc) on callback from audio control hal.
673      *
674      * <p>If gain config info carries duplicate info, do not generate events (i.e. eventType = 0)
675      * @param halReasons reasons for change to gain config info
676      * @param gain updated gain config info
677      * @return one or more of {@link EventTypeEnum}. Or 0 for duplicate gain config info
678      */
onAudioGainChanged(List<Integer> halReasons, CarAudioGainConfigInfo gain)679     int onAudioGainChanged(List<Integer> halReasons, CarAudioGainConfigInfo gain) {
680         int eventType = 0;
681         int halIndex = gain.getVolumeIndex();
682         if (getCarAudioDeviceInfoForAddress(gain.getDeviceAddress()) == null
683                 || !isValidGainIndex(halIndex)) {
684             Slogf.e(CarLog.TAG_AUDIO,
685                     "onAudioGainChanged invalid CarAudioGainConfigInfo: " + gain
686                     + " for group id: " + mId);
687             return eventType;
688         }
689         synchronized (mLock) {
690             int previousRestrictedIndex = getRestrictedGainForIndexLocked(mCurrentGainIndex);
691             mReasons = new ArrayList<>(halReasons);
692 
693             boolean shouldBlock = CarAudioGainMonitor.shouldBlockVolumeRequest(halReasons);
694             if ((shouldBlock != isBlockedLocked())
695                     || (shouldBlock && (halIndex != mBlockedGainIndex))) {
696                 setBlockedLocked(shouldBlock ? halIndex : UNINITIALIZED);
697                 eventType |= EVENT_TYPE_VOLUME_BLOCKED_CHANGED;
698             }
699 
700             boolean shouldLimit = CarAudioGainMonitor.shouldLimitVolume(halReasons);
701             if ((shouldLimit != isLimitedLocked())
702                     || (shouldLimit && (halIndex != mLimitedGainIndex))) {
703                 setLimitLocked(shouldLimit ? halIndex : getMaxGainIndex());
704                 eventType |= EVENT_TYPE_ATTENUATION_CHANGED;
705             }
706 
707             boolean shouldDuck = CarAudioGainMonitor.shouldDuckGain(halReasons);
708             if ((shouldDuck != isAttenuatedLocked())
709                     || (shouldDuck && (halIndex != mAttenuatedGainIndex))) {
710                 setAttenuatedGainLocked(shouldDuck ? halIndex : UNINITIALIZED);
711                 eventType |= EVENT_TYPE_ATTENUATION_CHANGED;
712             }
713 
714             // Accept mute callbacks from hal only if group mute is enabled.
715             // If disabled, such callbacks will be considered as blocking restriction only.
716             boolean shouldMute = CarAudioGainMonitor.shouldMuteVolumeGroup(halReasons);
717             if (mUseCarVolumeGroupMute && (shouldMute != isHalMutedLocked())) {
718                 setHalMuteLocked(shouldMute);
719                 eventType |= EVENT_TYPE_MUTE_CHANGED;
720             }
721 
722             if (CarAudioGainMonitor.shouldUpdateVolumeIndex(halReasons)
723                     && (halIndex != getRestrictedGainForIndexLocked(mCurrentGainIndex))) {
724                 mCurrentGainIndex = halIndex;
725                 eventType |= EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED;
726             }
727 
728             // Blocked/Attenuated index shall have been already apply by Audio HAL on HW.
729             // However, keep in sync & broadcast to all ports this volume group deals with.
730             //
731             // Do not update current gain cache, keep it for restoring rather using reported index
732             // when the event is cleared.
733             int newRestrictedIndex = getRestrictedGainForIndexLocked(mCurrentGainIndex);
734             setCurrentGainIndexLocked(newRestrictedIndex);
735             // Hal or user mute state can change (only user mute enabled while hal muted allowed).
736             // Force a sync of mute application.
737             applyMuteLocked(isFullyMutedLocked());
738 
739             if (newRestrictedIndex != previousRestrictedIndex) {
740                 eventType |= EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED;
741             }
742         }
743         return eventType;
744     }
745 
getCarVolumeGroupInfo()746     CarVolumeGroupInfo getCarVolumeGroupInfo() {
747         int gainIndex;
748         boolean isMuted;
749         boolean isBlocked;
750         boolean isAttenuated;
751         synchronized (mLock) {
752             gainIndex = getRestrictedGainForIndexLocked(mCurrentGainIndex);
753             isMuted = isMutedLocked();
754             isBlocked = isBlockedLocked();
755             isAttenuated = isAttenuatedLocked() || isLimitedLocked();
756         }
757 
758         String name = mName.isEmpty() ? "group id " + mId : mName;
759 
760         return new CarVolumeGroupInfo.Builder(name, mZoneId, mId)
761                 .setVolumeGainIndex(gainIndex).setMaxVolumeGainIndex(getMaxGainIndex())
762                 .setMinVolumeGainIndex(getMinGainIndex()).setMuted(isMuted).setBlocked(isBlocked)
763                 .setAttenuated(isAttenuated).setAudioAttributes(getAudioAttributes()).build();
764     }
765 
getAudioAttributes()766     List<AudioAttributes> getAudioAttributes() {
767         List<AudioAttributes> audioAttributes = new ArrayList<>();
768         for (int index = 0; index < mContextToAddress.size(); index++) {
769             int context = mContextToAddress.keyAt(index);
770             AudioAttributes[] contextAttributes =
771                     mCarAudioContext.getAudioAttributesForContext(context);
772             for (int attrIndex = 0; attrIndex < contextAttributes.length; attrIndex++) {
773                 audioAttributes.add(contextAttributes[attrIndex]);
774             }
775         }
776 
777         return audioAttributes;
778     }
779 
780     /**
781      * @return one or more {@link CarVolumeGroupEvent#EventTypeEnum}
782      */
onAudioVolumeGroupChanged(int flags)783     public int onAudioVolumeGroupChanged(int flags) {
784         return 0;
785     }
786 
787     /**
788      * Updates car audio device info with the hal audio device info
789      */
updateAudioDeviceInfo(HalAudioDeviceInfo halDeviceInfo)790     void updateAudioDeviceInfo(HalAudioDeviceInfo halDeviceInfo) {
791         synchronized (mLock) {
792             CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.get(halDeviceInfo.getAddress());
793             if (info == null) {
794                 Slogf.w(CarLog.TAG_AUDIO, "No matching car audio device info found for address: %s",
795                         halDeviceInfo.getAddress());
796                 return;
797             }
798             info.updateAudioDeviceInfo(halDeviceInfo);
799         }
800     }
801 
802     /**
803      * Calculates the new gain stages from list of assigned audio device infos
804      *
805      * <p>Used to update audio device gain stages dynamically.
806      *
807      * @return  one or more of {@link EventTypeEnum}. Or 0 if dynamic updates are not supported
808      */
calculateNewGainStageFromDeviceInfos()809     int calculateNewGainStageFromDeviceInfos() {
810         return 0;
811     }
812 }
813