/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v4.media; import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; import android.media.AudioAttributes; import android.media.AudioManager; import android.os.Build; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RestrictTo; import android.util.SparseIntArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** * A class to encapsulate a collection of attributes describing information about an audio stream. * *

AudioAttributesCompat supersede the notion of stream types (see for instance * {@link AudioManager#STREAM_MUSIC} or {@link AudioManager#STREAM_ALARM}) for defining the behavior * of audio playback. Attributes allow an application to specify more information than is conveyed * in a stream type by allowing the application to define: * *

* *

AudioAttributesCompat instance is built through its builder, {@link * AudioAttributesCompat.Builder}. Also see {@link android.media.AudioAttributes} for the framework * implementation of this class. */ public class AudioAttributesCompat { private static final String TAG = "AudioAttributesCompat"; /** * Content type value to use when the content type is unknown, or other than the ones defined. */ public static final int CONTENT_TYPE_UNKNOWN = AudioAttributes.CONTENT_TYPE_UNKNOWN; /** Content type value to use when the content type is speech. */ public static final int CONTENT_TYPE_SPEECH = AudioAttributes.CONTENT_TYPE_SPEECH; /** Content type value to use when the content type is music. */ public static final int CONTENT_TYPE_MUSIC = AudioAttributes.CONTENT_TYPE_MUSIC; /** * Content type value to use when the content type is a soundtrack, typically accompanying a * movie or TV program. */ public static final int CONTENT_TYPE_MOVIE = AudioAttributes.CONTENT_TYPE_MOVIE; /** * Content type value to use when the content type is a sound used to accompany a user action, * such as a beep or sound effect expressing a key click, or event, such as the type of a sound * for a bonus being received in a game. These sounds are mostly synthesized or short Foley * sounds. */ public static final int CONTENT_TYPE_SONIFICATION = AudioAttributes.CONTENT_TYPE_SONIFICATION; /** Usage value to use when the usage is unknown. */ public static final int USAGE_UNKNOWN = AudioAttributes.USAGE_UNKNOWN; /** Usage value to use when the usage is media, such as music, or movie soundtracks. */ public static final int USAGE_MEDIA = AudioAttributes.USAGE_MEDIA; /** Usage value to use when the usage is voice communications, such as telephony or VoIP. */ public static final int USAGE_VOICE_COMMUNICATION = AudioAttributes.USAGE_VOICE_COMMUNICATION; /** * Usage value to use when the usage is in-call signalling, such as with a "busy" beep, or DTMF * tones. */ public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING; /** Usage value to use when the usage is an alarm (e.g. wake-up alarm). */ public static final int USAGE_ALARM = AudioAttributes.USAGE_ALARM; /** * Usage value to use when the usage is notification. See other notification usages for more * specialized uses. */ public static final int USAGE_NOTIFICATION = AudioAttributes.USAGE_NOTIFICATION; /** Usage value to use when the usage is telephony ringtone. */ public static final int USAGE_NOTIFICATION_RINGTONE = AudioAttributes.USAGE_NOTIFICATION_RINGTONE; /** * Usage value to use when the usage is a request to enter/end a communication, such as a VoIP * communication or video-conference. */ public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST; /** * Usage value to use when the usage is notification for an "instant" communication such as a * chat, or SMS. */ public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT; /** * Usage value to use when the usage is notification for a non-immediate type of communication * such as e-mail. */ public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED; /** * Usage value to use when the usage is to attract the user's attention, such as a reminder or * low battery warning. */ public static final int USAGE_NOTIFICATION_EVENT = AudioAttributes.USAGE_NOTIFICATION_EVENT; /** Usage value to use when the usage is for accessibility, such as with a screen reader. */ public static final int USAGE_ASSISTANCE_ACCESSIBILITY = AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; /** Usage value to use when the usage is driving or navigation directions. */ public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; /** Usage value to use when the usage is sonification, such as with user interface sounds. */ public static final int USAGE_ASSISTANCE_SONIFICATION = AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; /** Usage value to use when the usage is for game audio. */ public static final int USAGE_GAME = AudioAttributes.USAGE_GAME; // usage not available to clients private static final int USAGE_VIRTUAL_SOURCE = 15; // AudioAttributes.USAGE_VIRTUAL_SOURCE; /** * Usage value to use for audio responses to user queries, audio instructions or help * utterances. */ public static final int USAGE_ASSISTANT = AudioAttributes.USAGE_ASSISTANT; /** * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES * if applicable. */ // private API private static final int SUPPRESSIBLE_NOTIFICATION = 1; private static final int SUPPRESSIBLE_CALL = 2; private static final SparseIntArray SUPPRESSIBLE_USAGES; // used by tests private static boolean sForceLegacyBehavior; static { SUPPRESSIBLE_USAGES = new SparseIntArray(); SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION, SUPPRESSIBLE_NOTIFICATION); SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_RINGTONE, SUPPRESSIBLE_CALL); SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_COMMUNICATION_REQUEST, SUPPRESSIBLE_CALL); SUPPRESSIBLE_USAGES.put( USAGE_NOTIFICATION_COMMUNICATION_INSTANT, SUPPRESSIBLE_NOTIFICATION); SUPPRESSIBLE_USAGES.put( USAGE_NOTIFICATION_COMMUNICATION_DELAYED, SUPPRESSIBLE_NOTIFICATION); SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_EVENT, SUPPRESSIBLE_NOTIFICATION); } private static final int[] SDK_USAGES = { USAGE_UNKNOWN, USAGE_MEDIA, USAGE_VOICE_COMMUNICATION, USAGE_VOICE_COMMUNICATION_SIGNALLING, USAGE_ALARM, USAGE_NOTIFICATION, USAGE_NOTIFICATION_RINGTONE, USAGE_NOTIFICATION_COMMUNICATION_REQUEST, USAGE_NOTIFICATION_COMMUNICATION_INSTANT, USAGE_NOTIFICATION_COMMUNICATION_DELAYED, USAGE_NOTIFICATION_EVENT, USAGE_ASSISTANCE_ACCESSIBILITY, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, USAGE_ASSISTANCE_SONIFICATION, USAGE_GAME, USAGE_ASSISTANT, }; /** Flag defining a behavior where the audibility of the sound will be ensured by the system. */ public static final int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0; // flags for @hide API so we can create a proper flags mask private static final int FLAG_SECURE = 0x1 << 1; private static final int FLAG_SCO = 0x1 << 2; private static final int FLAG_BEACON = 0x1 << 3; /** Flag requesting the use of an output stream supporting hardware A/V synchronization. */ public static final int FLAG_HW_AV_SYNC = 0x1 << 4; // more @hide flags private static final int FLAG_HW_HOTWORD = 0x1 << 5; private static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1 << 6; private static final int FLAG_BYPASS_MUTE = 0x1 << 7; private static final int FLAG_LOW_LATENCY = 0x1 << 8; private static final int FLAG_DEEP_BUFFER = 0x1 << 9; private static final int FLAG_ALL = (FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO | FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER); private static final int FLAG_ALL_PUBLIC = (FLAG_AUDIBILITY_ENFORCED | FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY); int mUsage = USAGE_UNKNOWN; int mContentType = CONTENT_TYPE_UNKNOWN; int mFlags = 0x0; Integer mLegacyStream; private AudioAttributesCompatApi21.Wrapper mAudioAttributesWrapper; private AudioAttributesCompat() { } /** * Returns the stream type matching the given attributes for volume control. Use this method to * derive the stream type needed to configure the volume control slider in an {@link * android.app.Activity} with {@link android.app.Activity#setVolumeControlStream(int)}.
* Do not use this method to set the stream type on an audio player object (e.g. {@link * android.media.AudioTrack}, {@link android.media.MediaPlayer}) as this is deprecated; * use AudioAttributes instead. * * @return a valid stream type for Activity or stream volume control that matches * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct * match. Note that USE_DEFAULT_STREAM_TYPE is not a valid value for {@link * AudioManager#setStreamVolume(int, int, int)}. */ public int getVolumeControlStream() { if (this == null) { throw new IllegalArgumentException("Invalid null audio attributes"); } if (Build.VERSION.SDK_INT >= 26 && !sForceLegacyBehavior && unwrap() != null) { return ((AudioAttributes) unwrap()).getVolumeControlStream(); } return toVolumeStreamType(true, this); } // public API unique to AudioAttributesCompat /** * If the current SDK level is 21 or higher, return the {@link AudioAttributes} object inside * this {@link AudioAttributesCompat}. Otherwise null. * * @return the underlying {@link AudioAttributes} object or null */ @Nullable public Object unwrap() { if (mAudioAttributesWrapper != null) { return mAudioAttributesWrapper.unwrap(); } return null; } /** * Return a stream type passed to {@link Builder#setLegacyStreamType(int)}, or -1 if no legacy * stream is available * * @return the stream type {@see AudioManager} */ public int getLegacyStreamType() { // case 1: developer explicitly set a legacy stream, // so just hand that back if (mLegacyStream != null) { return mLegacyStream; } // case 2: API 21+ and we have a real AudioAttributes // the same caveats in AudioAttributes#toLegacyStreamTyoe apply: // only use this for volume control if (Build.VERSION.SDK_INT >= 21) { if (!sForceLegacyBehavior) { return AudioAttributesCompatApi21.toLegacyStreamType(mAudioAttributesWrapper); } } // case 3: developer set up AudioAttrs using new flags/usage APIs // but we are running pre-API21, so use the heuristic below return toVolumeStreamType(false, mFlags, mUsage); } /** * Create an {@link AudioAttributesCompat} given an API 21 {@link AudioAttributes} object. * * @param aa an instance of {@link AudioAttributes} * @return the new AudioAttributesCompat, or null on API < 21 */ @Nullable public static AudioAttributesCompat wrap(@NonNull final Object aa) { if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior) { final AudioAttributesCompat aac = new AudioAttributesCompat(); aac.mAudioAttributesWrapper = AudioAttributesCompatApi21.Wrapper.wrap((AudioAttributes) aa); return aac; } return null; } // The rest of this file implements an approximation to AudioAttributes using old stream types /** * Return the content type. * * @return one of the values that can be set in {@link Builder#setContentType(int)} */ public int getContentType() { if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior && mAudioAttributesWrapper != null) { return mAudioAttributesWrapper.unwrap().getContentType(); } else { return mContentType; } } /** * Return the usage. * * @return one of the values that can be set in {@link Builder#setUsage(int)} */ public @AttributeUsage int getUsage() { if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior && mAudioAttributesWrapper != null) { return mAudioAttributesWrapper.unwrap().getUsage(); } else { return mUsage; } } /** * Return the flags. * * @return a combined mask of all flags */ public int getFlags() { if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior && mAudioAttributesWrapper != null) { return mAudioAttributesWrapper.unwrap().getFlags(); } else { int flags = mFlags; int legacyStream = getLegacyStreamType(); if (legacyStream == AudioManagerHidden.STREAM_BLUETOOTH_SCO) { flags |= FLAG_SCO; } else if (legacyStream == AudioManagerHidden.STREAM_SYSTEM_ENFORCED) { flags |= FLAG_AUDIBILITY_ENFORCED; } return flags & FLAG_ALL_PUBLIC; } } /** * Builder class for {@link AudioAttributesCompat} objects. * *

example: * *

     * new AudioAttributes.Builder()
     * .setUsage(AudioAttributes.USAGE_MEDIA)
     * .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
     * .build();
     * 
* *

By default all types of information (usage, content type, flags) conveyed by an * AudioAttributesCompat instance are set to "unknown". Unknown information will be * interpreted as a default value that is dependent on the context of use, for instance a {@link * android.media.MediaPlayer} will use a default usage of * {@link AudioAttributesCompat#USAGE_MEDIA}. See also {@link AudioAttributes.Builder}. */ public static class Builder { private int mUsage = USAGE_UNKNOWN; private int mContentType = CONTENT_TYPE_UNKNOWN; private int mFlags = 0x0; private Integer mLegacyStream; private Object mAAObject; /** * Constructs a new Builder with the defaults. By default, usage and content type are * respectively {@link AudioAttributesCompat#USAGE_UNKNOWN} and {@link * AudioAttributesCompat#CONTENT_TYPE_UNKNOWN}, and flags are 0. It is recommended to * configure the usage (with {@link #setUsage(int)}) or deriving attributes from a legacy * stream type (with {@link #setLegacyStreamType(int)}) before calling {@link #build()} to * override any default playback behavior in terms of routing and volume management. */ public Builder() { } /** * Constructs a new Builder from a given AudioAttributes * * @param aa the AudioAttributesCompat object whose data will be reused in the new Builder. */ public Builder(AudioAttributesCompat aa) { mUsage = aa.mUsage; mContentType = aa.mContentType; mFlags = aa.mFlags; mLegacyStream = aa.mLegacyStream; mAAObject = aa.unwrap(); } /** * Combines all of the attributes that have been set and return a new {@link * AudioAttributesCompat} object. * * @return a new {@link AudioAttributesCompat} object */ public AudioAttributesCompat build() { if (!sForceLegacyBehavior && Build.VERSION.SDK_INT >= 21) { // API21 if (mAAObject != null) { // best case: underlying real AudioAttributes return wrap(mAAObject); } else { // set up an API21 builder with whatever we have AudioAttributes.Builder api21Builder = new AudioAttributes.Builder() .setContentType(mContentType) .setFlags(mFlags) .setUsage(mUsage); if (mLegacyStream != null) { // if a legacy stream was specified, throw that in api21Builder.setLegacyStreamType(mLegacyStream); } return wrap(api21Builder.build()); } } else { // pre-API21 final AudioAttributesCompat aac = new AudioAttributesCompat(); aac.mContentType = mContentType; aac.mFlags = mFlags; aac.mUsage = mUsage; aac.mLegacyStream = mLegacyStream; aac.mAudioAttributesWrapper = null; return aac; } } /** * Sets the attribute describing what is the intended use of the the audio signal, such as * alarm or ringtone. * * @param usage one of {@link AudioAttributesCompat#USAGE_UNKNOWN}, {@link * AudioAttributesCompat#USAGE_MEDIA}, {@link * AudioAttributesCompat#USAGE_VOICE_COMMUNICATION}, {@link * AudioAttributesCompat#USAGE_VOICE_COMMUNICATION_SIGNALLING}, {@link * AudioAttributesCompat#USAGE_ALARM}, * {@link AudioAttributesCompat#USAGE_NOTIFICATION}, * {@link AudioAttributesCompat#USAGE_NOTIFICATION_RINGTONE}, {@link * AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}, {@link * AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}, {@link * AudioAttributesCompat#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}, {@link * AudioAttributesCompat#USAGE_NOTIFICATION_EVENT}, {@link * AudioAttributesCompat#USAGE_ASSISTANT}, {@link * AudioAttributesCompat#USAGE_ASSISTANCE_ACCESSIBILITY}, {@link * AudioAttributesCompat#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, {@link * AudioAttributesCompat#USAGE_ASSISTANCE_SONIFICATION}, {@link * AudioAttributesCompat#USAGE_GAME}. * @return the same Builder instance. */ public Builder setUsage(@AttributeUsage int usage) { switch (usage) { case USAGE_UNKNOWN: case USAGE_MEDIA: case USAGE_VOICE_COMMUNICATION: case USAGE_VOICE_COMMUNICATION_SIGNALLING: case USAGE_ALARM: case USAGE_NOTIFICATION: case USAGE_NOTIFICATION_RINGTONE: case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: case USAGE_NOTIFICATION_EVENT: case USAGE_ASSISTANCE_ACCESSIBILITY: case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: case USAGE_ASSISTANCE_SONIFICATION: case USAGE_GAME: case USAGE_VIRTUAL_SOURCE: mUsage = usage; break; case USAGE_ASSISTANT: if (!sForceLegacyBehavior && Build.VERSION.SDK_INT > 25) { mUsage = usage; } else { mUsage = USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; } break; default: mUsage = USAGE_UNKNOWN; } return this; } /** * Sets the attribute describing the content type of the audio signal, such as speech, or * music. * * @param contentType the content type values, one of {@link * AudioAttributesCompat#CONTENT_TYPE_MOVIE}, {@link * AudioAttributesCompat#CONTENT_TYPE_MUSIC}, {@link * AudioAttributesCompat#CONTENT_TYPE_SONIFICATION}, {@link * AudioAttributesCompat#CONTENT_TYPE_SPEECH}, {@link * AudioAttributesCompat#CONTENT_TYPE_UNKNOWN}. * @return the same Builder instance. */ public Builder setContentType(@AttributeContentType int contentType) { switch (contentType) { case CONTENT_TYPE_UNKNOWN: case CONTENT_TYPE_MOVIE: case CONTENT_TYPE_MUSIC: case CONTENT_TYPE_SONIFICATION: case CONTENT_TYPE_SPEECH: mContentType = contentType; break; default: mUsage = CONTENT_TYPE_UNKNOWN; } return this; } /** * Sets the combination of flags. * *

This is a bitwise OR with the existing flags. * * @param flags a combination of {@link AudioAttributesCompat#FLAG_AUDIBILITY_ENFORCED}, * {@link AudioAttributesCompat#FLAG_HW_AV_SYNC}. * @return the same Builder instance. */ public Builder setFlags(int flags) { flags &= AudioAttributesCompat.FLAG_ALL; mFlags |= flags; return this; } /** * Create an {@link AudioAttributesCompat} that best approximates the specified {@link * AudioManager} stream type constant. * * @param streamType one of AudioManager.STREAM_* * @return this same Builder */ public Builder setLegacyStreamType(int streamType) { if (streamType == AudioManagerHidden.STREAM_ACCESSIBILITY) { throw new IllegalArgumentException( "STREAM_ACCESSIBILITY is not a legacy stream " + "type that was used for audio playback"); } mLegacyStream = streamType; mUsage = usageForStreamType(streamType); return this; } } @Override public int hashCode() { if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior && mAudioAttributesWrapper != null) { return mAudioAttributesWrapper.unwrap().hashCode(); } return Arrays.hashCode(new Object[] {mContentType, mFlags, mUsage, mLegacyStream}); } @Override public String toString() { final StringBuilder sb = new StringBuilder("AudioAttributesCompat:"); if (unwrap() != null) { sb.append(" audioattributes=").append(unwrap()); } else { if (mLegacyStream != null) { sb.append(" stream=").append(mLegacyStream); sb.append(" derived"); } sb.append(" usage=") .append(usageToString()) .append(" content=") .append(mContentType) .append(" flags=0x") .append(Integer.toHexString(mFlags).toUpperCase()); } return sb.toString(); } String usageToString() { return usageToString(mUsage); } static String usageToString(int usage) { switch (usage) { case USAGE_UNKNOWN: return new String("USAGE_UNKNOWN"); case USAGE_MEDIA: return new String("USAGE_MEDIA"); case USAGE_VOICE_COMMUNICATION: return new String("USAGE_VOICE_COMMUNICATION"); case USAGE_VOICE_COMMUNICATION_SIGNALLING: return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING"); case USAGE_ALARM: return new String("USAGE_ALARM"); case USAGE_NOTIFICATION: return new String("USAGE_NOTIFICATION"); case USAGE_NOTIFICATION_RINGTONE: return new String("USAGE_NOTIFICATION_RINGTONE"); case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST"); case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT"); case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: return new String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED"); case USAGE_NOTIFICATION_EVENT: return new String("USAGE_NOTIFICATION_EVENT"); case USAGE_ASSISTANCE_ACCESSIBILITY: return new String("USAGE_ASSISTANCE_ACCESSIBILITY"); case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: return new String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"); case USAGE_ASSISTANCE_SONIFICATION: return new String("USAGE_ASSISTANCE_SONIFICATION"); case USAGE_GAME: return new String("USAGE_GAME"); case USAGE_ASSISTANT: return new String("USAGE_ASSISTANT"); default: return new String("unknown usage " + usage); } } private abstract static class AudioManagerHidden { public static final int STREAM_BLUETOOTH_SCO = 6; public static final int STREAM_SYSTEM_ENFORCED = 7; public static final int STREAM_TTS = 9; public static final int STREAM_ACCESSIBILITY = 10; } private static int usageForStreamType(int streamType) { switch (streamType) { case AudioManager.STREAM_VOICE_CALL: return USAGE_VOICE_COMMUNICATION; case AudioManagerHidden.STREAM_SYSTEM_ENFORCED: case AudioManager.STREAM_SYSTEM: return USAGE_ASSISTANCE_SONIFICATION; case AudioManager.STREAM_RING: return USAGE_NOTIFICATION_RINGTONE; case AudioManager.STREAM_MUSIC: return USAGE_MEDIA; case AudioManager.STREAM_ALARM: return USAGE_ALARM; case AudioManager.STREAM_NOTIFICATION: return USAGE_NOTIFICATION; case AudioManagerHidden.STREAM_BLUETOOTH_SCO: return USAGE_VOICE_COMMUNICATION; case AudioManager.STREAM_DTMF: return USAGE_VOICE_COMMUNICATION_SIGNALLING; case AudioManagerHidden.STREAM_ACCESSIBILITY: return USAGE_ASSISTANCE_ACCESSIBILITY; case AudioManagerHidden.STREAM_TTS: default: return USAGE_UNKNOWN; } } /** * Prevent AudioAttributes from being used even on platforms that support it. * * @hide For testing only. */ @RestrictTo(LIBRARY_GROUP) public static void setForceLegacyBehavior(boolean force) { sForceLegacyBehavior = force; } static int toVolumeStreamType(boolean fromGetVolumeControlStream, AudioAttributesCompat aa) { return toVolumeStreamType(fromGetVolumeControlStream, aa.getFlags(), aa.getUsage()); } static int toVolumeStreamType( boolean fromGetVolumeControlStream, int flags, @AttributeUsage int usage) { // flags to stream type mapping if ((flags & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) { return fromGetVolumeControlStream ? AudioManager.STREAM_SYSTEM : AudioManagerHidden.STREAM_SYSTEM_ENFORCED; } if ((flags & FLAG_SCO) == FLAG_SCO) { return fromGetVolumeControlStream ? AudioManager.STREAM_VOICE_CALL : AudioManagerHidden.STREAM_BLUETOOTH_SCO; } // usage to stream type mapping switch (usage) { case USAGE_MEDIA: case USAGE_GAME: case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: case USAGE_ASSISTANT: return AudioManager.STREAM_MUSIC; case USAGE_ASSISTANCE_SONIFICATION: return AudioManager.STREAM_SYSTEM; case USAGE_VOICE_COMMUNICATION: return AudioManager.STREAM_VOICE_CALL; case USAGE_VOICE_COMMUNICATION_SIGNALLING: return fromGetVolumeControlStream ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_DTMF; case USAGE_ALARM: return AudioManager.STREAM_ALARM; case USAGE_NOTIFICATION_RINGTONE: return AudioManager.STREAM_RING; case USAGE_NOTIFICATION: case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: case USAGE_NOTIFICATION_EVENT: return AudioManager.STREAM_NOTIFICATION; case USAGE_ASSISTANCE_ACCESSIBILITY: return AudioManagerHidden.STREAM_ACCESSIBILITY; case USAGE_UNKNOWN: return fromGetVolumeControlStream ? AudioManager.USE_DEFAULT_STREAM_TYPE : AudioManager.STREAM_MUSIC; default: if (fromGetVolumeControlStream) { throw new IllegalArgumentException( "Unknown usage value " + usage + " in audio attributes"); } else { return AudioManager.STREAM_MUSIC; } } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final AudioAttributesCompat that = (AudioAttributesCompat) o; if (Build.VERSION.SDK_INT >= 21 && !sForceLegacyBehavior && mAudioAttributesWrapper != null) { return mAudioAttributesWrapper.unwrap().equals(that.unwrap()); } return ((mContentType == that.getContentType()) && (mFlags == that.getFlags()) && (mUsage == that.getUsage()) && (mLegacyStream != null ? mLegacyStream.equals(that.mLegacyStream) : that.mLegacyStream == null)); // query the slot directly, don't guess } /** @hide */ @IntDef({ USAGE_UNKNOWN, USAGE_MEDIA, USAGE_VOICE_COMMUNICATION, USAGE_VOICE_COMMUNICATION_SIGNALLING, USAGE_ALARM, USAGE_NOTIFICATION, USAGE_NOTIFICATION_RINGTONE, USAGE_NOTIFICATION_COMMUNICATION_REQUEST, USAGE_NOTIFICATION_COMMUNICATION_INSTANT, USAGE_NOTIFICATION_COMMUNICATION_DELAYED, USAGE_NOTIFICATION_EVENT, USAGE_ASSISTANCE_ACCESSIBILITY, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, USAGE_ASSISTANCE_SONIFICATION, USAGE_GAME, USAGE_ASSISTANT, }) @RestrictTo(LIBRARY_GROUP) @Retention(RetentionPolicy.SOURCE) public @interface AttributeUsage { } /** @hide */ @IntDef({ CONTENT_TYPE_UNKNOWN, CONTENT_TYPE_SPEECH, CONTENT_TYPE_MUSIC, CONTENT_TYPE_MOVIE, CONTENT_TYPE_SONIFICATION }) @Retention(RetentionPolicy.SOURCE) @RestrictTo(LIBRARY_GROUP) public @interface AttributeContentType { } }