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