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