1 /* 2 * Copyright (C) 2023 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.server.audio; 18 19 import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.media.AudioAttributes; 24 import android.media.AudioManager; 25 import android.media.AudioPlaybackConfiguration; 26 import android.media.FadeManagerConfiguration; 27 import android.media.VolumeShaper; 28 import android.util.Slog; 29 30 import com.android.internal.annotations.GuardedBy; 31 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.Objects; 35 36 /** 37 * Class to encapsulate configurations used for fading players 38 */ 39 public final class FadeConfigurations { 40 public static final String TAG = "AS.FadeConfigurations"; 41 42 private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG; 43 44 45 /** duration of the fade out curve */ 46 private static final long DEFAULT_FADE_OUT_DURATION_MS = 2000; 47 /** 48 * delay after which a faded out player will be faded back in. This will be heard by the 49 * user only in the case of unmuting players that didn't respect audio focus and didn't 50 * stop/pause when their app lost focus. 51 * This is the amount of time between the app being notified of 52 * the focus loss (when its muted by the fade out), and the time fade in (to unmute) starts 53 */ 54 private static final long DEFAULT_DELAY_FADE_IN_OFFENDERS_MS = 2000; 55 56 private static final List<Integer> DEFAULT_UNFADEABLE_PLAYER_TYPES = List.of( 57 AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO, 58 AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL 59 ); 60 61 private static final List<Integer> DEFAULT_UNFADEABLE_CONTENT_TYPES = List.of( 62 AudioAttributes.CONTENT_TYPE_SPEECH 63 ); 64 65 private static final List<Integer> DEFAULT_FADEABLE_USAGES = List.of( 66 AudioAttributes.USAGE_GAME, 67 AudioAttributes.USAGE_MEDIA 68 ); 69 70 private static final VolumeShaper.Configuration DEFAULT_FADEOUT_VSHAPE = 71 new VolumeShaper.Configuration.Builder() 72 .setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID) 73 .setCurve(new float[]{0.f, 0.25f, 1.0f} /* times */, 74 new float[]{1.f, 0.65f, 0.0f} /* volumes */) 75 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME) 76 .setDuration(DEFAULT_FADE_OUT_DURATION_MS) 77 .build(); 78 79 private static final int INVALID_UID = -1; 80 81 private final Object mLock = new Object(); 82 @GuardedBy("mLock") 83 private FadeManagerConfiguration mDefaultFadeManagerConfig; 84 @GuardedBy("mLock") 85 private FadeManagerConfiguration mUpdatedFadeManagerConfig; 86 @GuardedBy("mLock") 87 private FadeManagerConfiguration mTransientFadeManagerConfig; 88 /** active fade manager is one of: transient > updated > default */ 89 @GuardedBy("mLock") 90 private FadeManagerConfiguration mActiveFadeManagerConfig; 91 92 /** 93 * Sets the custom fade manager configuration 94 * 95 * @param fadeManagerConfig custom fade manager configuration 96 * @return {@link AudioManager#SUCCESS} if setting custom fade manager configuration succeeds 97 * or {@link AudioManager#ERROR} otherwise (example - when fade manager configuration 98 * feature is disabled) 99 */ setFadeManagerConfiguration( @onNull FadeManagerConfiguration fadeManagerConfig)100 public int setFadeManagerConfiguration( 101 @NonNull FadeManagerConfiguration fadeManagerConfig) { 102 if (!enableFadeManagerConfiguration()) { 103 return AudioManager.ERROR; 104 } 105 106 synchronized (mLock) { 107 mUpdatedFadeManagerConfig = Objects.requireNonNull(fadeManagerConfig, 108 "Fade manager configuration cannot be null"); 109 mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked(); 110 } 111 return AudioManager.SUCCESS; 112 } 113 114 /** 115 * Clears the fade manager configuration that was previously set with 116 * {@link #setFadeManagerConfiguration(FadeManagerConfiguration)} 117 * 118 * @return {@link AudioManager#SUCCESS} if previously set fade manager configuration is cleared 119 * or {@link AudioManager#ERROR} otherwise (example, when fade manager configuration feature 120 * is disabled) 121 */ clearFadeManagerConfiguration()122 public int clearFadeManagerConfiguration() { 123 if (!enableFadeManagerConfiguration()) { 124 return AudioManager.ERROR; 125 } 126 127 synchronized (mLock) { 128 mUpdatedFadeManagerConfig = null; 129 mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked(); 130 } 131 return AudioManager.SUCCESS; 132 } 133 134 /** 135 * Returns the active fade manager configuration 136 * 137 * @return {@code null} if feature is disabled, or the custom fade manager configuration if set, 138 * or default fade manager configuration if not set. 139 */ 140 @Nullable getFadeManagerConfiguration()141 public FadeManagerConfiguration getFadeManagerConfiguration() { 142 if (!enableFadeManagerConfiguration()) { 143 return null; 144 } 145 146 synchronized (mLock) { 147 return mActiveFadeManagerConfig; 148 } 149 } 150 151 /** 152 * Sets the transient fade manager configuration 153 * 154 * @param fadeManagerConfig custom fade manager configuration 155 * @return {@link AudioManager#SUCCESS} if setting custom fade manager configuration succeeds 156 * or {@link AudioManager#ERROR} otherwise (example - when fade manager configuration is 157 * disabled) 158 */ setTransientFadeManagerConfiguration( @onNull FadeManagerConfiguration fadeManagerConfig)159 public int setTransientFadeManagerConfiguration( 160 @NonNull FadeManagerConfiguration fadeManagerConfig) { 161 if (!enableFadeManagerConfiguration()) { 162 return AudioManager.ERROR; 163 } 164 165 synchronized (mLock) { 166 mTransientFadeManagerConfig = Objects.requireNonNull(fadeManagerConfig, 167 "Transient FadeManagerConfiguration cannot be null"); 168 mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked(); 169 } 170 return AudioManager.SUCCESS; 171 } 172 173 /** 174 * Clears the transient fade manager configuration that was previously set with 175 * {@link #setTransientFadeManagerConfiguration(FadeManagerConfiguration)} 176 * 177 * @return {@link AudioManager#SUCCESS} if previously set transient fade manager configuration 178 * is cleared or {@link AudioManager#ERROR} otherwise (example - when fade manager 179 * configuration is disabled) 180 */ clearTransientFadeManagerConfiguration()181 public int clearTransientFadeManagerConfiguration() { 182 if (!enableFadeManagerConfiguration()) { 183 return AudioManager.ERROR; 184 } 185 synchronized (mLock) { 186 mTransientFadeManagerConfig = null; 187 mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked(); 188 } 189 return AudioManager.SUCCESS; 190 } 191 192 /** 193 * Query if fade should be enforecd on players 194 * 195 * @return {@code true} if fade is enabled or using default configurations, {@code false} 196 * otherwise. 197 */ isFadeEnabled()198 public boolean isFadeEnabled() { 199 if (!enableFadeManagerConfiguration()) { 200 return true; 201 } 202 203 synchronized (mLock) { 204 return getUpdatedFadeManagerConfigLocked().isFadeEnabled(); 205 } 206 } 207 208 /** 209 * Query {@link android.media.AudioAttributes.AttributeUsage usages} that are allowed to 210 * fade 211 * 212 * @return list of {@link android.media.AudioAttributes.AttributeUsage} 213 */ 214 @NonNull getFadeableUsages()215 public List<Integer> getFadeableUsages() { 216 if (!enableFadeManagerConfiguration()) { 217 return DEFAULT_FADEABLE_USAGES; 218 } 219 220 synchronized (mLock) { 221 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 222 // when fade is not enabled, return an empty list instead 223 return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getFadeableUsages() 224 : Collections.EMPTY_LIST; 225 } 226 } 227 228 /** 229 * Query {@link android.media.AudioAttributes.AttributeContentType content types} that are 230 * exempted from fade enforcement 231 * 232 * @return list of {@link android.media.AudioAttributes.AttributeContentType} 233 */ 234 @NonNull getUnfadeableContentTypes()235 public List<Integer> getUnfadeableContentTypes() { 236 if (!enableFadeManagerConfiguration()) { 237 return DEFAULT_UNFADEABLE_CONTENT_TYPES; 238 } 239 240 synchronized (mLock) { 241 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 242 // when fade is not enabled, return an empty list instead 243 return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getUnfadeableContentTypes() 244 : Collections.EMPTY_LIST; 245 } 246 } 247 248 /** 249 * Query {@link android.media.AudioPlaybackConfiguration.PlayerType player types} that are 250 * exempted from fade enforcement 251 * 252 * @return list of {@link android.media.AudioPlaybackConfiguration.PlayerType} 253 */ 254 @NonNull getUnfadeablePlayerTypes()255 public List<Integer> getUnfadeablePlayerTypes() { 256 if (!enableFadeManagerConfiguration()) { 257 return DEFAULT_UNFADEABLE_PLAYER_TYPES; 258 } 259 260 synchronized (mLock) { 261 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 262 // when fade is not enabled, return an empty list instead 263 return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getUnfadeablePlayerTypes() 264 : Collections.EMPTY_LIST; 265 } 266 } 267 268 /** 269 * Get the {@link android.media.VolumeShaper.Configuration} configuration to be applied 270 * for the fade-out 271 * 272 * @param aa The {@link android.media.AudioAttributes} 273 * @return {@link android.media.VolumeShaper.Configuration} for the 274 * {@link android.media.AudioAttributes} or default volume shaper if not configured 275 */ 276 @NonNull getFadeOutVolumeShaperConfig(@onNull AudioAttributes aa)277 public VolumeShaper.Configuration getFadeOutVolumeShaperConfig(@NonNull AudioAttributes aa) { 278 if (!enableFadeManagerConfiguration()) { 279 return DEFAULT_FADEOUT_VSHAPE; 280 } 281 return getOptimalFadeOutVolShaperConfig(aa); 282 } 283 284 /** 285 * Get the {@link android.media.VolumeShaper.Configuration} configuration to be applied for the 286 * fade in 287 * 288 * @param aa The {@link android.media.AudioAttributes} 289 * @return {@link android.media.VolumeShaper.Configuration} for the 290 * {@link android.media.AudioAttributes} or {@code null} otherwise 291 */ 292 @Nullable getFadeInVolumeShaperConfig(@onNull AudioAttributes aa)293 public VolumeShaper.Configuration getFadeInVolumeShaperConfig(@NonNull AudioAttributes aa) { 294 if (!enableFadeManagerConfiguration()) { 295 return null; 296 } 297 return getOptimalFadeInVolShaperConfig(aa); 298 } 299 300 301 /** 302 * Get the duration to fade out a player of type usage 303 * 304 * @param aa The {@link android.media.AudioAttributes} 305 * @return duration in milliseconds for the 306 * {@link android.media.AudioAttributes} or default duration if not configured 307 */ getFadeOutDuration(@onNull AudioAttributes aa)308 public long getFadeOutDuration(@NonNull AudioAttributes aa) { 309 if (!isFadeable(aa, INVALID_UID, AudioPlaybackConfiguration.PLAYER_TYPE_UNKNOWN)) { 310 return 0; 311 } 312 if (!enableFadeManagerConfiguration()) { 313 return DEFAULT_FADE_OUT_DURATION_MS; 314 } 315 return getOptimalFadeOutDuration(aa); 316 } 317 318 /** 319 * Get the delay to fade in offending players that do not stop after losing audio focus 320 * 321 * @param aa The {@link android.media.AudioAttributes} 322 * @return delay in milliseconds for the 323 * {@link android.media.AudioAttributes.Attribute} or default delay if not configured 324 */ getDelayFadeInOffenders(@onNull AudioAttributes aa)325 public long getDelayFadeInOffenders(@NonNull AudioAttributes aa) { 326 if (!enableFadeManagerConfiguration()) { 327 return DEFAULT_DELAY_FADE_IN_OFFENDERS_MS; 328 } 329 330 synchronized (mLock) { 331 return getUpdatedFadeManagerConfigLocked().getFadeInDelayForOffenders(); 332 } 333 } 334 335 /** 336 * Query {@link android.media.AudioAttributes} that are exempted from fade enforcement 337 * 338 * @return list of {@link android.media.AudioAttributes} 339 */ 340 @NonNull getUnfadeableAudioAttributes()341 public List<AudioAttributes> getUnfadeableAudioAttributes() { 342 // unfadeable audio attributes is only supported with fade manager configurations 343 if (!enableFadeManagerConfiguration()) { 344 return Collections.EMPTY_LIST; 345 } 346 347 synchronized (mLock) { 348 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 349 // when fade is not enabled, return empty list 350 return fadeManagerConfig.isFadeEnabled() 351 ? fadeManagerConfig.getUnfadeableAudioAttributes() : Collections.EMPTY_LIST; 352 } 353 } 354 355 /** 356 * Query uids that are exempted from fade enforcement 357 * 358 * @return list of uids 359 */ 360 @NonNull getUnfadeableUids()361 public List<Integer> getUnfadeableUids() { 362 // unfadeable uids is only supported with fade manager configurations 363 if (!enableFadeManagerConfiguration()) { 364 return Collections.EMPTY_LIST; 365 } 366 367 synchronized (mLock) { 368 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 369 // when fade is not enabled, return empty list 370 return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getUnfadeableUids() 371 : Collections.EMPTY_LIST; 372 } 373 } 374 375 /** 376 * Check if it is allowed to fade for the given {@link android.media.AudioAttributes}, 377 * client uid and {@link android.media.AudioPlaybackConfiguration.PlayerType} config 378 * 379 * @param aa The {@link android.media.AudioAttributes} 380 * @param uid The uid of the client owning the player 381 * @param playerType The {@link android.media.AudioPlaybackConfiguration.PlayerType} 382 * @return {@code true} if it the config is fadeable and {@code false} otherwise 383 */ isFadeable(@onNull AudioAttributes aa, int uid, @AudioPlaybackConfiguration.PlayerType int playerType)384 public boolean isFadeable(@NonNull AudioAttributes aa, int uid, 385 @AudioPlaybackConfiguration.PlayerType int playerType) { 386 synchronized (mLock) { 387 if (isPlayerTypeUnfadeableLocked(playerType)) { 388 if (DEBUG) { 389 Slog.i(TAG, "not fadeable: player type:" + playerType); 390 } 391 return false; 392 } 393 if (isContentTypeUnfadeableLocked(aa.getContentType())) { 394 if (DEBUG) { 395 Slog.i(TAG, "not fadeable: content type:" + aa.getContentType()); 396 } 397 return false; 398 } 399 if (!isUsageFadeableLocked(aa.getSystemUsage())) { 400 if (DEBUG) { 401 Slog.i(TAG, "not fadeable: usage:" + aa.getUsage()); 402 } 403 return false; 404 } 405 // new configs using fade manager configuration 406 if (isUnfadeableForFadeMgrConfigLocked(aa, uid)) { 407 return false; 408 } 409 return true; 410 } 411 } 412 413 /** Tries to get the fade out volume shaper config closest to the audio attributes */ getOptimalFadeOutVolShaperConfig(AudioAttributes aa)414 private VolumeShaper.Configuration getOptimalFadeOutVolShaperConfig(AudioAttributes aa) { 415 synchronized (mLock) { 416 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 417 // check if the specific audio attributes has a volume shaper config defined 418 VolumeShaper.Configuration volShaperConfig = 419 fadeManagerConfig.getFadeOutVolumeShaperConfigForAudioAttributes(aa); 420 if (volShaperConfig != null) { 421 return volShaperConfig; 422 } 423 424 // get the volume shaper config for usage 425 // for fadeable usages, this should never return null 426 return fadeManagerConfig.getFadeOutVolumeShaperConfigForUsage( 427 aa.getSystemUsage()); 428 } 429 } 430 431 /** Tries to get the fade in volume shaper config closest to the audio attributes */ getOptimalFadeInVolShaperConfig(AudioAttributes aa)432 private VolumeShaper.Configuration getOptimalFadeInVolShaperConfig(AudioAttributes aa) { 433 synchronized (mLock) { 434 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 435 // check if the specific audio attributes has a volume shaper config defined 436 VolumeShaper.Configuration volShaperConfig = 437 fadeManagerConfig.getFadeInVolumeShaperConfigForAudioAttributes(aa); 438 if (volShaperConfig != null) { 439 return volShaperConfig; 440 } 441 442 // get the volume shaper config for usage 443 // for fadeable usages, this should never return null 444 return fadeManagerConfig.getFadeInVolumeShaperConfigForUsage(aa.getSystemUsage()); 445 } 446 } 447 448 /** Tries to get the duration closest to the audio attributes */ getOptimalFadeOutDuration(AudioAttributes aa)449 private long getOptimalFadeOutDuration(AudioAttributes aa) { 450 synchronized (mLock) { 451 FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked(); 452 // check if specific audio attributes has a duration defined 453 long duration = fadeManagerConfig.getFadeOutDurationForAudioAttributes(aa); 454 if (duration != FadeManagerConfiguration.DURATION_NOT_SET) { 455 return duration; 456 } 457 458 // get the duration for usage 459 // for fadeable usages, this should never return DURATION_NOT_SET 460 return fadeManagerConfig.getFadeOutDurationForUsage(aa.getSystemUsage()); 461 } 462 } 463 464 @GuardedBy("mLock") isUnfadeableForFadeMgrConfigLocked(AudioAttributes aa, int uid)465 private boolean isUnfadeableForFadeMgrConfigLocked(AudioAttributes aa, int uid) { 466 if (isAudioAttributesUnfadeableLocked(aa)) { 467 if (DEBUG) { 468 Slog.i(TAG, "not fadeable: aa:" + aa); 469 } 470 return true; 471 } 472 if (isUidUnfadeableLocked(uid)) { 473 if (DEBUG) { 474 Slog.i(TAG, "not fadeable: uid:" + uid); 475 } 476 return true; 477 } 478 return false; 479 } 480 481 @GuardedBy("mLock") isUsageFadeableLocked(int usage)482 private boolean isUsageFadeableLocked(int usage) { 483 if (!enableFadeManagerConfiguration()) { 484 return DEFAULT_FADEABLE_USAGES.contains(usage); 485 } 486 return getUpdatedFadeManagerConfigLocked().isUsageFadeable(usage); 487 } 488 489 @GuardedBy("mLock") isContentTypeUnfadeableLocked(int contentType)490 private boolean isContentTypeUnfadeableLocked(int contentType) { 491 if (!enableFadeManagerConfiguration()) { 492 return DEFAULT_UNFADEABLE_CONTENT_TYPES.contains(contentType); 493 } 494 return getUpdatedFadeManagerConfigLocked().isContentTypeUnfadeable(contentType); 495 } 496 497 @GuardedBy("mLock") isPlayerTypeUnfadeableLocked(int playerType)498 private boolean isPlayerTypeUnfadeableLocked(int playerType) { 499 if (!enableFadeManagerConfiguration()) { 500 return DEFAULT_UNFADEABLE_PLAYER_TYPES.contains(playerType); 501 } 502 return getUpdatedFadeManagerConfigLocked().isPlayerTypeUnfadeable(playerType); 503 } 504 505 @GuardedBy("mLock") isAudioAttributesUnfadeableLocked(AudioAttributes aa)506 private boolean isAudioAttributesUnfadeableLocked(AudioAttributes aa) { 507 if (!enableFadeManagerConfiguration()) { 508 // default fade configs do not support unfadeable audio attributes, hence return false 509 return false; 510 } 511 return getUpdatedFadeManagerConfigLocked().isAudioAttributesUnfadeable(aa); 512 } 513 514 @GuardedBy("mLock") isUidUnfadeableLocked(int uid)515 private boolean isUidUnfadeableLocked(int uid) { 516 if (!enableFadeManagerConfiguration()) { 517 // default fade configs do not support unfadeable uids, hence return false 518 return false; 519 } 520 return getUpdatedFadeManagerConfigLocked().isUidUnfadeable(uid); 521 } 522 523 @GuardedBy("mLock") getUpdatedFadeManagerConfigLocked()524 private FadeManagerConfiguration getUpdatedFadeManagerConfigLocked() { 525 if (mActiveFadeManagerConfig == null) { 526 mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked(); 527 } 528 return mActiveFadeManagerConfig; 529 } 530 531 /** Priority between fade manager configs: Transient > Updated > Default */ 532 @GuardedBy("mLock") getActiveFadeMgrConfigLocked()533 private FadeManagerConfiguration getActiveFadeMgrConfigLocked() { 534 // below configs are arranged in the order of priority 535 // configs placed higher have higher priority 536 if (mTransientFadeManagerConfig != null) { 537 return mTransientFadeManagerConfig; 538 } 539 540 if (mUpdatedFadeManagerConfig != null) { 541 return mUpdatedFadeManagerConfig; 542 } 543 544 // default - must be the lowest priority 545 return getDefaultFadeManagerConfigLocked(); 546 } 547 548 @GuardedBy("mLock") getDefaultFadeManagerConfigLocked()549 private FadeManagerConfiguration getDefaultFadeManagerConfigLocked() { 550 if (mDefaultFadeManagerConfig == null) { 551 mDefaultFadeManagerConfig = new FadeManagerConfiguration.Builder().build(); 552 } 553 return mDefaultFadeManagerConfig; 554 } 555 } 556