• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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_VOLUME_GAIN_INDEX_CHANGED;
19 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_MAX_INDEX_CHANGED;
20 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_MIN_INDEX_CHANGED;
21 
22 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
23 
24 import android.util.SparseArray;
25 
26 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
27 import com.android.car.internal.util.IndentingPrintWriter;
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.util.Preconditions;
30 
31 /**
32  * A class encapsulates a volume group in car.
33  *
34  * Volume in a car is controlled by group. A group holds one or more car audio contexts.
35  * Call {@link CarAudioManager#getVolumeGroupCount()} to get the count of {@link CarVolumeGroup}
36  * supported in a car.
37  */
38 final class CarAudioVolumeGroup extends CarVolumeGroup {
39     private static final int UNSET_STEP_SIZE = -1;
40     private static final int EVENT_TYPE_NONE = 0;
41     @GuardedBy("mLock")
42     private int mDefaultGain = Integer.MAX_VALUE;
43     @GuardedBy("mLock")
44     private int mMaxGain = Integer.MIN_VALUE;
45     @GuardedBy("mLock")
46     private int mMinGain = Integer.MAX_VALUE;
47     @GuardedBy("mLock")
48     private int mStepSize = UNSET_STEP_SIZE;
49 
CarAudioVolumeGroup(CarAudioContext carAudioContext, CarAudioSettings settingsManager, SparseArray<CarAudioDeviceInfo> contextToDeviceInfo, int zoneId, int configId, int volumeGroupId, String name, int stepSize, int defaultGain, int minGain, int maxGain, boolean useCarVolumeGroupMute, CarActivationVolumeConfig carActivationVolumeConfig)50     CarAudioVolumeGroup(CarAudioContext carAudioContext,
51             CarAudioSettings settingsManager,
52             SparseArray<CarAudioDeviceInfo> contextToDeviceInfo, int zoneId, int configId,
53             int volumeGroupId, String name, int stepSize, int defaultGain, int minGain, int maxGain,
54             boolean useCarVolumeGroupMute, CarActivationVolumeConfig carActivationVolumeConfig) {
55         super(carAudioContext, settingsManager, contextToDeviceInfo, zoneId, configId,
56                 volumeGroupId, name, useCarVolumeGroupMute, carActivationVolumeConfig);
57         Preconditions.checkArgument(stepSize != 0, "Step Size must not be zero");
58         mStepSize = stepSize;
59         mDefaultGain = defaultGain;
60         mMinGain = minGain;
61         mMaxGain = maxGain;
62         mLimitedGainIndex = getIndexForGainLocked(mMaxGain);
63     }
64 
65     @Override
getMaxGainIndex()66     public int getMaxGainIndex() {
67         synchronized (mLock) {
68             return getIndexForGainLocked(mMaxGain);
69         }
70     }
71 
72     @Override
getMinGainIndex()73     public int getMinGainIndex() {
74         synchronized (mLock) {
75             return getIndexForGainLocked(mMinGain);
76         }
77     }
78 
79     @Override
80     @GuardedBy("mLock")
81     @SuppressWarnings("GuardedBy")
setCurrentGainIndexLocked(int gainIndex)82     protected void setCurrentGainIndexLocked(int gainIndex) {
83         int gainInMillibels = getGainForIndexLocked(gainIndex);
84         for (int index = 0; index < mAddressToCarAudioDeviceInfo.size(); index++) {
85             CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.valueAt(index);
86             info.setCurrentGain(gainInMillibels);
87         }
88         super.setCurrentGainIndexLocked(gainIndex);
89     }
90 
91     @Override
92     @GuardedBy("mLock")
isValidGainIndexLocked(int gainIndex)93     protected boolean isValidGainIndexLocked(int gainIndex) {
94         return gainIndex >= getIndexForGainLocked(mMinGain)
95                 && gainIndex <= getIndexForGainLocked(mMaxGain);
96     }
97 
98     @Override
99     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
100     @GuardedBy("mLock")
dumpLocked(IndentingPrintWriter writer)101     protected void dumpLocked(IndentingPrintWriter writer) {
102         writer.printf("Step size: %d\n", mStepSize);
103         writer.printf("Gain values (min / max / default/ current): %d %d %d %d\n", mMinGain,
104                 mMaxGain, mDefaultGain, getGainForIndexLocked(mCurrentGainIndex));
105     }
106 
107     @Override
getDefaultGainIndex()108     protected int getDefaultGainIndex() {
109         synchronized (mLock) {
110             return getIndexForGainLocked(mDefaultGain);
111         }
112     }
113 
114     @GuardedBy("mLock")
getGainForIndexLocked(int gainIndex)115     private int getGainForIndexLocked(int gainIndex) {
116         return mMinGain + gainIndex * mStepSize;
117     }
118 
119     @GuardedBy("mLock")
getIndexForGainLocked(int gainInMillibel)120     private int getIndexForGainLocked(int gainInMillibel) {
121         return (gainInMillibel - mMinGain) / mStepSize;
122     }
123 
124     @Override
calculateNewGainStageFromDeviceInfos()125     int calculateNewGainStageFromDeviceInfos() {
126         int minGain = Integer.MAX_VALUE;
127         int maxGain = Integer.MIN_VALUE;
128         int defaultGain = Integer.MAX_VALUE;
129         int stepSize = UNSET_STEP_SIZE;
130 
131         int eventType = EVENT_TYPE_NONE;
132         synchronized (mLock) {
133             // compute the new volume group gain stage from scratch
134             for (int index = 0; index < mAddressToCarAudioDeviceInfo.size(); index++) {
135                 CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.valueAt(index);
136                 if (stepSize == UNSET_STEP_SIZE) {
137                     stepSize = info.getStepValue();
138                 } else {
139                     Preconditions.checkArgument(
140                             info.getStepValue() == stepSize,
141                             "Gain stages within one group must have same step value");
142                 }
143                 if (info.getDefaultGain() < defaultGain) {
144                     // We're arbitrarily selecting the lowest
145                     // device default gain as the group's default.
146                     defaultGain = info.getDefaultGain();
147                 }
148                 if (info.getMaxGain() > maxGain) {
149                     maxGain = info.getMaxGain();
150                 }
151                 if (info.getMinGain() < minGain) {
152                     minGain = info.getMinGain();
153                 }
154             }
155 
156             // update the new gain stage and return event types so that callback can be triggered.
157             // get the current and restricted gains in mb before updating the volume bounds. These
158             // will be used for extrapolating to new volume ranges
159             int epCurrentGainInMb = getGainForIndexLocked(getCurrentGainIndexLocked());
160             int epLimitedGainInMb = getGainForIndexLocked(mLimitedGainIndex);
161             int epBlockedGainInMb = getGainForIndexLocked(mBlockedGainIndex);
162             int epAttenuatedGainInMb = getGainForIndexLocked(mAttenuatedGainIndex);
163             boolean isLimited = isLimitedLocked();
164 
165             // update the volume ranges
166             if (minGain != mMinGain) {
167                 mMinGain = minGain;
168                 eventType |= (EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED
169                         | EVENT_TYPE_VOLUME_MIN_INDEX_CHANGED);
170             }
171             if (maxGain != mMaxGain) {
172                 mMaxGain = maxGain;
173                 eventType |= (EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED
174                         | EVENT_TYPE_VOLUME_MAX_INDEX_CHANGED);
175             }
176             // if only default gain changes, no impact to volume gain stages (i.e. no event).
177             if (defaultGain != mDefaultGain) {
178                 mDefaultGain = defaultGain;
179             }
180             if (stepSize != mStepSize) {
181                 mStepSize = stepSize;
182                 eventType |= (EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED
183                         | EVENT_TYPE_VOLUME_MAX_INDEX_CHANGED);
184             }
185             // if no change to indexes, return
186             if (eventType == EVENT_TYPE_NONE) {
187                 return eventType;
188             }
189 
190             // if min/max/step values change, we shall try to maintain the same volume level
191             // through simple extrapolation, i.e., the new  gain index is calculated from the
192             // previous {@code mCurrentGainIndex} in millibels.
193             // caution: before updating, check if the previous gain is out of bound in the
194             //          new gain stage. If yes, use the safe value (i.e default gain)
195             //          provided by the hal implementations.
196             mCurrentGainIndex = getIndexForGainLocked(epCurrentGainInMb);
197             if (!isValidGainIndexLocked(mCurrentGainIndex)) {
198                 mCurrentGainIndex = getIndexForGainLocked(mDefaultGain);
199             }
200 
201             // similar extrapolation is tried for restricted gains: limited, blocked, attenuated.
202             // Note: Even after best effort, it is possible that some or all of the old restriction
203             // indexes are invalid and therefore reset.
204             int newLimitedGainIndex = getIndexForGainLocked(epLimitedGainInMb);
205             if (isLimited && isValidGainIndexLocked(newLimitedGainIndex)) {
206                 setLimitLocked(newLimitedGainIndex);
207             } else {
208                 resetLimitLocked();
209             }
210 
211             int newBlockedGainIndex = getIndexForGainLocked(epBlockedGainInMb);
212             if (isBlockedLocked() && isValidGainIndexLocked(newBlockedGainIndex)) {
213                 setBlockedLocked(newBlockedGainIndex);
214             } else {
215                 resetBlockedLocked();
216             }
217 
218             int newAttenuatedGainIndex = getIndexForGainLocked(epAttenuatedGainInMb);
219             if (isAttenuatedLocked() && isValidGainIndexLocked(newAttenuatedGainIndex)) {
220                 setAttenuatedGainLocked(newAttenuatedGainIndex);
221             } else {
222                 resetAttenuationLocked();
223             }
224 
225             // Notes:
226             // (1) Setting current gain index will trigger Audio HAL. If restrictions are still
227             //     valid but were reset above, we expect AudioControl HAL to resend the restrictions
228             //     in a callback.
229             // (2) Audio HAL will be responsible to ensure consistent speaker output during this
230             //     transition.
231             setCurrentGainIndexLocked(getRestrictedGainForIndexLocked(mCurrentGainIndex));
232         }
233         return eventType;
234     }
235 }
236