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