• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 com.android.car.audio;
18 
19 import static android.car.builtin.media.AudioManagerHelper.adjustToString;
20 import static android.car.media.CarAudioManager.INVALID_VOLUME_GROUP_ID;
21 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
22 import static android.media.AudioManager.ADJUST_LOWER;
23 import static android.media.AudioManager.ADJUST_MUTE;
24 import static android.media.AudioManager.ADJUST_RAISE;
25 import static android.media.AudioManager.ADJUST_SAME;
26 import static android.media.AudioManager.ADJUST_TOGGLE_MUTE;
27 import static android.media.AudioManager.ADJUST_UNMUTE;
28 import static android.media.AudioManager.FLAG_FROM_KEY;
29 import static android.media.AudioManager.FLAG_SHOW_UI;
30 import static android.util.Log.VERBOSE;
31 
32 import static com.android.car.CarLog.TAG_AUDIO;
33 
34 import android.car.builtin.util.Slogf;
35 import android.car.media.CarVolumeGroupInfo;
36 import android.car.oem.OemCarAudioVolumeRequest;
37 import android.car.oem.OemCarVolumeChangeInfo;
38 import android.media.AudioAttributes;
39 import android.media.audiopolicy.AudioPolicy;
40 import android.util.Log;
41 
42 import com.android.car.CarLocalServices;
43 import com.android.car.oem.CarOemProxyService;
44 
45 import java.util.List;
46 import java.util.Objects;
47 
48 final class CarAudioPolicyVolumeCallback extends AudioPolicy.AudioPolicyVolumeCallback {
49 
50     public static final boolean DEBUG = Slogf.isLoggable(TAG_AUDIO, Log.DEBUG);
51 
52     private final AudioManagerWrapper mAudioManager;
53     private final boolean mUseCarVolumeGroupMuting;
54     private final AudioPolicyVolumeCallbackInternal mVolumeCallback;
55     private final CarVolumeInfoWrapper mCarVolumeInfo;
56 
addVolumeCallbackToPolicy(AudioPolicy.Builder policyBuilder, CarAudioPolicyVolumeCallback callback)57     static void addVolumeCallbackToPolicy(AudioPolicy.Builder policyBuilder,
58             CarAudioPolicyVolumeCallback callback) {
59         Objects.requireNonNull(policyBuilder, "AudioPolicy.Builder cannot be null");
60         policyBuilder.setAudioPolicyVolumeCallback(callback);
61         if (DEBUG) {
62             Slogf.d(TAG_AUDIO, "Registered car audio policy volume callback");
63         }
64     }
65 
CarAudioPolicyVolumeCallback(AudioPolicyVolumeCallbackInternal volumeCallback, AudioManagerWrapper audioManager, CarVolumeInfoWrapper carVolumeInfo, boolean useCarVolumeGroupMuting)66     CarAudioPolicyVolumeCallback(AudioPolicyVolumeCallbackInternal volumeCallback,
67             AudioManagerWrapper audioManager, CarVolumeInfoWrapper carVolumeInfo,
68             boolean useCarVolumeGroupMuting) {
69         mVolumeCallback = Objects.requireNonNull(volumeCallback, "Volume Callback cannot be null");
70         mAudioManager = Objects.requireNonNull(audioManager, "AudioManager cannot be null");
71         mCarVolumeInfo = Objects.requireNonNull(carVolumeInfo, "Volume Info cannot be null");
72         mUseCarVolumeGroupMuting = useCarVolumeGroupMuting;
73     }
74 
onVolumeAdjustment(int adjustment, int zoneId)75     void onVolumeAdjustment(int adjustment, int zoneId) {
76         if (isOemDuckingServiceAvailable()) {
77             evaluateVolumeAdjustmentExternal(adjustment, zoneId);
78             return;
79         }
80         evaluateVolumeAdjustmentInternal(adjustment, zoneId);
81     }
82 
evaluateVolumeAdjustmentExternal(int adjustment, int zoneId)83     private void evaluateVolumeAdjustmentExternal(int adjustment, int zoneId) {
84         OemCarAudioVolumeRequest request = getCarAudioVolumeRequest(zoneId);
85 
86         if (DEBUG) {
87             Slogf.d(TAG_AUDIO, "evaluateVolumeAdjustmentExternal %s", request);
88         }
89 
90         OemCarVolumeChangeInfo changedVolume =
91                 CarLocalServices.getService(CarOemProxyService.class)
92                         .getCarOemAudioVolumeService()
93                         .getSuggestedGroupForVolumeChange(request, adjustment);
94 
95         if (changedVolume == null || !changedVolume.isVolumeChanged()) {
96             if (DEBUG) {
97                 Slogf.d(TAG_AUDIO, "No volume change for adjustment %s in zone %s",
98                         adjustToString(adjustment), zoneId);
99             }
100             return;
101         }
102 
103         CarVolumeGroupInfo info = changedVolume.getChangedVolumeGroup();
104         int flags = FLAG_FROM_KEY | FLAG_SHOW_UI;
105 
106         switch (adjustment) {
107             case ADJUST_LOWER: // Fallthrough
108             case ADJUST_RAISE:
109                 mVolumeCallback.onGroupVolumeChange(info.getZoneId(), info.getId(),
110                         info.getVolumeGainIndex(), flags);
111                 break;
112             case ADJUST_MUTE: // Fallthrough
113             case ADJUST_UNMUTE: // Fallthrough
114             case ADJUST_TOGGLE_MUTE:
115                 mVolumeCallback.onMuteChange(info.isMuted(), info.getZoneId(), info.getId(), flags);
116                 break;
117             case ADJUST_SAME: // Fallthrough
118             default:
119                 // There should be a change in volume info, compare against the current one
120                 CarVolumeGroupInfo currentInfo =
121                         mCarVolumeInfo.getVolumeGroupInfo(info.getZoneId(), info.getId());
122                 if (info.isMuted() != currentInfo.isMuted()) {
123                     mVolumeCallback.onMuteChange(info.isMuted(), info.getZoneId(),
124                             info.getId(), flags);
125                 }
126                 if (info.getVolumeGainIndex() != currentInfo.getVolumeGainIndex()) {
127                     mVolumeCallback.onGroupVolumeChange(info.getZoneId(),
128                             info.getId(), info.getVolumeGainIndex(), flags);
129                 }
130                 break;
131         }
132     }
133 
getCarAudioVolumeRequest(int zoneId)134     private OemCarAudioVolumeRequest getCarAudioVolumeRequest(int zoneId) {
135         List<CarVolumeGroupInfo> infos = mCarVolumeInfo.getVolumeGroupInfosForZone(zoneId);
136         List<AudioAttributes> activeAudioAttributes =
137                 mCarVolumeInfo.getActiveAudioAttributesForZone(zoneId);
138 
139         return new OemCarAudioVolumeRequest.Builder(zoneId)
140                 .setCarVolumeGroupInfos(infos)
141                 .setActivePlaybackAttributes(activeAudioAttributes)
142                 .setCallState(mCarVolumeInfo.getCallStateForZone(zoneId))
143                 .build();
144     }
145 
evaluateVolumeAdjustmentInternal(int adjustment, int zoneId)146     private void evaluateVolumeAdjustmentInternal(int adjustment, int zoneId) {
147         int groupId = mCarVolumeInfo.getVolumeGroupIdForAudioZone(zoneId);
148         if (groupId == INVALID_VOLUME_GROUP_ID) {
149             // This can happen if all volume groups are configured with dynamic devices and the
150             // configuration to allow volume key events to dynamic devices is disabled.
151             Slogf.w(TAG_AUDIO, "onVolumeAdjustment: %s but no suitable volume group id was found.",
152                     adjustToString(adjustment));
153             return;
154         }
155         boolean isMuted = isMuted(zoneId, groupId);
156 
157         if (Slogf.isLoggable(TAG_AUDIO, VERBOSE)) {
158             Slogf.v(TAG_AUDIO,
159                     "onVolumeAdjustment: %s suggested volume group: %s is muted: %b",
160                     adjustToString(adjustment),
161                     groupId, isMuted);
162         }
163 
164         int currentVolume = mCarVolumeInfo.getGroupVolume(zoneId, groupId);
165         int flags = FLAG_FROM_KEY | FLAG_SHOW_UI;
166         int minGain = mCarVolumeInfo.getGroupMinVolume(zoneId, groupId);
167         switch (adjustment) {
168             case ADJUST_LOWER:
169                 int minValue = Math.max(currentVolume - 1, minGain);
170                 if (isMuted)  {
171                     minValue = minGain;
172                 }
173                 mVolumeCallback.onGroupVolumeChange(zoneId, groupId, minValue, flags);
174                 break;
175             case ADJUST_RAISE:
176                 int maxValue = Math.min(currentVolume + 1,
177                         mCarVolumeInfo.getGroupMaxVolume(zoneId, groupId));
178                 if (isMuted)  {
179                     maxValue = minGain;
180                 }
181                 mVolumeCallback.onGroupVolumeChange(zoneId, groupId, maxValue, flags);
182                 break;
183             case ADJUST_MUTE:
184             case ADJUST_UNMUTE:
185                 mVolumeCallback.onMuteChange(adjustment == ADJUST_MUTE, zoneId, groupId, flags);
186                 break;
187             case ADJUST_TOGGLE_MUTE:
188                 mVolumeCallback.onMuteChange(!isMuted, zoneId, groupId, flags);
189                 break;
190             case ADJUST_SAME:
191             default:
192                 break;
193         }
194     }
195 
196     @Override
onVolumeAdjustment(int adjustment)197     public void onVolumeAdjustment(int adjustment) {
198         onVolumeAdjustment(adjustment, PRIMARY_AUDIO_ZONE);
199     }
200 
isMuted(int zoneId, int groupId)201     private boolean isMuted(int zoneId, int groupId) {
202         if (mUseCarVolumeGroupMuting) {
203             return mCarVolumeInfo.isVolumeGroupMuted(zoneId, groupId);
204         }
205         return mAudioManager.isMasterMuted();
206     }
207 
isOemDuckingServiceAvailable()208     private boolean isOemDuckingServiceAvailable() {
209         CarOemProxyService carService = CarLocalServices.getService(CarOemProxyService.class);
210 
211         return carService != null
212                 && carService.isOemServiceEnabled() && carService.isOemServiceReady()
213                 && carService.getCarOemAudioVolumeService() != null;
214     }
215 
216     public interface AudioPolicyVolumeCallbackInternal {
217 
218         /**
219          * Called when on volume key event has triggered a mute change for a particular volume group
220          */
onMuteChange(boolean mute, int zoneId, int groupId, int flags)221         void onMuteChange(boolean mute, int zoneId, int groupId, int flags);
222 
223         /**
224          * Called when on volume key event has triggered a volume
225          * change for a particular volume group
226          */
onGroupVolumeChange(int zoneId, int groupId, int volumeValue, int flags)227         void onGroupVolumeChange(int zoneId, int groupId, int volumeValue, int flags);
228     }
229 }
230