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