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 &lt; 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