• 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 com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.car.media.CarVolumeGroupInfo;
22 import android.car.oem.OemCarAudioVolumeRequest;
23 import android.media.AudioAttributes;
24 import android.media.AudioFocusInfo;
25 import android.util.SparseArray;
26 
27 import com.android.car.CarLocalServices;
28 import com.android.car.audio.CarZonesAudioFocus.CarFocusCallback;
29 import com.android.car.audio.hal.AudioControlWrapper;
30 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
31 import com.android.car.internal.util.IndentingPrintWriter;
32 import com.android.car.oem.CarOemProxyService;
33 import com.android.internal.annotations.GuardedBy;
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Objects;
39 
40 final class CarDucking implements CarFocusCallback {
41     private static final String TAG = CarDucking.class.getSimpleName();
42 
43     private final SparseArray<CarAudioZone> mCarAudioZones;
44     private final AudioControlWrapper mAudioControlWrapper;
45     private final Object mLock = new Object();
46 
47     @GuardedBy("mLock")
48     private final SparseArray<CarDuckingInfo> mCurrentDuckingInfo = new SparseArray<>();
49 
CarDucking(SparseArray<CarAudioZone> carAudioZones, AudioControlWrapper audioControlWrapper)50     CarDucking(SparseArray<CarAudioZone> carAudioZones, AudioControlWrapper audioControlWrapper) {
51         mCarAudioZones = Objects.requireNonNull(carAudioZones, "Car audio zones can not be null");
52         mAudioControlWrapper = Objects.requireNonNull(audioControlWrapper,
53                         "Audio control wrapper can not be null");
54 
55         for (int i = 0; i < carAudioZones.size(); i++) {
56             int zoneId = carAudioZones.keyAt(i);
57             mCurrentDuckingInfo.put(
58                     zoneId,
59                     new CarDuckingInfo(
60                             zoneId, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()));
61         }
62     }
63 
64     @VisibleForTesting
getCurrentDuckingInfo()65     SparseArray<CarDuckingInfo> getCurrentDuckingInfo() {
66         synchronized (mLock) {
67             return mCurrentDuckingInfo;
68         }
69     }
70 
71     @Override
onFocusChange(int[] audioZoneIds, SparseArray<List<AudioFocusInfo>> focusHoldersByZoneId)72     public void onFocusChange(int[] audioZoneIds,
73             SparseArray<List<AudioFocusInfo>> focusHoldersByZoneId) {
74         synchronized (mLock) {
75             List<CarDuckingInfo> newDuckingInfos = new ArrayList<>(audioZoneIds.length);
76             for (int i = 0; i < audioZoneIds.length; i++) {
77                 int zoneId = audioZoneIds[i];
78                 List<AudioFocusInfo> focusHolders = focusHoldersByZoneId.get(zoneId);
79                 CarDuckingInfo newDuckingInfo = updateDuckingForZoneIdLocked(zoneId, focusHolders);
80                 newDuckingInfos.add(newDuckingInfo);
81             }
82             mAudioControlWrapper.onDevicesToDuckChange(newDuckingInfos);
83         }
84     }
85 
86     @GuardedBy("mLock")
updateDuckingForZoneIdLocked(int zoneId, List<AudioFocusInfo> focusHolders)87     private CarDuckingInfo updateDuckingForZoneIdLocked(int zoneId,
88             List<AudioFocusInfo> focusHolders) {
89         CarDuckingInfo oldDuckingInfo = mCurrentDuckingInfo.get(zoneId);
90         CarDuckingInfo newDuckingInfo = generateNewDuckingInfoLocked(oldDuckingInfo,
91                 focusHolders);
92         mCurrentDuckingInfo.put(zoneId, newDuckingInfo);
93         return newDuckingInfo;
94     }
95 
96     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)97     public void dump(IndentingPrintWriter writer) {
98         writer.printf("*%s*\n", TAG);
99         writer.increaseIndent();
100         synchronized (mLock) {
101             for (int i = 0; i < mCurrentDuckingInfo.size(); i++) {
102                 mCurrentDuckingInfo.valueAt(i).dump(writer);
103             }
104         }
105         writer.decreaseIndent();
106     }
107 
108     @GuardedBy("mLock")
generateNewDuckingInfoLocked(CarDuckingInfo oldDuckingInfo, List<AudioFocusInfo> focusHolders)109     private CarDuckingInfo generateNewDuckingInfoLocked(CarDuckingInfo oldDuckingInfo,
110             List<AudioFocusInfo> focusHolders) {
111         int zoneId = oldDuckingInfo.mZoneId;
112         CarAudioZone zone = mCarAudioZones.get(zoneId);
113 
114         List<CarVolumeGroupInfo> groupInfos = zone.getVolumeGroupInfos();
115 
116         List<AudioAttributes> attributesHoldingFocus =
117                 CarDuckingUtils.getAudioAttributesHoldingFocus(focusHolders);
118 
119         OemCarAudioVolumeRequest request = new OemCarAudioVolumeRequest.Builder(zoneId)
120                 .setActivePlaybackAttributes(attributesHoldingFocus)
121                 .setCarVolumeGroupInfos(groupInfos).build();
122 
123         List<AudioAttributes> audioAttributesToDuck = evaluateAttributesToDuck(request);
124 
125         return CarDuckingUtils.generateDuckingInfo(oldDuckingInfo, audioAttributesToDuck,
126                 attributesHoldingFocus, zone);
127     }
128 
evaluateAttributesToDuck(OemCarAudioVolumeRequest requestInfo)129     private List<AudioAttributes> evaluateAttributesToDuck(OemCarAudioVolumeRequest requestInfo)  {
130         return isOemDuckingServiceAvailable() ? evaluateAttributesToDuckExternally(requestInfo) :
131                 evaluateAttributesToDuckInternally(requestInfo);
132     }
133 
evaluateAttributesToDuckExternally( OemCarAudioVolumeRequest requestInfo)134     private List<AudioAttributes> evaluateAttributesToDuckExternally(
135             OemCarAudioVolumeRequest requestInfo) {
136         return CarLocalServices.getService(CarOemProxyService.class).getCarOemAudioDuckingService()
137                 .evaluateAttributesToDuck(requestInfo);
138     }
139 
evaluateAttributesToDuckInternally( OemCarAudioVolumeRequest requestInfo)140     private List<AudioAttributes> evaluateAttributesToDuckInternally(
141             OemCarAudioVolumeRequest requestInfo) {
142         return CarAudioContext.evaluateAudioAttributesToDuck(
143                 requestInfo.getActivePlaybackAttributes());
144     }
145 
isOemDuckingServiceAvailable()146     private boolean isOemDuckingServiceAvailable() {
147         CarOemProxyService carService = CarLocalServices.getService(CarOemProxyService.class);
148 
149         return carService != null
150                 && carService.isOemServiceEnabled() && carService.isOemServiceReady()
151                 && carService.getCarOemAudioDuckingService() != null;
152     }
153 }
154