AudioAttributesCompat.java revision 328a0994eba8065571e09c1b3459743b340ce70b
1/* 2 * Copyright (C) 2017 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 17package android.support.v4.media; 18 19import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21import android.media.AudioAttributes; 22import android.media.AudioManager; 23import android.os.Build; 24import android.support.annotation.IntDef; 25import android.support.annotation.NonNull; 26import android.support.annotation.Nullable; 27import android.support.annotation.RestrictTo; 28import android.util.SparseIntArray; 29 30import java.lang.annotation.Retention; 31import java.lang.annotation.RetentionPolicy; 32import java.util.Arrays; 33 34/** 35 * A class to encapsulate a collection of attributes describing information about an audio stream. 36 * 37 * <p><code>AudioAttributesCompat</code> supersede the notion of stream types (see for instance 38 * {@link AudioManager#STREAM_MUSIC} or {@link AudioManager#STREAM_ALARM}) for defining the behavior 39 * of audio playback. Attributes allow an application to specify more information than is conveyed 40 * in a stream type by allowing the application to define: 41 * 42 * <ul> 43 * <li>usage: "why" you are playing a sound, what is this sound used for. This is achieved with 44 * the "usage" information. Examples of usage are {@link #USAGE_MEDIA} and {@link 45 * #USAGE_ALARM}. These two examples are the closest to stream types, but more detailed use 46 * cases are available. Usage information is more expressive than a stream type, and allows 47 * certain platforms or routing policies to use this information for more refined volume or 48 * routing decisions. Usage is the most important information to supply in <code> 49 * AudioAttributesCompat</code> and it is recommended to build any instance with this 50 * information supplied, see {@link AudioAttributesCompat.Builder} for exceptions. 51 * <li>content type: "what" you are playing. The content type expresses the general category of 52 * the content. This information is optional. But in case it is known (for instance {@link 53 * #CONTENT_TYPE_MOVIE} for a movie streaming service or {@link #CONTENT_TYPE_MUSIC} for a 54 * music playback application) this information might be used by the audio framework to 55 * selectively configure some audio post-processing blocks. 56 * <li>flags: "how" is playback to be affected, see the flag definitions for the specific playback 57 * behaviors they control. 58 * </ul> 59 * 60 * <p><code>AudioAttributesCompat</code> instance is built through its builder, {@link 61 * AudioAttributesCompat.Builder}. Also see {@link android.media.AudioAttributes} for the framework 62 * implementation of this class. 63 */ 64public class AudioAttributesCompat { 65 private static final String TAG = "AudioAttributesCompat"; 66 67 /** 68 * Content type value to use when the content type is unknown, or other than the ones defined. 69 */ 70 public static final int CONTENT_TYPE_UNKNOWN = AudioAttributes.CONTENT_TYPE_UNKNOWN; 71 /** Content type value to use when the content type is speech. */ 72 public static final int CONTENT_TYPE_SPEECH = AudioAttributes.CONTENT_TYPE_SPEECH; 73 /** Content type value to use when the content type is music. */ 74 public static final int CONTENT_TYPE_MUSIC = AudioAttributes.CONTENT_TYPE_MUSIC; 75 /** 76 * Content type value to use when the content type is a soundtrack, typically accompanying a 77 * movie or TV program. 78 */ 79 public static final int CONTENT_TYPE_MOVIE = AudioAttributes.CONTENT_TYPE_MOVIE; 80 /** 81 * Content type value to use when the content type is a sound used to accompany a user action, 82 * such as a beep or sound effect expressing a key click, or event, such as the type of a sound 83 * for a bonus being received in a game. These sounds are mostly synthesized or short Foley 84 * sounds. 85 */ 86 public static final int CONTENT_TYPE_SONIFICATION = AudioAttributes.CONTENT_TYPE_SONIFICATION; 87 88 /** Usage value to use when the usage is unknown. */ 89 public static final int USAGE_UNKNOWN = AudioAttributes.USAGE_UNKNOWN; 90 /** Usage value to use when the usage is media, such as music, or movie soundtracks. */ 91 public static final int USAGE_MEDIA = AudioAttributes.USAGE_MEDIA; 92 /** Usage value to use when the usage is voice communications, such as telephony or VoIP. */ 93 public static final int USAGE_VOICE_COMMUNICATION = AudioAttributes.USAGE_VOICE_COMMUNICATION; 94 /** 95 * Usage value to use when the usage is in-call signalling, such as with a "busy" beep, or DTMF 96 * tones. 97 */ 98 public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 99 AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING; 100 /** Usage value to use when the usage is an alarm (e.g. wake-up alarm). */ 101 public static final int USAGE_ALARM = AudioAttributes.USAGE_ALARM; 102 /** 103 * Usage value to use when the usage is notification. See other notification usages for more 104 * specialized uses. 105 */ 106 public static final int USAGE_NOTIFICATION = AudioAttributes.USAGE_NOTIFICATION; 107 /** Usage value to use when the usage is telephony ringtone. */ 108 public static final int USAGE_NOTIFICATION_RINGTONE = 109 AudioAttributes.USAGE_NOTIFICATION_RINGTONE; 110 /** 111 * Usage value to use when the usage is a request to enter/end a communication, such as a VoIP 112 * communication or video-conference. 113 */ 114 public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 115 AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST; 116 /** 117 * Usage value to use when the usage is notification for an "instant" communication such as a 118 * chat, or SMS. 119 */ 120 public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 121 AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT; 122 /** 123 * Usage value to use when the usage is notification for a non-immediate type of communication 124 * such as e-mail. 125 */ 126 public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 127 AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED; 128 /** 129 * Usage value to use when the usage is to attract the user's attention, such as a reminder or 130 * low battery warning. 131 */ 132 public static final int USAGE_NOTIFICATION_EVENT = AudioAttributes.USAGE_NOTIFICATION_EVENT; 133 /** Usage value to use when the usage is for accessibility, such as with a screen reader. */ 134 public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 135 AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; 136 /** Usage value to use when the usage is driving or navigation directions. */ 137 public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 138 AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; 139 /** Usage value to use when the usage is sonification, such as with user interface sounds. */ 140 public static final int USAGE_ASSISTANCE_SONIFICATION = 141 AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; 142 /** Usage value to use when the usage is for game audio. */ 143 public static final int USAGE_GAME = AudioAttributes.USAGE_GAME; 144 145 // usage not available to clients 146 private static final int USAGE_VIRTUAL_SOURCE = 15; // AudioAttributes.USAGE_VIRTUAL_SOURCE; 147 /** 148 * Usage value to use for audio responses to user queries, audio instructions or help 149 * utterances. 150 */ 151 public static final int USAGE_ASSISTANT = AudioAttributes.USAGE_ASSISTANT; 152 153 /** 154 * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES 155 * if applicable. 156 */ 157 158 // private API 159 private static final int SUPPRESSIBLE_NOTIFICATION = 1; 160 161 private static final int SUPPRESSIBLE_CALL = 2; 162 private static final SparseIntArray SUPPRESSIBLE_USAGES; 163 164 // used by tests 165 private static boolean sForceLegacyBehavior; 166 167 static { 168 SUPPRESSIBLE_USAGES = new SparseIntArray(); 169 SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION, SUPPRESSIBLE_NOTIFICATION); 170 SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_RINGTONE, SUPPRESSIBLE_CALL); 171 SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_COMMUNICATION_REQUEST, SUPPRESSIBLE_CALL); 172 SUPPRESSIBLE_USAGES.put( 173 USAGE_NOTIFICATION_COMMUNICATION_INSTANT, SUPPRESSIBLE_NOTIFICATION); 174 SUPPRESSIBLE_USAGES.put( 175 USAGE_NOTIFICATION_COMMUNICATION_DELAYED, SUPPRESSIBLE_NOTIFICATION); 176 SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_EVENT, SUPPRESSIBLE_NOTIFICATION); 177 } 178 179 private static final int[] SDK_USAGES = { 180 USAGE_UNKNOWN, 181 USAGE_MEDIA, 182 USAGE_VOICE_COMMUNICATION, 183 USAGE_VOICE_COMMUNICATION_SIGNALLING, 184 USAGE_ALARM, 185 USAGE_NOTIFICATION, 186 USAGE_NOTIFICATION_RINGTONE, 187 USAGE_NOTIFICATION_COMMUNICATION_REQUEST, 188 USAGE_NOTIFICATION_COMMUNICATION_INSTANT, 189 USAGE_NOTIFICATION_COMMUNICATION_DELAYED, 190 USAGE_NOTIFICATION_EVENT, 191 USAGE_ASSISTANCE_ACCESSIBILITY, 192 USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, 193 USAGE_ASSISTANCE_SONIFICATION, 194 USAGE_GAME, 195 USAGE_ASSISTANT, 196 }; 197 198 /** Flag defining a behavior where the audibility of the sound will be ensured by the system. */ 199 public static final int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0; 200 201 // flags for @hide API so we can create a proper flags mask 202 private static final int FLAG_SECURE = 0x1 << 1; 203 private static final int FLAG_SCO = 0x1 << 2; 204 private static final int FLAG_BEACON = 0x1 << 3; 205 206 /** Flag requesting the use of an output stream supporting hardware A/V synchronization. */ 207 public static final int FLAG_HW_AV_SYNC = 0x1 << 4; 208 209 // more @hide flags 210 private static final int FLAG_HW_HOTWORD = 0x1 << 5; 211 private static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1 << 6; 212 private static final int FLAG_BYPASS_MUTE = 0x1 << 7; 213 private static final int FLAG_LOW_LATENCY = 0x1 << 8; 214 private static final int FLAG_DEEP_BUFFER = 0x1 << 9; 215 216 private static final int FLAG_ALL = 217 (FLAG_AUDIBILITY_ENFORCED 218 | FLAG_SECURE 219 | FLAG_SCO 220 | FLAG_BEACON 221 | FLAG_HW_AV_SYNC 222 | FLAG_HW_HOTWORD 223 | FLAG_BYPASS_INTERRUPTION_POLICY 224 | FLAG_BYPASS_MUTE 225 | FLAG_LOW_LATENCY 226 | FLAG_DEEP_BUFFER); 227 private static final int FLAG_ALL_PUBLIC = 228 (FLAG_AUDIBILITY_ENFORCED | FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY); 229 230 int mUsage = USAGE_UNKNOWN; 231 int mContentType = CONTENT_TYPE_UNKNOWN; 232 int mFlags = 0x0; 233 Integer mLegacyStream; 234 private AudioAttributesCompatApi21.Wrapper mAudioAttributesWrapper; 235 236 private AudioAttributesCompat() { 237 } 238 239 /** 240 * Returns the stream type matching the given attributes for volume control. Use this method to 241 * derive the stream type needed to configure the volume control slider in an {@link 242 * android.app.Activity} with {@link android.app.Activity#setVolumeControlStream(int)}. <br> 243 * Do not use this method to set the stream type on an audio player object (e.g. {@link 244 * android.media.AudioTrack}, {@link android.media.MediaPlayer}) as this is deprecated; 245 * use <code>AudioAttributes</code> instead. 246 * 247 * @return a valid stream type for <code>Activity</code> or stream volume control that matches 248 * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct 249 * match. Note that <code>USE_DEFAULT_STREAM_TYPE</code> is not a valid value for {@link 250 * AudioManager#setStreamVolume(int, int, int)}. 251 */ 252 public int getVolumeControlStream() { 253 if (this == null) { 254 throw new IllegalArgumentException("Invalid null audio attributes"); 255 } 256 if (Build.VERSION.SDK_INT >= 26 257 && !sForceLegacyBehavior 258 && unwrap() != null) { 259 return AudioAttributes.getVolumeControlStream((AudioAttributes) unwrap()); 260 } 261 return toVolumeStreamType(true, this); 262 } 263 264 // public API unique to AudioAttributesCompat 265 266 /** 267 * If the current SDK level is 21 or higher, return the {@link AudioAttributes} object inside 268 * this {@link AudioAttributesCompat}. Otherwise <code>null</code>. 269 * 270 * @return the underlying {@link AudioAttributes} object or null 271 */ 272 @Nullable 273 public Object unwrap() { 274 if (mAudioAttributesWrapper != null) { 275 return mAudioAttributesWrapper.unwrap(); 276 } 277 return null; 278 } 279 280 /** 281 * Return a stream type passed to {@link Builder#setLegacyStreamType(int)}, or -1 if no legacy 282 * stream is available 283 * 284 * @return the stream type {@see AudioManager} 285 */ 286 public int getLegacyStreamType() { 287 // case 1: developer explicitly set a legacy stream, 288 // so just hand that back 289 if (mLegacyStream != null) { 290 return mLegacyStream; 291 } 292 293 // case 2: API 21+ and we have a real AudioAttributes 294 // the same caveats in AudioAttributes#toLegacyStreamTyoe apply: 295 // only use this for volume control 296 if (Build.VERSION.SDK_INT >= 21) { 297 if (!sForceLegacyBehavior) { 298 return AudioAttributesCompatApi21.toLegacyStreamType(mAudioAttributesWrapper); 299 } 300 } 301 302 // case 3: developer set up AudioAttrs using new flags/usage APIs 303 // but we are running pre-API21, so use the heuristic below 304 return toVolumeStreamType(false, mFlags, mUsage); 305 } 306 307 /** 308 * Create an {@link AudioAttributesCompat} given an API 21 {@link AudioAttributes} object. 309 * 310 * @param aa an instance of {@link AudioAttributes} 311 * @return the new <code>AudioAttributesCompat</code>, or <code>null</code> on API < 21 312 */ 313 @Nullable 314 public static AudioAttributesCompat wrap(@NonNull final Object aa) { 315 if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior) { 316 final AudioAttributesCompat aac = new AudioAttributesCompat(); 317 aac.mAudioAttributesWrapper = 318 AudioAttributesCompatApi21.Wrapper.wrap((AudioAttributes) aa); 319 return aac; 320 } 321 return null; 322 } 323 324 // The rest of this file implements an approximation to AudioAttributes using old stream types 325 326 /** 327 * Return the content type. 328 * 329 * @return one of the values that can be set in {@link Builder#setContentType(int)} 330 */ 331 public int getContentType() { 332 if (Build.VERSION.SDK_INT >= 21 333 && !sForceLegacyBehavior 334 && mAudioAttributesWrapper != null) { 335 return mAudioAttributesWrapper.unwrap().getContentType(); 336 } else { 337 return mContentType; 338 } 339 } 340 341 /** 342 * Return the usage. 343 * 344 * @return one of the values that can be set in {@link Builder#setUsage(int)} 345 */ 346 public @AttributeUsage int getUsage() { 347 if (Build.VERSION.SDK_INT >= 21 348 && !sForceLegacyBehavior 349 && mAudioAttributesWrapper != null) { 350 return mAudioAttributesWrapper.unwrap().getUsage(); 351 } else { 352 return mUsage; 353 } 354 } 355 356 /** 357 * Return the flags. 358 * 359 * @return a combined mask of all flags 360 */ 361 public int getFlags() { 362 if (Build.VERSION.SDK_INT >= 21 363 && !sForceLegacyBehavior 364 && mAudioAttributesWrapper != null) { 365 return mAudioAttributesWrapper.unwrap().getFlags(); 366 } else { 367 int flags = mFlags; 368 int legacyStream = getLegacyStreamType(); 369 if (legacyStream == AudioManagerHidden.STREAM_BLUETOOTH_SCO) { 370 flags |= FLAG_SCO; 371 } else if (legacyStream == AudioManagerHidden.STREAM_SYSTEM_ENFORCED) { 372 flags |= FLAG_AUDIBILITY_ENFORCED; 373 } 374 return flags & FLAG_ALL_PUBLIC; 375 } 376 } 377 378 /** 379 * Builder class for {@link AudioAttributesCompat} objects. 380 * 381 * <p>example: 382 * 383 * <pre class="prettyprint"> 384 * new AudioAttributes.Builder() 385 * .setUsage(AudioAttributes.USAGE_MEDIA) 386 * .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) 387 * .build(); 388 * </pre> 389 * 390 * <p>By default all types of information (usage, content type, flags) conveyed by an <code> 391 * AudioAttributesCompat</code> instance are set to "unknown". Unknown information will be 392 * interpreted as a default value that is dependent on the context of use, for instance a {@link 393 * android.media.MediaPlayer} will use a default usage of 394 * {@link AudioAttributesCompat#USAGE_MEDIA}. See also {@link AudioAttributes.Builder}. 395 */ 396 public static class Builder { 397 private int mUsage = USAGE_UNKNOWN; 398 private int mContentType = CONTENT_TYPE_UNKNOWN; 399 private int mFlags = 0x0; 400 private Integer mLegacyStream; 401 private Object mAAObject; 402 403 /** 404 * Constructs a new Builder with the defaults. By default, usage and content type are 405 * respectively {@link AudioAttributesCompat#USAGE_UNKNOWN} and {@link 406 * AudioAttributesCompat#CONTENT_TYPE_UNKNOWN}, and flags are 0. It is recommended to 407 * configure the usage (with {@link #setUsage(int)}) or deriving attributes from a legacy 408 * stream type (with {@link #setLegacyStreamType(int)}) before calling {@link #build()} to 409 * override any default playback behavior in terms of routing and volume management. 410 */ 411 public Builder() { 412 } 413 414 /** 415 * Constructs a new Builder from a given AudioAttributes 416 * 417 * @param aa the AudioAttributesCompat object whose data will be reused in the new Builder. 418 */ 419 public Builder(AudioAttributesCompat aa) { 420 mUsage = aa.mUsage; 421 mContentType = aa.mContentType; 422 mFlags = aa.mFlags; 423 mLegacyStream = aa.mLegacyStream; 424 mAAObject = aa.unwrap(); 425 } 426 427 /** 428 * Combines all of the attributes that have been set and return a new {@link 429 * AudioAttributesCompat} object. 430 * 431 * @return a new {@link AudioAttributesCompat} object 432 */ 433 public AudioAttributesCompat build() { 434 if (!sForceLegacyBehavior && Build.VERSION.SDK_INT >= 21) { 435 // API21 436 if (mAAObject != null) { 437 // best case: underlying real AudioAttributes 438 return wrap(mAAObject); 439 } else { 440 // set up an API21 builder with whatever we have 441 AudioAttributes.Builder api21Builder = 442 new AudioAttributes.Builder() 443 .setContentType(mContentType) 444 .setFlags(mFlags) 445 .setUsage(mUsage); 446 if (mLegacyStream != null) { 447 // if a legacy stream was specified, throw that in 448 api21Builder.setLegacyStreamType(mLegacyStream); 449 } 450 return wrap(api21Builder.build()); 451 } 452 } else { 453 // pre-API21 454 final AudioAttributesCompat aac = new AudioAttributesCompat(); 455 aac.mContentType = mContentType; 456 aac.mFlags = mFlags; 457 aac.mUsage = mUsage; 458 aac.mLegacyStream = mLegacyStream; 459 aac.mAudioAttributesWrapper = null; 460 return aac; 461 } 462 } 463 464 /** 465 * Sets the attribute describing what is the intended use of the the audio signal, such as 466 * alarm or ringtone. 467 * 468 * @param usage one of {@link AudioAttributesCompat#USAGE_UNKNOWN}, {@link 469 * AudioAttributesCompat#USAGE_MEDIA}, {@link 470 * AudioAttributesCompat#USAGE_VOICE_COMMUNICATION}, {@link 471 * AudioAttributesCompat#USAGE_VOICE_COMMUNICATION_SIGNALLING}, {@link 472 * AudioAttributesCompat#USAGE_ALARM}, 473 * {@link AudioAttributesCompat#USAGE_NOTIFICATION}, 474 * {@link AudioAttributesCompat#USAGE_NOTIFICATION_RINGTONE}, {@link 475 * AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}, {@link 476 * AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}, {@link 477 * AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}, {@link 478 * AudioAttributesCompat#USAGE_NOTIFICATION_EVENT}, {@link 479 * AudioAttributesCompat#USAGE_ASSISTANT}, {@link 480 * AudioAttributesCompat#USAGE_ASSISTANCE_ACCESSIBILITY}, {@link 481 * AudioAttributesCompat#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, {@link 482 * AudioAttributesCompat#USAGE_ASSISTANCE_SONIFICATION}, {@link 483 * AudioAttributesCompat#USAGE_GAME}. 484 * @return the same Builder instance. 485 */ 486 public Builder setUsage(@AttributeUsage int usage) { 487 switch (usage) { 488 case USAGE_UNKNOWN: 489 case USAGE_MEDIA: 490 case USAGE_VOICE_COMMUNICATION: 491 case USAGE_VOICE_COMMUNICATION_SIGNALLING: 492 case USAGE_ALARM: 493 case USAGE_NOTIFICATION: 494 case USAGE_NOTIFICATION_RINGTONE: 495 case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 496 case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 497 case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 498 case USAGE_NOTIFICATION_EVENT: 499 case USAGE_ASSISTANCE_ACCESSIBILITY: 500 case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: 501 case USAGE_ASSISTANCE_SONIFICATION: 502 case USAGE_GAME: 503 case USAGE_VIRTUAL_SOURCE: 504 mUsage = usage; 505 break; 506 case USAGE_ASSISTANT: 507 if (!sForceLegacyBehavior && Build.VERSION.SDK_INT > 25) { 508 mUsage = usage; 509 } else { 510 mUsage = USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; 511 } 512 break; 513 default: 514 mUsage = USAGE_UNKNOWN; 515 } 516 return this; 517 } 518 519 /** 520 * Sets the attribute describing the content type of the audio signal, such as speech, or 521 * music. 522 * 523 * @param contentType the content type values, one of {@link 524 * AudioAttributesCompat#CONTENT_TYPE_MOVIE}, {@link 525 * AudioAttributesCompat#CONTENT_TYPE_MUSIC}, {@link 526 * AudioAttributesCompat#CONTENT_TYPE_SONIFICATION}, {@link 527 * AudioAttributesCompat#CONTENT_TYPE_SPEECH}, {@link 528 * AudioAttributesCompat#CONTENT_TYPE_UNKNOWN}. 529 * @return the same Builder instance. 530 */ 531 public Builder setContentType(@AttributeContentType int contentType) { 532 switch (contentType) { 533 case CONTENT_TYPE_UNKNOWN: 534 case CONTENT_TYPE_MOVIE: 535 case CONTENT_TYPE_MUSIC: 536 case CONTENT_TYPE_SONIFICATION: 537 case CONTENT_TYPE_SPEECH: 538 mContentType = contentType; 539 break; 540 default: 541 mUsage = CONTENT_TYPE_UNKNOWN; 542 } 543 return this; 544 } 545 546 /** 547 * Sets the combination of flags. 548 * 549 * <p>This is a bitwise OR with the existing flags. 550 * 551 * @param flags a combination of {@link AudioAttributesCompat#FLAG_AUDIBILITY_ENFORCED}, 552 * {@link AudioAttributesCompat#FLAG_HW_AV_SYNC}. 553 * @return the same Builder instance. 554 */ 555 public Builder setFlags(int flags) { 556 flags &= AudioAttributesCompat.FLAG_ALL; 557 mFlags |= flags; 558 return this; 559 } 560 561 /** 562 * Create an {@link AudioAttributesCompat} that best approximates the specified {@link 563 * AudioManager} stream type constant. 564 * 565 * @param streamType one of <code>AudioManager.STREAM_*</code> 566 * @return this same Builder 567 */ 568 public Builder setLegacyStreamType(int streamType) { 569 if (streamType == AudioManagerHidden.STREAM_ACCESSIBILITY) { 570 throw new IllegalArgumentException( 571 "STREAM_ACCESSIBILITY is not a legacy stream " 572 + "type that was used for audio playback"); 573 } 574 mLegacyStream = streamType; 575 mUsage = usageForStreamType(streamType); 576 return this; 577 } 578 } 579 580 @Override 581 public int hashCode() { 582 if (Build.VERSION.SDK_INT >= 21 583 && !sForceLegacyBehavior 584 && mAudioAttributesWrapper != null) { 585 return mAudioAttributesWrapper.unwrap().hashCode(); 586 } 587 588 return Arrays.hashCode(new Object[] {mContentType, mFlags, mUsage, mLegacyStream}); 589 } 590 591 @Override 592 public String toString() { 593 final StringBuilder sb = new StringBuilder("AudioAttributesCompat:"); 594 if (unwrap() != null) { 595 sb.append(" audioattributes=").append(unwrap()); 596 } else { 597 if (mLegacyStream != null) { 598 sb.append(" stream=").append(mLegacyStream); 599 sb.append(" derived"); 600 } 601 sb.append(" usage=") 602 .append(usageToString()) 603 .append(" content=") 604 .append(mContentType) 605 .append(" flags=0x") 606 .append(Integer.toHexString(mFlags).toUpperCase()); 607 } 608 return sb.toString(); 609 } 610 611 String usageToString() { 612 return usageToString(mUsage); 613 } 614 615 static String usageToString(int usage) { 616 switch (usage) { 617 case USAGE_UNKNOWN: 618 return new String("USAGE_UNKNOWN"); 619 case USAGE_MEDIA: 620 return new String("USAGE_MEDIA"); 621 case USAGE_VOICE_COMMUNICATION: 622 return new String("USAGE_VOICE_COMMUNICATION"); 623 case USAGE_VOICE_COMMUNICATION_SIGNALLING: 624 return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING"); 625 case USAGE_ALARM: 626 return new String("USAGE_ALARM"); 627 case USAGE_NOTIFICATION: 628 return new String("USAGE_NOTIFICATION"); 629 case USAGE_NOTIFICATION_RINGTONE: 630 return new String("USAGE_NOTIFICATION_RINGTONE"); 631 case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 632 return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST"); 633 case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 634 return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT"); 635 case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 636 return new String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED"); 637 case USAGE_NOTIFICATION_EVENT: 638 return new String("USAGE_NOTIFICATION_EVENT"); 639 case USAGE_ASSISTANCE_ACCESSIBILITY: 640 return new String("USAGE_ASSISTANCE_ACCESSIBILITY"); 641 case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: 642 return new String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"); 643 case USAGE_ASSISTANCE_SONIFICATION: 644 return new String("USAGE_ASSISTANCE_SONIFICATION"); 645 case USAGE_GAME: 646 return new String("USAGE_GAME"); 647 case USAGE_ASSISTANT: 648 return new String("USAGE_ASSISTANT"); 649 default: 650 return new String("unknown usage " + usage); 651 } 652 } 653 654 private abstract static class AudioManagerHidden { 655 public static final int STREAM_BLUETOOTH_SCO = 6; 656 public static final int STREAM_SYSTEM_ENFORCED = 7; 657 public static final int STREAM_TTS = 9; 658 public static final int STREAM_ACCESSIBILITY = 10; 659 } 660 661 private static int usageForStreamType(int streamType) { 662 switch (streamType) { 663 case AudioManager.STREAM_VOICE_CALL: 664 return USAGE_VOICE_COMMUNICATION; 665 case AudioManagerHidden.STREAM_SYSTEM_ENFORCED: 666 case AudioManager.STREAM_SYSTEM: 667 return USAGE_ASSISTANCE_SONIFICATION; 668 case AudioManager.STREAM_RING: 669 return USAGE_NOTIFICATION_RINGTONE; 670 case AudioManager.STREAM_MUSIC: 671 return USAGE_MEDIA; 672 case AudioManager.STREAM_ALARM: 673 return USAGE_ALARM; 674 case AudioManager.STREAM_NOTIFICATION: 675 return USAGE_NOTIFICATION; 676 case AudioManagerHidden.STREAM_BLUETOOTH_SCO: 677 return USAGE_VOICE_COMMUNICATION; 678 case AudioManager.STREAM_DTMF: 679 return USAGE_VOICE_COMMUNICATION_SIGNALLING; 680 case AudioManagerHidden.STREAM_ACCESSIBILITY: 681 return USAGE_ASSISTANCE_ACCESSIBILITY; 682 case AudioManagerHidden.STREAM_TTS: 683 default: 684 return USAGE_UNKNOWN; 685 } 686 } 687 688 /** 689 * Prevent AudioAttributes from being used even on platforms that support it. 690 * 691 * @hide For testing only. 692 */ 693 @RestrictTo(LIBRARY_GROUP) 694 public static void setForceLegacyBehavior(boolean force) { 695 sForceLegacyBehavior = force; 696 } 697 698 static int toVolumeStreamType(boolean fromGetVolumeControlStream, AudioAttributesCompat aa) { 699 return toVolumeStreamType(fromGetVolumeControlStream, aa.getFlags(), aa.getUsage()); 700 } 701 702 static int toVolumeStreamType( 703 boolean fromGetVolumeControlStream, int flags, @AttributeUsage int usage) { 704 // flags to stream type mapping 705 if ((flags & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) { 706 return fromGetVolumeControlStream 707 ? AudioManager.STREAM_SYSTEM 708 : AudioManagerHidden.STREAM_SYSTEM_ENFORCED; 709 } 710 if ((flags & FLAG_SCO) == FLAG_SCO) { 711 return fromGetVolumeControlStream 712 ? AudioManager.STREAM_VOICE_CALL 713 : AudioManagerHidden.STREAM_BLUETOOTH_SCO; 714 } 715 716 // usage to stream type mapping 717 switch (usage) { 718 case USAGE_MEDIA: 719 case USAGE_GAME: 720 case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: 721 case USAGE_ASSISTANT: 722 return AudioManager.STREAM_MUSIC; 723 case USAGE_ASSISTANCE_SONIFICATION: 724 return AudioManager.STREAM_SYSTEM; 725 case USAGE_VOICE_COMMUNICATION: 726 return AudioManager.STREAM_VOICE_CALL; 727 case USAGE_VOICE_COMMUNICATION_SIGNALLING: 728 return fromGetVolumeControlStream 729 ? AudioManager.STREAM_VOICE_CALL 730 : AudioManager.STREAM_DTMF; 731 case USAGE_ALARM: 732 return AudioManager.STREAM_ALARM; 733 case USAGE_NOTIFICATION_RINGTONE: 734 return AudioManager.STREAM_RING; 735 case USAGE_NOTIFICATION: 736 case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 737 case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 738 case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 739 case USAGE_NOTIFICATION_EVENT: 740 return AudioManager.STREAM_NOTIFICATION; 741 case USAGE_ASSISTANCE_ACCESSIBILITY: 742 return AudioManagerHidden.STREAM_ACCESSIBILITY; 743 case USAGE_UNKNOWN: 744 return fromGetVolumeControlStream 745 ? AudioManager.USE_DEFAULT_STREAM_TYPE 746 : AudioManager.STREAM_MUSIC; 747 default: 748 if (fromGetVolumeControlStream) { 749 throw new IllegalArgumentException( 750 "Unknown usage value " + usage + " in audio attributes"); 751 } else { 752 return AudioManager.STREAM_MUSIC; 753 } 754 } 755 } 756 757 @Override 758 public boolean equals(Object o) { 759 if (this == o) return true; 760 if (o == null || getClass() != o.getClass()) return false; 761 762 final AudioAttributesCompat that = (AudioAttributesCompat) o; 763 764 if (Build.VERSION.SDK_INT >= 21 765 && !sForceLegacyBehavior 766 && mAudioAttributesWrapper != null) { 767 return mAudioAttributesWrapper.unwrap().equals(that.unwrap()); 768 } 769 770 return ((mContentType == that.getContentType()) 771 && (mFlags == that.getFlags()) 772 && (mUsage == that.getUsage()) 773 && (mLegacyStream == that.mLegacyStream)); // query the slot directly, don't guess 774 } 775 776 /** @hide */ 777 @IntDef({ 778 USAGE_UNKNOWN, 779 USAGE_MEDIA, 780 USAGE_VOICE_COMMUNICATION, 781 USAGE_VOICE_COMMUNICATION_SIGNALLING, 782 USAGE_ALARM, 783 USAGE_NOTIFICATION, 784 USAGE_NOTIFICATION_RINGTONE, 785 USAGE_NOTIFICATION_COMMUNICATION_REQUEST, 786 USAGE_NOTIFICATION_COMMUNICATION_INSTANT, 787 USAGE_NOTIFICATION_COMMUNICATION_DELAYED, 788 USAGE_NOTIFICATION_EVENT, 789 USAGE_ASSISTANCE_ACCESSIBILITY, 790 USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, 791 USAGE_ASSISTANCE_SONIFICATION, 792 USAGE_GAME, 793 USAGE_ASSISTANT, 794 }) 795 @RestrictTo(LIBRARY_GROUP) 796 @Retention(RetentionPolicy.SOURCE) 797 public @interface AttributeUsage { 798 } 799 800 /** @hide */ 801 @IntDef({ 802 CONTENT_TYPE_UNKNOWN, 803 CONTENT_TYPE_SPEECH, 804 CONTENT_TYPE_MUSIC, 805 CONTENT_TYPE_MOVIE, 806 CONTENT_TYPE_SONIFICATION 807 }) 808 @Retention(RetentionPolicy.SOURCE) 809 @RestrictTo(LIBRARY_GROUP) 810 public @interface AttributeContentType { 811 } 812} 813