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.audio.CarHalAudioUtils.usageToMetadata;
24 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
25 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.car.builtin.os.ServiceManagerHelper;
30 import android.car.builtin.util.Slogf;
31 import android.hardware.audio.common.PlaybackTrackMetadata;
32 import android.hardware.automotive.audiocontrol.AudioGainConfigInfo;
33 import android.hardware.automotive.audiocontrol.DuckingInfo;
34 import android.hardware.automotive.audiocontrol.IAudioControl;
35 import android.hardware.automotive.audiocontrol.IAudioGainCallback;
36 import android.hardware.automotive.audiocontrol.IFocusListener;
37 import android.hardware.automotive.audiocontrol.IModuleChangeCallback;
38 import android.hardware.automotive.audiocontrol.MutingInfo;
39 import android.media.audio.common.AudioPort;
40 import android.os.IBinder;
41 import android.os.RemoteException;
42 import android.util.Log;
43 
44 import com.android.car.CarLog;
45 import com.android.car.audio.CarAudioGainConfigInfo;
46 import com.android.car.audio.CarDuckingInfo;
47 import com.android.car.audio.CarHalAudioUtils;
48 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
49 import com.android.car.internal.annotation.AttributeUsage;
50 import com.android.car.internal.util.IndentingPrintWriter;
51 import com.android.internal.util.Preconditions;
52 
53 import java.util.ArrayList;
54 import java.util.List;
55 import java.util.Objects;
56 import java.util.concurrent.Executor;
57 import java.util.concurrent.Executors;
58 
59 /** Wrapper for AIDL interface for AudioControl HAL */
60 public final class AudioControlWrapperAidl implements AudioControlWrapper, IBinder.DeathRecipient {
61     static final String TAG = CarLog.tagFor(AudioControlWrapperAidl.class);
62 
63     private static final String AUDIO_CONTROL_SERVICE =
64             "android.hardware.automotive.audiocontrol.IAudioControl/default";
65 
66     private static final int AIDL_AUDIO_CONTROL_VERSION_1 = 1;
67     private static final int AIDL_AUDIO_CONTROL_VERSION_2 = 2;
68     private static final int AIDL_AUDIO_CONTROL_VERSION_3 = 3;
69 
70     private IBinder mBinder;
71     private IAudioControl mAudioControl;
72     private boolean mListenerRegistered = false;
73     private boolean mGainCallbackRegistered = false;
74     private boolean mModuleChangeCallbackRegistered;
75 
76     private AudioControlDeathRecipient mDeathRecipient;
77 
78     private Executor mExecutor = Executors.newSingleThreadExecutor();
79 
getService()80     public static @Nullable IBinder getService() {
81         return ServiceManagerHelper.waitForDeclaredService(AUDIO_CONTROL_SERVICE);
82     }
83 
AudioControlWrapperAidl(IBinder binder)84     public AudioControlWrapperAidl(IBinder binder) {
85         mBinder = Objects.requireNonNull(binder);
86         mAudioControl = IAudioControl.Stub.asInterface(binder);
87     }
88 
89     @Override
90     @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
unregisterFocusListener()91     public void unregisterFocusListener() {
92         // Focus listener will be unregistered by HAL automatically
93     }
94 
95     @Override
supportsFeature(int feature)96     public boolean supportsFeature(int feature) {
97         switch (feature) {
98             case AUDIOCONTROL_FEATURE_AUDIO_FOCUS:
99             case AUDIOCONTROL_FEATURE_AUDIO_DUCKING:
100             case AUDIOCONTROL_FEATURE_AUDIO_GROUP_MUTING:
101                 return true;
102             case AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA:
103             case AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK:
104                 try {
105                     return mAudioControl.getInterfaceVersion() > AIDL_AUDIO_CONTROL_VERSION_1;
106                 } catch (RemoteException e) {
107                     Slogf.w("supportsFeature Failed to get version for feature: " + feature, e);
108                 }
109                 return false;
110             case AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK:
111                 try {
112                     return mAudioControl.getInterfaceVersion() > AIDL_AUDIO_CONTROL_VERSION_2;
113                 } catch (RemoteException e) {
114                     Slogf.w("supportsFeature Failed to get version for feature: " + feature, e);
115                 }
116                 return false;
117             default:
118                 return false;
119         }
120     }
121 
122     @Override
registerFocusListener(HalFocusListener focusListener)123     public void registerFocusListener(HalFocusListener focusListener) {
124         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
125             Slogf.d(TAG, "Registering focus listener on AudioControl HAL");
126         }
127         IFocusListener listenerWrapper = new FocusListenerWrapper(focusListener);
128         try {
129             mAudioControl.registerFocusListener(listenerWrapper);
130         } catch (RemoteException e) {
131             Slogf.e(TAG, "Failed to register focus listener");
132             throw new IllegalStateException("IAudioControl#registerFocusListener failed", e);
133         }
134         mListenerRegistered = true;
135     }
136 
137     @Override
registerAudioGainCallback(HalAudioGainCallback gainCallback)138     public void registerAudioGainCallback(HalAudioGainCallback gainCallback) {
139         if (Log.isLoggable(TAG, Log.DEBUG)) {
140             Slogf.d(TAG, "Registering Audio Gain Callback on AudioControl HAL");
141         }
142         Objects.requireNonNull(gainCallback, "Audio Gain Callback can not be null");
143         IAudioGainCallback agc = new AudioGainCallbackWrapper(gainCallback);
144         try {
145             if (mAudioControl.getInterfaceVersion() < AIDL_AUDIO_CONTROL_VERSION_2) {
146                 Slogf.w(TAG, "Registering audio gain callback is not supported"
147                         + " for versions less than " + AIDL_AUDIO_CONTROL_VERSION_2);
148                 return;
149             }
150             mAudioControl.registerGainCallback(agc);
151         } catch (RemoteException e) {
152             Slogf.e(TAG, "Failed to register gain callback");
153             throw new IllegalStateException("IAudioControl#registerAudioGainCallback failed", e);
154         }
155         mGainCallbackRegistered = true;
156     }
157 
158     @Override
unregisterAudioGainCallback()159     public void unregisterAudioGainCallback() {
160         // Audio Gain Callback will be unregistered by HAL automatically
161     }
162 
163     @Override
onAudioFocusChange(PlaybackTrackMetadata metaData, int zoneId, int focusChange)164     public void onAudioFocusChange(PlaybackTrackMetadata metaData, int zoneId, int focusChange) {
165         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
166             Slogf.d(TAG, "onAudioFocusChange: metadata %s, zoneId %d, focusChanged %d", metaData,
167                     zoneId, focusChange);
168         }
169         try {
170             mAudioControl.onAudioFocusChangeWithMetaData(metaData, zoneId, focusChange);
171         } catch (RemoteException e) {
172             Slogf.d(TAG, "onAudioFocusChange: failed with metadata, retry with usage.");
173             onAudioFocusChange(metaData.usage, zoneId, focusChange);
174         }
175     }
176 
onAudioFocusChange(@ttributeUsage int usage, int zoneId, int focusChange)177     private void onAudioFocusChange(@AttributeUsage int usage, int zoneId, int focusChange) {
178         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
179             Slogf.d(TAG, "onAudioFocusChange: usage %s, zoneId %d, focusChanged %d",
180                     usageToString(usage), zoneId, focusChange);
181         }
182         try {
183             String usageName = usageToXsdString(usage);
184             mAudioControl.onAudioFocusChange(usageName, zoneId, focusChange);
185         } catch (RemoteException e) {
186             throw new IllegalStateException("Failed to query IAudioControl#onAudioFocusChange", e);
187         }
188     }
189 
190     @Override
191     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)192     public void dump(IndentingPrintWriter writer) {
193         writer.println("*AudioControlWrapperAidl*");
194         writer.increaseIndent();
195         try {
196             writer.printf("Aidl Version: %d\n", mAudioControl.getInterfaceVersion());
197         } catch (RemoteException e) {
198             Slogf.e(TAG, "dump getInterfaceVersion error", e);
199             writer.printf("Version: Could not be retrieved\n");
200         }
201         writer.printf("Focus listener registered on HAL? %b\n", mListenerRegistered);
202         writer.printf("Audio Gain Callback registered on HAL? %b\n", mGainCallbackRegistered);
203         writer.printf("Module change Callback set on HAL? %b\n", mModuleChangeCallbackRegistered);
204 
205         writer.println("Supported Features");
206         writer.increaseIndent();
207         writer.println("- AUDIOCONTROL_FEATURE_AUDIO_FOCUS");
208         writer.println("- AUDIOCONTROL_FEATURE_AUDIO_DUCKING");
209         if (supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA)) {
210             writer.println("- AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA");
211             writer.println("- AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK");
212         }
213         if (supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)) {
214             writer.println("- AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK");
215         }
216         writer.decreaseIndent();
217 
218         writer.decreaseIndent();
219     }
220 
221     @Override
setFadeTowardFront(float value)222     public void setFadeTowardFront(float value) {
223         try {
224             mAudioControl.setFadeTowardFront(value);
225         } catch (RemoteException e) {
226             Slogf.e(TAG, "setFadeTowardFront with " + value + " failed", e);
227         }
228     }
229 
230     @Override
setBalanceTowardRight(float value)231     public void setBalanceTowardRight(float value) {
232         try {
233             mAudioControl.setBalanceTowardRight(value);
234         } catch (RemoteException e) {
235             Slogf.e(TAG, "setBalanceTowardRight with " + value + " failed", e);
236         }
237     }
238 
239     @Override
onDevicesToDuckChange(@onNull List<CarDuckingInfo> carDuckingInfos)240     public void onDevicesToDuckChange(@NonNull List<CarDuckingInfo> carDuckingInfos) {
241         Objects.requireNonNull(carDuckingInfos);
242         DuckingInfo[] duckingInfos = new DuckingInfo[carDuckingInfos.size()];
243         for (int i = 0; i < carDuckingInfos.size(); i++) {
244             CarDuckingInfo info = Objects.requireNonNull(carDuckingInfos.get(i));
245             duckingInfos[i] = CarHalAudioUtils.generateDuckingInfo(info);
246         }
247 
248         try {
249             mAudioControl.onDevicesToDuckChange(duckingInfos);
250         } catch (RemoteException e) {
251             Slogf.e(TAG, e, "onDevicesToDuckChange failed");
252         }
253     }
254 
255     @Override
onDevicesToMuteChange(@onNull List<MutingInfo> carZonesMutingInfo)256     public void onDevicesToMuteChange(@NonNull List<MutingInfo> carZonesMutingInfo) {
257         Objects.requireNonNull(carZonesMutingInfo, "Muting info can not be null");
258         Preconditions.checkArgument(!carZonesMutingInfo.isEmpty(), "Muting info can not be empty");
259         MutingInfo[] mutingInfoToHal = carZonesMutingInfo
260                 .toArray(new MutingInfo[carZonesMutingInfo.size()]);
261         try {
262             mAudioControl.onDevicesToMuteChange(mutingInfoToHal);
263         } catch (RemoteException e) {
264             Slogf.e(TAG, e, "onDevicesToMuteChange failed");
265         }
266     }
267 
268     @Override
setModuleChangeCallback(HalAudioModuleChangeCallback moduleChangeCallback)269     public void setModuleChangeCallback(HalAudioModuleChangeCallback moduleChangeCallback) {
270         Objects.requireNonNull(moduleChangeCallback, "Module change callback can not be null");
271 
272         IModuleChangeCallback callback = new ModuleChangeCallbackWrapper(moduleChangeCallback);
273         mExecutor.execute(new Runnable() {
274             @Override
275             public void run() {
276                 try {
277                     if (mAudioControl.getInterfaceVersion() < AIDL_AUDIO_CONTROL_VERSION_3) {
278                         Slogf.w(TAG, "Setting module change callback is not supported"
279                                 + " for versions less than " + AIDL_AUDIO_CONTROL_VERSION_3);
280                         return;
281                     }
282                     mAudioControl.setModuleChangeCallback(callback);
283                     mModuleChangeCallbackRegistered = true;
284                 } catch (RemoteException e) {
285                     throw new IllegalStateException(
286                             "IAudioControl#setModuleChangeCallback failed", e);
287                 } catch (UnsupportedOperationException e) {
288                     Slogf.w(TAG, "Failed to set module change callback, feature not supported");
289                 } catch (IllegalStateException e) {
290                     // we hit this if car service crashed and restarted. lets clear callbacks and
291                     // try again one more time.
292                     Slogf.w(TAG, "Module change callback already set, retry after clearing");
293                     try {
294                         mAudioControl.clearModuleChangeCallback();
295                         mAudioControl.setModuleChangeCallback(callback);
296                         mModuleChangeCallbackRegistered = true;
297                     } catch (RemoteException ex) {
298                         throw new IllegalStateException(
299                                 "IAudioControl#setModuleChangeCallback failed (after retry)", ex);
300                     } catch (IllegalStateException ex) {
301                         Slogf.e(TAG, ex, "Failed to set module change callback (after retry)");
302                         // lets  not throw any exception since it may lead to car service failure
303                     }
304                 }
305             }
306         });
307     }
308 
309     @Override
clearModuleChangeCallback()310     public void clearModuleChangeCallback() {
311         mExecutor.execute(new Runnable() {
312             @Override
313             public void run() {
314                 try {
315                     if (mAudioControl.getInterfaceVersion() < AIDL_AUDIO_CONTROL_VERSION_3) {
316                         Slogf.w(TAG, "Clearing module change callback is not supported"
317                                 + " for versions less than " + AIDL_AUDIO_CONTROL_VERSION_3);
318                         return;
319                     }
320                     mAudioControl.clearModuleChangeCallback();
321                     mModuleChangeCallbackRegistered = false;
322                 } catch (RemoteException e) {
323                     throw new IllegalStateException(
324                             "IAudioControl#clearModuleChangeCallback failed", e);
325                 } catch (UnsupportedOperationException e) {
326                     Slogf.w(TAG, "Failed to clear module change callback, feature not supported");
327                 }
328             }
329         });
330     }
331 
332     @Override
linkToDeath(@ullable AudioControlDeathRecipient deathRecipient)333     public void linkToDeath(@Nullable AudioControlDeathRecipient deathRecipient) {
334         try {
335             mBinder.linkToDeath(this, 0);
336             mDeathRecipient = deathRecipient;
337         } catch (RemoteException e) {
338             throw new IllegalStateException("Call to IAudioControl#linkToDeath failed", e);
339         }
340     }
341 
342     @Override
unlinkToDeath()343     public void unlinkToDeath() {
344         mBinder.unlinkToDeath(this, 0);
345         mDeathRecipient = null;
346     }
347 
348     @Override
binderDied()349     public void binderDied() {
350         Slogf.w(TAG, "AudioControl HAL died. Fetching new handle");
351         mBinder.unlinkToDeath(this, 0);
352         mListenerRegistered = false;
353         mGainCallbackRegistered = false;
354         mModuleChangeCallbackRegistered = false;
355         mBinder = AudioControlWrapperAidl.getService();
356         mAudioControl = IAudioControl.Stub.asInterface(mBinder);
357         // TODO(b/284043199): Refactor the retry logic out and add delay between retry.
358         try {
359             mBinder.linkToDeath(this, 0);
360         } catch (RemoteException e) {
361             // Avoid crashing the binder thread.
362             Slogf.e(TAG, "Call to IAudioControl#linkToDeath failed", e);
363         }
364         if (mDeathRecipient != null) {
365             mDeathRecipient.serviceDied();
366         }
367     }
368 
369     private static final class FocusListenerWrapper extends IFocusListener.Stub {
370         private final HalFocusListener mListener;
371 
FocusListenerWrapper(HalFocusListener halFocusListener)372         FocusListenerWrapper(HalFocusListener halFocusListener) {
373             mListener = halFocusListener;
374         }
375 
376         @Override
377         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
getInterfaceVersion()378         public int getInterfaceVersion() {
379             return this.VERSION;
380         }
381 
382         @Override
383         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
getInterfaceHash()384         public String getInterfaceHash() {
385             return this.HASH;
386         }
387 
388         @Override
requestAudioFocus(String usage, int zoneId, int focusGain)389         public void requestAudioFocus(String usage, int zoneId, int focusGain) {
390             @AttributeUsage int usageValue = xsdStringToUsage(usage);
391             requestAudioFocus(usageValue, zoneId, focusGain);
392         }
393 
394         @Override
abandonAudioFocus(String usage, int zoneId)395         public void abandonAudioFocus(String usage, int zoneId) {
396             @AttributeUsage int usageValue = xsdStringToUsage(usage);
397             abandonAudioFocus(usageValue, zoneId);
398         }
399 
400         @Override
requestAudioFocusWithMetaData( PlaybackTrackMetadata playbackMetaData, int zoneId, int focusGain)401         public void requestAudioFocusWithMetaData(
402                 PlaybackTrackMetadata playbackMetaData, int zoneId, int focusGain) {
403             if (Log.isLoggable(TAG, Log.DEBUG)) {
404                 Slogf.d(TAG, "requestAudioFocusWithMetaData metadata=%s, zoneId=%d, focusGain=%d",
405                         playbackMetaData, zoneId, focusGain);
406             }
407             mListener.requestAudioFocus(playbackMetaData, zoneId, focusGain);
408         }
409 
410         @Override
abandonAudioFocusWithMetaData( PlaybackTrackMetadata playbackMetaData, int zoneId)411         public void abandonAudioFocusWithMetaData(
412                 PlaybackTrackMetadata playbackMetaData, int zoneId) {
413             if (Log.isLoggable(TAG, Log.DEBUG)) {
414                 Slogf.d(TAG, "abandonAudioFocusWithMetaData metadata=%s, zoneId=%d",
415                         playbackMetaData, zoneId);
416             }
417             mListener.abandonAudioFocus(playbackMetaData, zoneId);
418         }
419 
abandonAudioFocus(int usage, int zoneId)420         private void abandonAudioFocus(int usage, int zoneId) {
421             abandonAudioFocusWithMetaData(usageToMetadata(usage), zoneId);
422         }
423 
requestAudioFocus(int usage, int zoneId, int focusGain)424         private void requestAudioFocus(int usage, int zoneId, int focusGain) {
425             requestAudioFocusWithMetaData(usageToMetadata(usage), zoneId, focusGain);
426         }
427     }
428 
429     private static final class AudioGainCallbackWrapper extends IAudioGainCallback.Stub {
430         private @NonNull final HalAudioGainCallback mCallback;
431 
AudioGainCallbackWrapper(@onNull HalAudioGainCallback gainCallback)432         AudioGainCallbackWrapper(@NonNull HalAudioGainCallback gainCallback) {
433             mCallback = gainCallback;
434         }
435 
436         @Override
437         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
getInterfaceVersion()438         public int getInterfaceVersion() {
439             return VERSION;
440         }
441 
442         @Override
443         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
getInterfaceHash()444         public String getInterfaceHash() {
445             return HASH;
446         }
447 
448         @Override
onAudioDeviceGainsChanged(int[] halReasons, AudioGainConfigInfo[] gains)449         public void onAudioDeviceGainsChanged(int[] halReasons, AudioGainConfigInfo[] gains) {
450             List<CarAudioGainConfigInfo> carAudioGainConfigs = new ArrayList<>();
451             for (int index = 0; index < gains.length; index++) {
452                 AudioGainConfigInfo gain = gains[index];
453                 carAudioGainConfigs.add(new CarAudioGainConfigInfo(gain));
454             }
455             List<Integer> reasonsList = new ArrayList<>();
456             for (int index = 0; index < halReasons.length; index++) {
457                 int halReason = halReasons[index];
458                 if (!HalAudioGainCallback.isReasonValid(halReason)) {
459                     Slogf.e(
460                             TAG,
461                             "onAudioDeviceGainsChanged invalid reasons %d reported, skipped",
462                             halReason);
463                     continue;
464                 }
465                 reasonsList.add(halReason);
466             }
467             if (Log.isLoggable(TAG, Log.DEBUG)) {
468                 List<String> gainsString = new ArrayList<>();
469                 for (int i = 0; i < carAudioGainConfigs.size(); i++) {
470                     gainsString.add(carAudioGainConfigs.get(i).toString());
471                 }
472                 String gainsLiteral = String.join(",", gainsString);
473 
474                 List<String> reasonsString = new ArrayList<>();
475                 for (int i = 0; i < reasonsString.size(); i++) {
476                     reasonsString.add(HalAudioGainCallback.reasonToString(reasonsList.get(i)));
477                 }
478                 String reasonsLiteral = String.join(",", reasonsString);
479                 Slogf.d(
480                         TAG,
481                         "onAudioDeviceGainsChanged for reasons=[%s], gains=[%s]",
482                         reasonsLiteral,
483                         gainsLiteral);
484             }
485             mCallback.onAudioDeviceGainsChanged(reasonsList, carAudioGainConfigs);
486         }
487     }
488 
489     private static final class ModuleChangeCallbackWrapper extends IModuleChangeCallback.Stub {
490         private final HalAudioModuleChangeCallback mCallback;
491 
ModuleChangeCallbackWrapper(HalAudioModuleChangeCallback callback)492         ModuleChangeCallbackWrapper(HalAudioModuleChangeCallback callback) {
493             mCallback = callback;
494         }
495 
496         @Override
497         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
getInterfaceVersion()498         public int getInterfaceVersion() {
499             return this.VERSION;
500         }
501 
502         @Override
503         @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
getInterfaceHash()504         public String getInterfaceHash() {
505             return this.HASH;
506         }
507 
508         @Override
onAudioPortsChanged(AudioPort[] audioPorts)509         public void onAudioPortsChanged(AudioPort[] audioPorts) {
510             List<HalAudioDeviceInfo> halAudioDeviceInfos = new ArrayList<>();
511             for (int index = 0; index < audioPorts.length; index++) {
512                 AudioPort port = audioPorts[index];
513                 halAudioDeviceInfos.add(new HalAudioDeviceInfo(port));
514             }
515             mCallback.onAudioPortsChanged(halAudioDeviceInfos);
516         }
517     }
518 }
519