1 /* 2 * Copyright (C) 2018 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 package com.android.car.audio; 17 18 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_ATTENUATION_CHANGED; 19 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED; 20 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_BLOCKED_CHANGED; 21 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED; 22 23 import static com.android.car.audio.hal.HalAudioGainCallback.reasonToString; 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.annotation.UserIdInt; 30 import android.car.builtin.util.Slogf; 31 import android.car.media.CarVolumeGroupInfo; 32 import android.media.AudioAttributes; 33 import android.media.AudioDeviceInfo; 34 import android.media.AudioManager; 35 import android.os.UserHandle; 36 import android.util.ArrayMap; 37 import android.util.SparseArray; 38 39 import com.android.car.CarLog; 40 import com.android.car.audio.CarAudioContext.AudioContext; 41 import com.android.car.audio.hal.HalAudioDeviceInfo; 42 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 43 import com.android.car.internal.util.IndentingPrintWriter; 44 import com.android.internal.annotations.GuardedBy; 45 import com.android.internal.util.Preconditions; 46 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.List; 50 import java.util.Objects; 51 52 /** 53 * A class encapsulates a volume group in car. 54 * 55 * Interface holding volume interface APIs and also common code for: 56 * 57 * -volume groups using {@link AudioManager#setAudioPortGain} to control the volume 58 * while the audioserver resource config_useFixedVolume is set. 59 * 60 * -volume groups relying on audioserver to control the volume and access using 61 * {@link AudioManager#setVolumeIndexForAttributes(AudioAttributes, int, int)} and all other 62 * related volume APIs. 63 * Gain may either be controlled on hardware amplifier using Audio HAL setaudioPortConfig if the 64 * correlated audio device port defines a gain controller with attribute name="useForVolume" set 65 * or in software using the port id in Audio flinger. 66 * Gains are set only when activity is detected on the given audio device port (Mixer thread, or 67 * {@link android.media.HwAudioSource} realized through a software bridge or hardware bridge. 68 * 69 */ 70 /* package */ abstract class CarVolumeGroup { 71 public static final int UNINITIALIZED = -1; 72 73 private final boolean mUseCarVolumeGroupMute; 74 private final boolean mHasCriticalAudioContexts; 75 private final CarAudioSettings mSettingsManager; 76 protected final int mId; 77 private final String mName; 78 protected final int mZoneId; 79 protected final int mConfigId; 80 protected final SparseArray<String> mContextToAddress; 81 protected final ArrayMap<String, CarAudioDeviceInfo> mAddressToCarAudioDeviceInfo; 82 83 protected final Object mLock = new Object(); 84 private final CarAudioContext mCarAudioContext; 85 86 @GuardedBy("mLock") 87 protected int mStoredGainIndex; 88 89 @GuardedBy("mLock") 90 protected int mCurrentGainIndex = UNINITIALIZED; 91 92 /** 93 * Mute state for requests coming from clients. See {@link #mIsHalMuted} for state of requests 94 * coming from HAL. 95 */ 96 @GuardedBy("mLock") 97 protected boolean mIsMuted; 98 @GuardedBy("mLock") 99 protected @UserIdInt int mUserId = UserHandle.CURRENT.getIdentifier(); 100 101 /** 102 * Attenuated gain is set to {@link #UNINITIALIZED} till attenuation explicitly reported by 103 * {@link com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or 104 * more {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason is cleared, 105 * it returns back to {@link #UNINITIALIZED}. 106 */ 107 @GuardedBy("mLock") 108 protected int mAttenuatedGainIndex = UNINITIALIZED; 109 110 /** 111 * Limitation gain is set to max gain value till limitation explicitly reported by {@link 112 * com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or more 113 * {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason is cleared, it 114 * returns back to max. 115 */ 116 @GuardedBy("mLock") 117 protected int mLimitedGainIndex; 118 119 /** 120 * Blocked gain is set to {@link #UNINITIALIZED} till blocking case explicitly reported by 121 * {@link com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or 122 * more {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason is cleared, 123 * it returns back to {@link #UNINITIALIZED}. 124 */ 125 @GuardedBy("mLock") 126 protected int mBlockedGainIndex = UNINITIALIZED; 127 128 /** 129 * The default state of HAL mute is {@code false} until HAL explicitly reports through 130 * {@link com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged} for one or 131 * more {@link android.hardware.automotive.audiocontrol.Reasons}. When the reason 132 * is cleared, it is reset. See {@link #mIsMuted} for state of requests coming from clients. 133 */ 134 @GuardedBy("mLock") 135 private boolean mIsHalMuted = false; 136 137 /** 138 * Reasons list currently reported for this port by {@link 139 * com.android.car.audio.hal.HalAudioGainCallback#onAudioDeviceGainsChanged}. 140 */ 141 protected List<Integer> mReasons = new ArrayList<>(); 142 CarVolumeGroup(CarAudioContext carAudioContext, CarAudioSettings settingsManager, SparseArray<String> contextToAddress, ArrayMap<String, CarAudioDeviceInfo> addressToCarAudioDeviceInfo, int zoneId, int configId, int volumeGroupId, String name, boolean useCarVolumeGroupMute)143 protected CarVolumeGroup(CarAudioContext carAudioContext, CarAudioSettings settingsManager, 144 SparseArray<String> contextToAddress, ArrayMap<String, 145 CarAudioDeviceInfo> addressToCarAudioDeviceInfo, int zoneId, int configId, 146 int volumeGroupId, String name, boolean useCarVolumeGroupMute) { 147 mSettingsManager = settingsManager; 148 mContextToAddress = contextToAddress; 149 mAddressToCarAudioDeviceInfo = addressToCarAudioDeviceInfo; 150 mCarAudioContext = carAudioContext; 151 mZoneId = zoneId; 152 mConfigId = configId; 153 mId = volumeGroupId; 154 mName = Objects.requireNonNull(name, "Volume group name cannot be null"); 155 mUseCarVolumeGroupMute = useCarVolumeGroupMute; 156 List<AudioAttributes> volumeAttributes = new ArrayList<>(); 157 for (int index = 0; index < contextToAddress.size(); index++) { 158 int context = contextToAddress.keyAt(index); 159 List<AudioAttributes> audioAttributes = 160 Arrays.asList(mCarAudioContext.getAudioAttributesForContext(context)); 161 volumeAttributes.addAll(audioAttributes); 162 } 163 164 mHasCriticalAudioContexts = containsCriticalAttributes(volumeAttributes); 165 } 166 init()167 void init() { 168 synchronized (mLock) { 169 mStoredGainIndex = mSettingsManager.getStoredVolumeGainIndexForUser( 170 mUserId, mZoneId, mConfigId, mId); 171 updateCurrentGainIndexLocked(); 172 } 173 } 174 175 @GuardedBy("mLock") hasPendingAttenuationReasonsLocked()176 protected boolean hasPendingAttenuationReasonsLocked() { 177 return !mReasons.isEmpty(); 178 } 179 180 @GuardedBy("mLock") setBlockedLocked(int blockedIndex)181 protected void setBlockedLocked(int blockedIndex) { 182 mBlockedGainIndex = blockedIndex; 183 } 184 185 @GuardedBy("mLock") resetBlockedLocked()186 protected void resetBlockedLocked() { 187 setBlockedLocked(UNINITIALIZED); 188 } 189 190 @GuardedBy("mLock") isBlockedLocked()191 protected boolean isBlockedLocked() { 192 return mBlockedGainIndex != UNINITIALIZED; 193 } 194 195 @GuardedBy("mLock") setLimitLocked(int limitIndex)196 protected void setLimitLocked(int limitIndex) { 197 mLimitedGainIndex = limitIndex; 198 } 199 200 @GuardedBy("mLock") resetLimitLocked()201 protected void resetLimitLocked() { 202 setLimitLocked(getMaxGainIndex()); 203 } 204 205 @GuardedBy("mLock") isLimitedLocked()206 protected boolean isLimitedLocked() { 207 return mLimitedGainIndex != getMaxGainIndex(); 208 } 209 210 @GuardedBy("mLock") isOverLimitLocked()211 protected boolean isOverLimitLocked() { 212 return isOverLimitLocked(mCurrentGainIndex); 213 } 214 215 @GuardedBy("mLock") isOverLimitLocked(int index)216 protected boolean isOverLimitLocked(int index) { 217 return isLimitedLocked() && (index > mLimitedGainIndex); 218 } 219 220 @GuardedBy("mLock") setAttenuatedGainLocked(int attenuatedGainIndex)221 protected void setAttenuatedGainLocked(int attenuatedGainIndex) { 222 mAttenuatedGainIndex = attenuatedGainIndex; 223 } 224 225 @GuardedBy("mLock") resetAttenuationLocked()226 protected void resetAttenuationLocked() { 227 setAttenuatedGainLocked(UNINITIALIZED); 228 } 229 230 @GuardedBy("mLock") isAttenuatedLocked()231 protected boolean isAttenuatedLocked() { 232 return mAttenuatedGainIndex != UNINITIALIZED; 233 } 234 235 @GuardedBy("mLock") setHalMuteLocked(boolean mute)236 private void setHalMuteLocked(boolean mute) { 237 mIsHalMuted = mute; 238 } 239 240 @GuardedBy("mLock") isHalMutedLocked()241 protected boolean isHalMutedLocked() { 242 return mIsHalMuted; 243 } 244 setBlocked(int blockedIndex)245 void setBlocked(int blockedIndex) { 246 synchronized (mLock) { 247 setBlockedLocked(blockedIndex); 248 } 249 } 250 resetBlocked()251 void resetBlocked() { 252 synchronized (mLock) { 253 resetBlockedLocked(); 254 } 255 } 256 isBlocked()257 boolean isBlocked() { 258 synchronized (mLock) { 259 return isBlockedLocked(); 260 } 261 } 262 setLimit(int limitIndex)263 void setLimit(int limitIndex) { 264 synchronized (mLock) { 265 setLimitLocked(limitIndex); 266 } 267 } 268 resetLimit()269 void resetLimit() { 270 synchronized (mLock) { 271 resetLimitLocked(); 272 } 273 } 274 isLimited()275 boolean isLimited() { 276 synchronized (mLock) { 277 return isLimitedLocked(); 278 } 279 } 280 isOverLimit()281 boolean isOverLimit() { 282 synchronized (mLock) { 283 return isOverLimitLocked(); 284 } 285 } 286 setAttenuatedGain(int attenuatedGainIndex)287 void setAttenuatedGain(int attenuatedGainIndex) { 288 synchronized (mLock) { 289 setAttenuatedGainLocked(attenuatedGainIndex); 290 } 291 } 292 resetAttenuation()293 void resetAttenuation() { 294 synchronized (mLock) { 295 resetAttenuationLocked(); 296 } 297 } 298 isAttenuated()299 boolean isAttenuated() { 300 synchronized (mLock) { 301 return isAttenuatedLocked(); 302 } 303 } 304 305 @Nullable getCarAudioDeviceInfoForAddress(String address)306 CarAudioDeviceInfo getCarAudioDeviceInfoForAddress(String address) { 307 return mAddressToCarAudioDeviceInfo.get(address); 308 } 309 310 @AudioContext getContexts()311 int[] getContexts() { 312 final int[] carAudioContexts = new int[mContextToAddress.size()]; 313 for (int i = 0; i < carAudioContexts.length; i++) { 314 carAudioContexts[i] = mContextToAddress.keyAt(i); 315 } 316 return carAudioContexts; 317 } 318 319 /** 320 * Returns the id of the volume group. 321 * <p> Note that all clients are already developed in the way that when they get the number of 322 * volume group, they will then address a given volume group using its id as if the id was the 323 * index of the array of group (aka 0 to length - 1). 324 */ getId()325 int getId() { 326 return mId; 327 } 328 getName()329 String getName() { 330 return mName; 331 } 332 333 /** 334 * Returns the devices address for the given context 335 * or {@code null} if the context does not exist in the volume group 336 */ 337 @Nullable getAddressForContext(int audioContext)338 String getAddressForContext(int audioContext) { 339 return mContextToAddress.get(audioContext); 340 } 341 342 /** 343 * Returns the audio devices for the given context 344 * or {@code null} if the context does not exist in the volume group 345 */ 346 @Nullable getAudioDeviceForContext(int audioContext)347 AudioDeviceInfo getAudioDeviceForContext(int audioContext) { 348 String address = getAddressForContext(audioContext); 349 if (address == null) { 350 return null; 351 } 352 353 CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.get(address); 354 if (info == null) { 355 return null; 356 } 357 358 return info.getAudioDeviceInfo(); 359 } 360 361 @AudioContext getContextsForAddress(@onNull String address)362 List<Integer> getContextsForAddress(@NonNull String address) { 363 List<Integer> carAudioContexts = new ArrayList<>(); 364 for (int i = 0; i < mContextToAddress.size(); i++) { 365 String value = mContextToAddress.valueAt(i); 366 if (address.equals(value)) { 367 carAudioContexts.add(mContextToAddress.keyAt(i)); 368 } 369 } 370 return carAudioContexts; 371 } 372 getAddresses()373 List<String> getAddresses() { 374 return new ArrayList<>(mAddressToCarAudioDeviceInfo.keySet()); 375 } 376 getAllSupportedUsagesForAddress(@onNull String address)377 List<Integer> getAllSupportedUsagesForAddress(@NonNull String address) { 378 List<Integer> supportedUsagesForAddress = new ArrayList<>(); 379 List<Integer> contextsForAddress = getContextsForAddress(address); 380 for (int contextIndex = 0; contextIndex < contextsForAddress.size(); contextIndex++) { 381 int contextId = contextsForAddress.get(contextIndex); 382 AudioAttributes[] attributes = 383 mCarAudioContext.getAudioAttributesForContext(contextId); 384 for (int attrIndex = 0; attrIndex < attributes.length; attrIndex++) { 385 int usage = attributes[attrIndex].getSystemUsage(); 386 if (!supportedUsagesForAddress.contains(usage)) { 387 supportedUsagesForAddress.add(usage); 388 } 389 } 390 } 391 return supportedUsagesForAddress; 392 } 393 getMaxGainIndex()394 abstract int getMaxGainIndex(); 395 getMinGainIndex()396 abstract int getMinGainIndex(); 397 getCurrentGainIndex()398 int getCurrentGainIndex() { 399 synchronized (mLock) { 400 if (isMutedLocked()) { 401 return getMinGainIndex(); 402 } 403 404 return getRestrictedGainForIndexLocked(getCurrentGainIndexLocked()); 405 } 406 } 407 408 @GuardedBy("mLock") getCurrentGainIndexLocked()409 protected int getCurrentGainIndexLocked() { 410 return mCurrentGainIndex; 411 } 412 413 @GuardedBy("mLock") getRestrictedGainForIndexLocked(int index)414 protected int getRestrictedGainForIndexLocked(int index) { 415 if (isBlockedLocked()) { 416 return mBlockedGainIndex; 417 } 418 if (isOverLimitLocked()) { 419 return mLimitedGainIndex; 420 } 421 if (isAttenuatedLocked()) { 422 // Need to figure out if attenuation shall be hidden to end user 423 // as while ducked from IAudioControl 424 // TODO(b/) clarify in case of volume adjustment if the reference index is the 425 // ducked index or the current index. Taking current may lead to gap of index > 1. 426 return mAttenuatedGainIndex; 427 } 428 return index; 429 } 430 431 /** 432 * Sets the gain on this group, gain will be set on all devices within volume group. 433 */ setCurrentGainIndex(int gainIndex)434 void setCurrentGainIndex(int gainIndex) { 435 synchronized (mLock) { 436 int currentgainIndex = gainIndex; 437 Preconditions.checkArgument(isValidGainIndexLocked(gainIndex), 438 "Gain out of range (%d:%d) index %d", getMinGainIndex(), getMaxGainIndex(), 439 gainIndex); 440 if (isBlockedLocked()) { 441 // prevent any volume change while {@link IAudioGainCallback} reported block event. 442 return; 443 } 444 if (isOverLimitLocked(currentgainIndex)) { 445 currentgainIndex = mLimitedGainIndex; 446 } 447 if (isAttenuatedLocked()) { 448 resetAttenuationLocked(); 449 } 450 // In case of attenuation/Limitation, requested index is now the new reference for 451 // cached current index. 452 mCurrentGainIndex = currentgainIndex; 453 454 if (mIsMuted) { 455 setMuteLocked(false); 456 } 457 setCurrentGainIndexLocked(mCurrentGainIndex); 458 } 459 } 460 461 @GuardedBy("mLock") setCurrentGainIndexLocked(int gainIndex)462 protected void setCurrentGainIndexLocked(int gainIndex) { 463 storeGainIndexForUserLocked(gainIndex, mUserId); 464 } 465 hasCriticalAudioContexts()466 boolean hasCriticalAudioContexts() { 467 return mHasCriticalAudioContexts; 468 } 469 470 @Override 471 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) toString()472 public String toString() { 473 synchronized (mLock) { 474 return "CarVolumeGroup id: " + mId 475 + " currentGainIndex: " + mCurrentGainIndex 476 + " contexts: " + Arrays.toString(getContexts()) 477 + " addresses: " + String.join(", ", getAddresses()); 478 } 479 } 480 481 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpLocked(IndentingPrintWriter writer)482 protected abstract void dumpLocked(IndentingPrintWriter writer); 483 484 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)485 void dump(IndentingPrintWriter writer) { 486 synchronized (mLock) { 487 writer.printf("CarVolumeGroup(%d)\n", mId); 488 writer.increaseIndent(); 489 writer.printf("Name(%s)\n", mName); 490 writer.printf("Zone Id(%d)\n", mZoneId); 491 writer.printf("Configuration Id(%d)\n", mConfigId); 492 writer.printf("Is Muted(%b)\n", isMutedLocked()); 493 writer.printf("UserId(%d)\n", mUserId); 494 writer.printf("Persist Volume Group Mute(%b)\n", 495 mSettingsManager.isPersistVolumeGroupMuteEnabled(mUserId)); 496 dumpLocked(writer); 497 writer.printf("Gain indexes (min / max / default / current): %d %d %d %d\n", 498 getMinGainIndex(), getMaxGainIndex(), getDefaultGainIndex(), 499 mCurrentGainIndex); 500 for (int i = 0; i < mContextToAddress.size(); i++) { 501 writer.printf("Context: %s -> Address: %s\n", 502 mCarAudioContext.toString(mContextToAddress.keyAt(i)), 503 mContextToAddress.valueAt(i)); 504 } 505 for (int i = 0; i < mAddressToCarAudioDeviceInfo.size(); i++) { 506 String address = mAddressToCarAudioDeviceInfo.keyAt(i); 507 CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.get(address); 508 info.dump(writer); 509 } 510 writer.printf("Reported reasons:\n"); 511 writer.increaseIndent(); 512 for (int index = 0; index < mReasons.size(); index++) { 513 int reason = mReasons.get(index); 514 writer.printf("%s\n", reasonToString(reason)); 515 } 516 writer.decreaseIndent(); 517 writer.printf("Gain infos:\n"); 518 writer.increaseIndent(); 519 writer.printf( 520 "Blocked: %b%s\n", 521 isBlockedLocked(), 522 (isBlockedLocked() ? " (at: " + mBlockedGainIndex + ")" : "")); 523 writer.printf( 524 "Limited: %b%s\n", 525 isLimitedLocked(), 526 (isLimitedLocked() ? " (at: " + mLimitedGainIndex + ")" : "")); 527 writer.printf( 528 "Attenuated: %b%s\n", 529 isAttenuatedLocked(), 530 (isAttenuatedLocked() ? " (at: " + mAttenuatedGainIndex + ")" : "")); 531 writer.printf("Muted by HAL: %b\n", isHalMutedLocked()); 532 writer.decreaseIndent(); 533 // Empty line for comfortable reading 534 writer.println(); 535 writer.decreaseIndent(); 536 } 537 } 538 loadVolumesSettingsForUser(@serIdInt int userId)539 void loadVolumesSettingsForUser(@UserIdInt int userId) { 540 synchronized (mLock) { 541 //Update the volume for the new user 542 updateUserIdLocked(userId); 543 //Update the current gain index 544 updateCurrentGainIndexLocked(); 545 setCurrentGainIndexLocked(getCurrentGainIndexLocked()); 546 //Reset devices with current gain index 547 updateGroupMuteLocked(); 548 } 549 } 550 551 /** 552 * Set the mute state of the Volume Group 553 * 554 * @param mute state requested 555 * @return true if mute state has changed, false otherwiser (already set or change not allowed) 556 */ setMute(boolean mute)557 boolean setMute(boolean mute) { 558 synchronized (mLock) { 559 // if hal muted the audio devices, then do not allow other incoming requests 560 // to perform unmute. 561 if (!mute && isHalMutedLocked()) { 562 Slogf.e(CarLog.TAG_AUDIO, "Un-mute request cannot be processed due to active " 563 + "hal mute restriction!"); 564 return false; 565 } 566 applyMuteLocked(mute); 567 return setMuteLocked(mute); 568 } 569 } 570 571 @GuardedBy("mLock") setMuteLocked(boolean mute)572 protected boolean setMuteLocked(boolean mute) { 573 boolean hasChanged = mIsMuted != mute; 574 mIsMuted = mute; 575 if (mSettingsManager.isPersistVolumeGroupMuteEnabled(mUserId)) { 576 mSettingsManager.storeVolumeGroupMuteForUser(mUserId, mZoneId, mConfigId, mId, mute); 577 } 578 return hasChanged; 579 } 580 581 @GuardedBy("mLock") applyMuteLocked(boolean mute)582 protected void applyMuteLocked(boolean mute) { 583 } 584 isMuted()585 boolean isMuted() { 586 synchronized (mLock) { 587 return isMutedLocked(); 588 } 589 } 590 591 @GuardedBy("mLock") isMutedLocked()592 protected boolean isMutedLocked() { 593 // if either of the mute states is set, it results in group being muted. 594 return isUserMutedLocked() || isHalMutedLocked(); 595 } 596 597 @GuardedBy("mLock") isUserMutedLocked()598 protected boolean isUserMutedLocked() { 599 return mIsMuted; 600 } 601 602 @GuardedBy("mLock") isFullyMutedLocked()603 protected boolean isFullyMutedLocked() { 604 return isUserMutedLocked() || isHalMutedLocked() || isBlockedLocked(); 605 } 606 containsCriticalAttributes(List<AudioAttributes> volumeAttributes)607 private static boolean containsCriticalAttributes(List<AudioAttributes> volumeAttributes) { 608 for (int index = 0; index < volumeAttributes.size(); index++) { 609 if (CarAudioContext.isCriticalAudioAudioAttribute(volumeAttributes.get(index))) { 610 return true; 611 } 612 } 613 return false; 614 } 615 616 @GuardedBy("mLock") updateUserIdLocked(@serIdInt int userId)617 private void updateUserIdLocked(@UserIdInt int userId) { 618 mUserId = userId; 619 mStoredGainIndex = getCurrentGainIndexForUserLocked(); 620 } 621 622 @GuardedBy("mLock") getCurrentGainIndexForUserLocked()623 private int getCurrentGainIndexForUserLocked() { 624 int gainIndexForUser = mSettingsManager.getStoredVolumeGainIndexForUser(mUserId, mZoneId, 625 mConfigId, mId); 626 Slogf.i(CarLog.TAG_AUDIO, "updateUserId userId " + mUserId 627 + " gainIndexForUser " + gainIndexForUser); 628 return gainIndexForUser; 629 } 630 631 /** 632 * Update the current gain index based on the stored gain index 633 */ 634 @GuardedBy("mLock") updateCurrentGainIndexLocked()635 private void updateCurrentGainIndexLocked() { 636 if (isValidGainIndexLocked(mStoredGainIndex)) { 637 mCurrentGainIndex = mStoredGainIndex; 638 } else { 639 mCurrentGainIndex = getDefaultGainIndex(); 640 } 641 } 642 isValidGainIndex(int gainIndex)643 protected boolean isValidGainIndex(int gainIndex) { 644 synchronized (mLock) { 645 return isValidGainIndexLocked(gainIndex); 646 } 647 } isValidGainIndexLocked(int gainIndex)648 protected abstract boolean isValidGainIndexLocked(int gainIndex); 649 getDefaultGainIndex()650 protected abstract int getDefaultGainIndex(); 651 652 @GuardedBy("mLock") storeGainIndexForUserLocked(int gainIndex, @UserIdInt int userId)653 private void storeGainIndexForUserLocked(int gainIndex, @UserIdInt int userId) { 654 mSettingsManager.storeVolumeGainIndexForUser(userId, 655 mZoneId, mConfigId, mId, gainIndex); 656 } 657 658 @GuardedBy("mLock") updateGroupMuteLocked()659 private void updateGroupMuteLocked() { 660 if (!mUseCarVolumeGroupMute) { 661 return; 662 } 663 if (!mSettingsManager.isPersistVolumeGroupMuteEnabled(mUserId)) { 664 mIsMuted = false; 665 return; 666 } 667 mIsMuted = mSettingsManager.getVolumeGroupMuteForUser(mUserId, mZoneId, mConfigId, mId); 668 applyMuteLocked(isFullyMutedLocked()); 669 } 670 671 /** 672 * Updates volume group states (index, mute, blocked etc) on callback from audio control hal. 673 * 674 * <p>If gain config info carries duplicate info, do not generate events (i.e. eventType = 0) 675 * @param halReasons reasons for change to gain config info 676 * @param gain updated gain config info 677 * @return one or more of {@link EventTypeEnum}. Or 0 for duplicate gain config info 678 */ onAudioGainChanged(List<Integer> halReasons, CarAudioGainConfigInfo gain)679 int onAudioGainChanged(List<Integer> halReasons, CarAudioGainConfigInfo gain) { 680 int eventType = 0; 681 int halIndex = gain.getVolumeIndex(); 682 if (getCarAudioDeviceInfoForAddress(gain.getDeviceAddress()) == null 683 || !isValidGainIndex(halIndex)) { 684 Slogf.e(CarLog.TAG_AUDIO, 685 "onAudioGainChanged invalid CarAudioGainConfigInfo: " + gain 686 + " for group id: " + mId); 687 return eventType; 688 } 689 synchronized (mLock) { 690 int previousRestrictedIndex = getRestrictedGainForIndexLocked(mCurrentGainIndex); 691 mReasons = new ArrayList<>(halReasons); 692 693 boolean shouldBlock = CarAudioGainMonitor.shouldBlockVolumeRequest(halReasons); 694 if ((shouldBlock != isBlockedLocked()) 695 || (shouldBlock && (halIndex != mBlockedGainIndex))) { 696 setBlockedLocked(shouldBlock ? halIndex : UNINITIALIZED); 697 eventType |= EVENT_TYPE_VOLUME_BLOCKED_CHANGED; 698 } 699 700 boolean shouldLimit = CarAudioGainMonitor.shouldLimitVolume(halReasons); 701 if ((shouldLimit != isLimitedLocked()) 702 || (shouldLimit && (halIndex != mLimitedGainIndex))) { 703 setLimitLocked(shouldLimit ? halIndex : getMaxGainIndex()); 704 eventType |= EVENT_TYPE_ATTENUATION_CHANGED; 705 } 706 707 boolean shouldDuck = CarAudioGainMonitor.shouldDuckGain(halReasons); 708 if ((shouldDuck != isAttenuatedLocked()) 709 || (shouldDuck && (halIndex != mAttenuatedGainIndex))) { 710 setAttenuatedGainLocked(shouldDuck ? halIndex : UNINITIALIZED); 711 eventType |= EVENT_TYPE_ATTENUATION_CHANGED; 712 } 713 714 // Accept mute callbacks from hal only if group mute is enabled. 715 // If disabled, such callbacks will be considered as blocking restriction only. 716 boolean shouldMute = CarAudioGainMonitor.shouldMuteVolumeGroup(halReasons); 717 if (mUseCarVolumeGroupMute && (shouldMute != isHalMutedLocked())) { 718 setHalMuteLocked(shouldMute); 719 eventType |= EVENT_TYPE_MUTE_CHANGED; 720 } 721 722 if (CarAudioGainMonitor.shouldUpdateVolumeIndex(halReasons) 723 && (halIndex != getRestrictedGainForIndexLocked(mCurrentGainIndex))) { 724 mCurrentGainIndex = halIndex; 725 eventType |= EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED; 726 } 727 728 // Blocked/Attenuated index shall have been already apply by Audio HAL on HW. 729 // However, keep in sync & broadcast to all ports this volume group deals with. 730 // 731 // Do not update current gain cache, keep it for restoring rather using reported index 732 // when the event is cleared. 733 int newRestrictedIndex = getRestrictedGainForIndexLocked(mCurrentGainIndex); 734 setCurrentGainIndexLocked(newRestrictedIndex); 735 // Hal or user mute state can change (only user mute enabled while hal muted allowed). 736 // Force a sync of mute application. 737 applyMuteLocked(isFullyMutedLocked()); 738 739 if (newRestrictedIndex != previousRestrictedIndex) { 740 eventType |= EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED; 741 } 742 } 743 return eventType; 744 } 745 getCarVolumeGroupInfo()746 CarVolumeGroupInfo getCarVolumeGroupInfo() { 747 int gainIndex; 748 boolean isMuted; 749 boolean isBlocked; 750 boolean isAttenuated; 751 synchronized (mLock) { 752 gainIndex = getRestrictedGainForIndexLocked(mCurrentGainIndex); 753 isMuted = isMutedLocked(); 754 isBlocked = isBlockedLocked(); 755 isAttenuated = isAttenuatedLocked() || isLimitedLocked(); 756 } 757 758 String name = mName.isEmpty() ? "group id " + mId : mName; 759 760 return new CarVolumeGroupInfo.Builder(name, mZoneId, mId) 761 .setVolumeGainIndex(gainIndex).setMaxVolumeGainIndex(getMaxGainIndex()) 762 .setMinVolumeGainIndex(getMinGainIndex()).setMuted(isMuted).setBlocked(isBlocked) 763 .setAttenuated(isAttenuated).setAudioAttributes(getAudioAttributes()).build(); 764 } 765 getAudioAttributes()766 List<AudioAttributes> getAudioAttributes() { 767 List<AudioAttributes> audioAttributes = new ArrayList<>(); 768 for (int index = 0; index < mContextToAddress.size(); index++) { 769 int context = mContextToAddress.keyAt(index); 770 AudioAttributes[] contextAttributes = 771 mCarAudioContext.getAudioAttributesForContext(context); 772 for (int attrIndex = 0; attrIndex < contextAttributes.length; attrIndex++) { 773 audioAttributes.add(contextAttributes[attrIndex]); 774 } 775 } 776 777 return audioAttributes; 778 } 779 780 /** 781 * @return one or more {@link CarVolumeGroupEvent#EventTypeEnum} 782 */ onAudioVolumeGroupChanged(int flags)783 public int onAudioVolumeGroupChanged(int flags) { 784 return 0; 785 } 786 787 /** 788 * Updates car audio device info with the hal audio device info 789 */ updateAudioDeviceInfo(HalAudioDeviceInfo halDeviceInfo)790 void updateAudioDeviceInfo(HalAudioDeviceInfo halDeviceInfo) { 791 synchronized (mLock) { 792 CarAudioDeviceInfo info = mAddressToCarAudioDeviceInfo.get(halDeviceInfo.getAddress()); 793 if (info == null) { 794 Slogf.w(CarLog.TAG_AUDIO, "No matching car audio device info found for address: %s", 795 halDeviceInfo.getAddress()); 796 return; 797 } 798 info.updateAudioDeviceInfo(halDeviceInfo); 799 } 800 } 801 802 /** 803 * Calculates the new gain stages from list of assigned audio device infos 804 * 805 * <p>Used to update audio device gain stages dynamically. 806 * 807 * @return one or more of {@link EventTypeEnum}. Or 0 if dynamic updates are not supported 808 */ calculateNewGainStageFromDeviceInfos()809 int calculateNewGainStageFromDeviceInfos() { 810 return 0; 811 } 812 } 813