• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.hal;
18 
19 import static android.car.builtin.media.AudioManagerHelper.usageToString;
20 import static android.car.builtin.media.AudioManagerHelper.usageToXsdString;
21 import static android.car.builtin.media.AudioManagerHelper.xsdStringToUsage;
22 
23 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.car.builtin.os.ServiceManagerHelper;
28 import android.car.builtin.util.Slogf;
29 import android.hardware.audio.common.PlaybackTrackMetadata;
30 import android.hardware.automotive.audiocontrol.AudioGainConfigInfo;
31 import android.hardware.automotive.audiocontrol.DuckingInfo;
32 import android.hardware.automotive.audiocontrol.IAudioControl;
33 import android.hardware.automotive.audiocontrol.IAudioGainCallback;
34 import android.hardware.automotive.audiocontrol.IFocusListener;
35 import android.hardware.automotive.audiocontrol.MutingInfo;
36 import android.os.IBinder;
37 import android.os.RemoteException;
38 import android.util.Log;
39 
40 import com.android.car.CarLog;
41 import com.android.car.audio.CarAudioGainConfigInfo;
42 import com.android.car.audio.CarDuckingInfo;
43 import com.android.car.audio.CarHalAudioUtils;
44 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
45 import com.android.car.internal.annotation.AttributeUsage;
46 import com.android.car.internal.util.IndentingPrintWriter;
47 import com.android.internal.util.Preconditions;
48 
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.Objects;
52 import java.util.stream.Collectors;
53 
54 /** Wrapper for AIDL interface for AudioControl HAL */
55 public final class AudioControlWrapperAidl implements AudioControlWrapper, IBinder.DeathRecipient {
56     static final String TAG = CarLog.tagFor(AudioControlWrapperAidl.class);
57 
58     private static final String AUDIO_CONTROL_SERVICE =
59             "android.hardware.automotive.audiocontrol.IAudioControl/default";
60 
61     private static final int AIDL_AUDIO_CONTROL_VERSION_1 = 1;
62 
63     private IBinder mBinder;
64     private IAudioControl mAudioControl;
65     private boolean mListenerRegistered = false;
66     private boolean mGainCallbackRegistered = false;
67 
68     private AudioControlDeathRecipient mDeathRecipient;
69 
getService()70     public static @Nullable IBinder getService() {
71         return ServiceManagerHelper.waitForDeclaredService(AUDIO_CONTROL_SERVICE);
72     }
73 
AudioControlWrapperAidl(IBinder binder)74     public AudioControlWrapperAidl(IBinder binder) {
75         mBinder = Objects.requireNonNull(binder);
76         mAudioControl = IAudioControl.Stub.asInterface(binder);
77     }
78 
79     @Override
unregisterFocusListener()80     public void unregisterFocusListener() {
81         // Focus listener will be unregistered by HAL automatically
82     }
83 
84     @Override
supportsFeature(int feature)85     public boolean supportsFeature(int feature) {
86         switch (feature) {
87             case AUDIOCONTROL_FEATURE_AUDIO_FOCUS:
88             case AUDIOCONTROL_FEATURE_AUDIO_DUCKING:
89             case AUDIOCONTROL_FEATURE_AUDIO_GROUP_MUTING:
90                 return true;
91             case AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA:
92             case AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK:
93                 try {
94                     return mAudioControl.getInterfaceVersion() > AIDL_AUDIO_CONTROL_VERSION_1;
95                 } catch (RemoteException e) {
96                     Slogf.w("supportsFeature Failed to get version for feature: " + feature, e);
97                 }
98                 // Fallthrough
99             default:
100                 return false;
101         }
102     }
103 
104     @Override
registerFocusListener(HalFocusListener focusListener)105     public void registerFocusListener(HalFocusListener focusListener) {
106         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
107             Slogf.d(TAG, "Registering focus listener on AudioControl HAL");
108         }
109         IFocusListener listenerWrapper = new FocusListenerWrapper(focusListener);
110         try {
111             mAudioControl.registerFocusListener(listenerWrapper);
112         } catch (RemoteException e) {
113             Slogf.e(TAG, "Failed to register focus listener");
114             throw new IllegalStateException("IAudioControl#registerFocusListener failed", e);
115         }
116         mListenerRegistered = true;
117     }
118 
119     @Override
registerAudioGainCallback(HalAudioGainCallback gainCallback)120     public void registerAudioGainCallback(HalAudioGainCallback gainCallback) {
121         if (Log.isLoggable(TAG, Log.DEBUG)) {
122             Slogf.d(TAG, "Registering Audio Gain Callback on AudioControl HAL");
123         }
124         Objects.requireNonNull(gainCallback, "Audio Gain Callback can not be null");
125         IAudioGainCallback agc = new AudioGainCallbackWrapper(gainCallback);
126         try {
127             mAudioControl.registerGainCallback(agc);
128         } catch (RemoteException e) {
129             Slogf.e(TAG, "Failed to register gain callback");
130             throw new IllegalStateException("IAudioControl#registerAudioGainCallback failed", e);
131         }
132         mGainCallbackRegistered = true;
133     }
134 
135     @Override
unregisterAudioGainCallback()136     public void unregisterAudioGainCallback() {
137         // Audio Gain Callback will be unregistered by HAL automatically
138     }
139 
140     @Override
onAudioFocusChange(@ttributeUsage int usage, int zoneId, int focusChange)141     public void onAudioFocusChange(@AttributeUsage int usage, int zoneId, int focusChange) {
142         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
143             Slogf.d(TAG, "onAudioFocusChange: usage " + usageToString(usage)
144                     + ", zoneId " + zoneId + ", focusChange " + focusChange);
145         }
146         try {
147             String usageName = usageToXsdString(usage);
148             mAudioControl.onAudioFocusChange(usageName, zoneId, focusChange);
149         } catch (RemoteException e) {
150             throw new IllegalStateException("Failed to query IAudioControl#onAudioFocusChange", e);
151         }
152     }
153 
154     @Override
155     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)156     public void dump(IndentingPrintWriter writer) {
157         writer.println("*AudioControlWrapperAidl*");
158         writer.increaseIndent();
159         try {
160             writer.printf("Aidl Version: %d\n", mAudioControl.getInterfaceVersion());
161         } catch (RemoteException e) {
162             Slogf.e(TAG, "dump getInterfaceVersion error", e);
163             writer.printf("Version: Could not be retrieved\n");
164         }
165         writer.printf("Focus listener registered on HAL? %b\n", mListenerRegistered);
166         writer.printf("Audio Gain Callback registered on HAL? %b\n", mGainCallbackRegistered);
167 
168         writer.println("Supported Features");
169         writer.increaseIndent();
170         writer.println("- AUDIOCONTROL_FEATURE_AUDIO_FOCUS");
171         writer.println("- AUDIOCONTROL_FEATURE_AUDIO_DUCKING");
172         if (supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA)) {
173             writer.println("- AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA");
174             writer.println("- AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK");
175         }
176         writer.decreaseIndent();
177 
178         writer.decreaseIndent();
179     }
180 
181     @Override
setFadeTowardFront(float value)182     public void setFadeTowardFront(float value) {
183         try {
184             mAudioControl.setFadeTowardFront(value);
185         } catch (RemoteException e) {
186             Slogf.e(TAG, "setFadeTowardFront with " + value + " failed", e);
187         }
188     }
189 
190     @Override
setBalanceTowardRight(float value)191     public void setBalanceTowardRight(float value) {
192         try {
193             mAudioControl.setBalanceTowardRight(value);
194         } catch (RemoteException e) {
195             Slogf.e(TAG, "setBalanceTowardRight with " + value + " failed", e);
196         }
197     }
198 
199     @Override
onDevicesToDuckChange(@onNull List<CarDuckingInfo> carDuckingInfos)200     public void onDevicesToDuckChange(@NonNull List<CarDuckingInfo> carDuckingInfos) {
201         Objects.requireNonNull(carDuckingInfos);
202         DuckingInfo[] duckingInfos = new DuckingInfo[carDuckingInfos.size()];
203         for (int i = 0; i < carDuckingInfos.size(); i++) {
204             CarDuckingInfo info = Objects.requireNonNull(carDuckingInfos.get(i));
205             duckingInfos[i] = CarHalAudioUtils.generateDuckingInfo(info);
206         }
207 
208         try {
209             mAudioControl.onDevicesToDuckChange(duckingInfos);
210         } catch (RemoteException e) {
211             Slogf.e(TAG, e, "onDevicesToDuckChange failed");
212         }
213     }
214 
215     @Override
onDevicesToMuteChange(@onNull List<MutingInfo> carZonesMutingInfo)216     public void onDevicesToMuteChange(@NonNull List<MutingInfo> carZonesMutingInfo) {
217         Objects.requireNonNull(carZonesMutingInfo, "Muting info can not be null");
218         Preconditions.checkArgument(!carZonesMutingInfo.isEmpty(), "Muting info can not be empty");
219         MutingInfo[] mutingInfoToHal = carZonesMutingInfo
220                 .toArray(new MutingInfo[carZonesMutingInfo.size()]);
221         try {
222             mAudioControl.onDevicesToMuteChange(mutingInfoToHal);
223         } catch (RemoteException e) {
224             Slogf.e(TAG, e, "onDevicesToMuteChange failed");
225         }
226     }
227 
228     @Override
linkToDeath(@ullable AudioControlDeathRecipient deathRecipient)229     public void linkToDeath(@Nullable AudioControlDeathRecipient deathRecipient) {
230         try {
231             mBinder.linkToDeath(this, 0);
232             mDeathRecipient = deathRecipient;
233         } catch (RemoteException e) {
234             throw new IllegalStateException("Call to IAudioControl#linkToDeath failed", e);
235         }
236     }
237 
238     @Override
unlinkToDeath()239     public void unlinkToDeath() {
240         mBinder.unlinkToDeath(this, 0);
241         mDeathRecipient = null;
242     }
243 
244     @Override
binderDied()245     public void binderDied() {
246         Slogf.w(TAG, "AudioControl HAL died. Fetching new handle");
247         mListenerRegistered = false;
248         mGainCallbackRegistered = false;
249         mBinder = AudioControlWrapperAidl.getService();
250         mAudioControl = IAudioControl.Stub.asInterface(mBinder);
251         linkToDeath(mDeathRecipient);
252         if (mDeathRecipient != null) {
253             mDeathRecipient.serviceDied();
254         }
255     }
256 
257     private static final class FocusListenerWrapper extends IFocusListener.Stub {
258         private final HalFocusListener mListener;
259 
FocusListenerWrapper(HalFocusListener halFocusListener)260         FocusListenerWrapper(HalFocusListener halFocusListener) {
261             mListener = halFocusListener;
262         }
263 
264         @Override
getInterfaceVersion()265         public int getInterfaceVersion() {
266             return this.VERSION;
267         }
268 
269         @Override
getInterfaceHash()270         public String getInterfaceHash() {
271             return this.HASH;
272         }
273 
274         @Override
requestAudioFocus(String usage, int zoneId, int focusGain)275         public void requestAudioFocus(String usage, int zoneId, int focusGain) {
276             @AttributeUsage int usageValue = xsdStringToUsage(usage);
277             mListener.requestAudioFocus(usageValue, zoneId, focusGain);
278         }
279 
280         @Override
abandonAudioFocus(String usage, int zoneId)281         public void abandonAudioFocus(String usage, int zoneId) {
282             @AttributeUsage int usageValue = xsdStringToUsage(usage);
283             mListener.abandonAudioFocus(usageValue, zoneId);
284         }
285 
286         @Override
requestAudioFocusWithMetaData( PlaybackTrackMetadata playbackMetaData, int zoneId, int focusGain)287         public void requestAudioFocusWithMetaData(
288                 PlaybackTrackMetadata playbackMetaData, int zoneId, int focusGain) {
289             if (Log.isLoggable(TAG, Log.DEBUG)) {
290                 Slogf.d(TAG, "requestAudioFocusWithMetaData metadata=" + playbackMetaData
291                         + ", zoneId=" + zoneId + ", focusGain=" + focusGain);
292             }
293             // TODO(b/224885748): Add missing focus management
294         }
295 
296         @Override
abandonAudioFocusWithMetaData( PlaybackTrackMetadata playbackMetaData, int zoneId)297         public void abandonAudioFocusWithMetaData(
298                 PlaybackTrackMetadata playbackMetaData, int zoneId) {
299             if (Log.isLoggable(TAG, Log.DEBUG)) {
300                 Slogf.d(TAG, "abandonAudioFocusWithMetaData metadata=" + playbackMetaData
301                         + ", zoneId=" + zoneId);
302             }
303             // TODO(b/224885748): Add missing focus management
304         }
305 
306     }
307 
308     private static final class AudioGainCallbackWrapper extends IAudioGainCallback.Stub {
309         private @NonNull final HalAudioGainCallback mCallback;
310 
AudioGainCallbackWrapper(@onNull HalAudioGainCallback gainCallback)311         AudioGainCallbackWrapper(@NonNull HalAudioGainCallback gainCallback) {
312             mCallback = gainCallback;
313         }
314 
315         @Override
getInterfaceVersion()316         public int getInterfaceVersion() {
317             return VERSION;
318         }
319 
320         @Override
getInterfaceHash()321         public String getInterfaceHash() {
322             return HASH;
323         }
324 
325         @Override
onAudioDeviceGainsChanged(int[] halReasons, AudioGainConfigInfo[] gains)326         public void onAudioDeviceGainsChanged(int[] halReasons, AudioGainConfigInfo[] gains) {
327             List<CarAudioGainConfigInfo> carAudioGainConfigs = new ArrayList<>();
328             for (int index = 0; index < gains.length; index++) {
329                 AudioGainConfigInfo gain = gains[index];
330                 carAudioGainConfigs.add(new CarAudioGainConfigInfo(gain));
331             }
332             List<Integer> reasonsList = new ArrayList<>();
333             for (int index = 0; index < halReasons.length; index++) {
334                 int halReason = halReasons[index];
335                 if (!HalAudioGainCallback.isReasonValid(halReason)) {
336                     Slogf.e(
337                             TAG,
338                             "onAudioDeviceGainsChanged invalid reasons %d reported, skipped",
339                             halReason);
340                     continue;
341                 }
342                 reasonsList.add(halReason);
343             }
344             if (Log.isLoggable(TAG, Log.DEBUG)) {
345                 String gainsLiteral =
346                         carAudioGainConfigs.stream()
347                                 .map(gain -> gain.toString())
348                                 .collect(Collectors.joining(","));
349                 String reasonsLiteral =
350                         reasonsList.stream()
351                                 .map(HalAudioGainCallback::reasonToString)
352                                 .collect(Collectors.joining(","));
353                 Slogf.d(
354                         TAG,
355                         "onAudioDeviceGainsChanged for reasons=[%s], gains=[%s]",
356                         reasonsLiteral,
357                         gainsLiteral);
358             }
359             mCallback.onAudioDeviceGainsChanged(reasonsList, carAudioGainConfigs);
360         }
361     }
362 }
363