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