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 com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 21 import android.annotation.NonNull; 22 import android.media.AudioFocusInfo; 23 import android.util.IndentingPrintWriter; 24 import android.util.SparseArray; 25 26 import com.android.car.audio.CarZonesAudioFocus.CarFocusCallback; 27 import com.android.car.audio.hal.AudioControlWrapper; 28 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 29 import com.android.internal.annotations.GuardedBy; 30 import com.android.internal.annotations.VisibleForTesting; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Objects; 35 36 final class CarDucking implements CarFocusCallback { 37 private static final String TAG = CarDucking.class.getSimpleName(); 38 39 private final SparseArray<CarAudioZone> mCarAudioZones; 40 private final AudioControlWrapper mAudioControlWrapper; 41 private final Object mLock = new Object(); 42 43 @GuardedBy("mLock") 44 private final SparseArray<CarDuckingInfo> mCurrentDuckingInfo = new SparseArray<>(); 45 CarDucking(@onNull SparseArray<CarAudioZone> carAudioZones, @NonNull AudioControlWrapper audioControlWrapper)46 CarDucking(@NonNull SparseArray<CarAudioZone> carAudioZones, 47 @NonNull AudioControlWrapper audioControlWrapper) { 48 mCarAudioZones = Objects.requireNonNull(carAudioZones); 49 mAudioControlWrapper = Objects.requireNonNull(audioControlWrapper); 50 for (int i = 0; i < carAudioZones.size(); i++) { 51 int zoneId = carAudioZones.keyAt(i); 52 mCurrentDuckingInfo.put(zoneId, 53 new CarDuckingInfo(zoneId, new ArrayList<>(), new ArrayList<>(), new int[0])); 54 } 55 } 56 57 @VisibleForTesting getCurrentDuckingInfo()58 SparseArray<CarDuckingInfo> getCurrentDuckingInfo() { 59 synchronized (mLock) { 60 return mCurrentDuckingInfo; 61 } 62 } 63 64 @Override onFocusChange(int[] audioZoneIds, @NonNull SparseArray<List<AudioFocusInfo>> focusHoldersByZoneId)65 public void onFocusChange(int[] audioZoneIds, 66 @NonNull SparseArray<List<AudioFocusInfo>> focusHoldersByZoneId) { 67 synchronized (mLock) { 68 List<CarDuckingInfo> newDuckingInfos = new ArrayList<>(audioZoneIds.length); 69 for (int i = 0; i < audioZoneIds.length; i++) { 70 int zoneId = audioZoneIds[i]; 71 List<AudioFocusInfo> focusHolders = focusHoldersByZoneId.get(zoneId); 72 CarDuckingInfo newDuckingInfo = updateDuckingForZoneIdLocked(zoneId, focusHolders); 73 newDuckingInfos.add(newDuckingInfo); 74 } 75 mAudioControlWrapper.onDevicesToDuckChange(newDuckingInfos); 76 } 77 } 78 79 @GuardedBy("mLock") updateDuckingForZoneIdLocked(int zoneId, List<AudioFocusInfo> focusHolders)80 private CarDuckingInfo updateDuckingForZoneIdLocked(int zoneId, 81 List<AudioFocusInfo> focusHolders) { 82 CarDuckingInfo oldDuckingInfo = mCurrentDuckingInfo.get(zoneId); 83 CarDuckingInfo newDuckingInfo = generateNewDuckingInfoLocked(oldDuckingInfo, 84 focusHolders); 85 mCurrentDuckingInfo.put(zoneId, newDuckingInfo); 86 return newDuckingInfo; 87 } 88 89 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)90 public void dump(IndentingPrintWriter writer) { 91 writer.printf("*%s*\n", TAG); 92 writer.increaseIndent(); 93 synchronized (mLock) { 94 for (int i = 0; i < mCurrentDuckingInfo.size(); i++) { 95 mCurrentDuckingInfo.valueAt(i).dump(writer); 96 } 97 } 98 writer.decreaseIndent(); 99 } 100 101 @GuardedBy("mLock") generateNewDuckingInfoLocked(CarDuckingInfo oldDuckingInfo, List<AudioFocusInfo> focusHolders)102 private CarDuckingInfo generateNewDuckingInfoLocked(CarDuckingInfo oldDuckingInfo, 103 List<AudioFocusInfo> focusHolders) { 104 int zoneId = oldDuckingInfo.mZoneId; 105 CarAudioZone zone = mCarAudioZones.get(zoneId); 106 107 int[] usagesHoldingFocus = CarDuckingUtils.getUsagesHoldingFocus(focusHolders); 108 List<String> addressesToDuck = CarDuckingUtils.getAddressesToDuck(usagesHoldingFocus, zone); 109 List<String> addressesToUnduck = CarDuckingUtils.getAddressesToUnduck(addressesToDuck, 110 oldDuckingInfo.mAddressesToDuck); 111 112 return new CarDuckingInfo(zoneId, addressesToDuck, addressesToUnduck, usagesHoldingFocus); 113 } 114 } 115