AudioAttributes.java revision 7b41467704f941b11af6aace3e40993afc7f6c6f
1/*
2 * Copyright (C) 2014 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.media;
18
19import android.annotation.IntDef;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.util.Log;
23
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.util.Collections;
27import java.util.HashSet;
28import java.util.Iterator;
29import java.util.Set;
30
31/**
32 * A class to encapsulate a collection of attributes describing information about an audio
33 * player or recorder.
34 */
35public final class AudioAttributes implements Parcelable {
36    private final static String TAG = "AudioAttributes";
37
38    /**
39     * Content type value to use when the content type is unknown, or other than the ones defined.
40     */
41    public final static int CONTENT_TYPE_UNKNOWN = 0;
42    /**
43     * Content type value to use when the content type is speech.
44     */
45    public final static int CONTENT_TYPE_SPEECH = 1;
46    /**
47     * Content type value to use when the content type is music.
48     */
49    public final static int CONTENT_TYPE_MUSIC = 2;
50    /**
51     * Content type value to use when the content type is a soundtrack, typically accompanying
52     * a movie or TV program.
53     */
54    public final static int CONTENT_TYPE_MOVIE = 3;
55    /**
56     * Content type value to use when the content type is a sound used to accompany a user
57     * action, such as a beep or sound effect expressing a key click, or event, such as the
58     * type of a sound for a bonus being received in a game. These sounds are mostly synthesized
59     * or short Foley sounds.
60     */
61    public final static int CONTENT_TYPE_SONIFICATION = 4;
62
63    /**
64     * Usage value to use when the usage is unknown.
65     */
66    public final static int USAGE_UNKNOWN = 0;
67    /**
68     * Usage value to use when the usage is media, such as music, or movie
69     * soundtracks.
70     */
71    public final static int USAGE_MEDIA = 1;
72    /**
73     * Usage value to use when the usage is voice communications, such as telephony
74     * or VoIP.
75     */
76    public final static int USAGE_VOICE_COMMUNICATION = 2;
77    /**
78     * Usage value to use when the usage is in-call signalling, such as with
79     * a "busy" beep, or DTMF tones.
80     */
81    public final static int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3;
82    /**
83     * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
84     */
85    public final static int USAGE_ALARM = 4;
86    /**
87     * Usage value to use when the usage is notification. See other
88     * notification usages for more specialized uses.
89     */
90    public final static int USAGE_NOTIFICATION = 5;
91    /**
92     * Usage value to use when the usage is telephony ringtone.
93     */
94    public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6;
95    /**
96     * Usage value to use when the usage is a request to enter/end a
97     * communication, such as a VoIP communication or video-conference.
98     */
99    public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7;
100    /**
101     * Usage value to use when the usage is notification for an "instant"
102     * communication such as a chat, or SMS.
103     */
104    public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8;
105    /**
106     * Usage value to use when the usage is notification for a
107     * non-immediate type of communication such as e-mail.
108     */
109    public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9;
110    /**
111     * Usage value to use when the usage is to attract the user's attention,
112     * such as a reminder or low battery warning.
113     */
114    public final static int USAGE_NOTIFICATION_EVENT = 10;
115    /**
116     * Usage value to use when the usage is for accessibility, such as with
117     * a screen reader.
118     */
119    public final static int USAGE_ASSISTANCE_ACCESSIBILITY = 11;
120    /**
121     * Usage value to use when the usage is driving or navigation directions.
122     */
123    public final static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12;
124    /**
125     * Usage value to use when the usage is sonification, such as  with user
126     * interface sounds.
127     */
128    public final static int USAGE_ASSISTANCE_SONIFICATION = 13;
129    /**
130     * Usage value to use when the usage is for game audio.
131     */
132    public final static int USAGE_GAME = 14;
133
134    /**
135     * Flag defining a behavior where the audibility of the sound will be ensured by the system.
136     */
137    public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
138    /**
139     * @hide
140     * Flag defining a behavior where the playback of the sound is ensured without
141     * degradation only when going to a secure sink.
142     */
143    // FIXME not guaranteed yet
144    // TODO  add OR to getFlags() when supported and in public API
145    public final static int FLAG_SECURE = 0x1 << 1;
146    /**
147     * @hide
148     * Flag to enable when the stream is associated with SCO usage.
149     * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO
150     */
151    public final static int FLAG_SCO = 0x1 << 2;
152
153
154    private int mUsage = USAGE_UNKNOWN;
155    private int mContentType = CONTENT_TYPE_UNKNOWN;
156    private int mFlags = 0x0;
157    private HashSet<String> mTags;
158    private String mFormattedTags;
159
160    private AudioAttributes() {
161    }
162
163    /**
164     * Return the content type.
165     * @return one of the values that can be set in {@link Builder#setContentType(int)}
166     */
167    public int getContentType() {
168        return mContentType;
169    }
170
171    /**
172     * Return the usage.
173     * @return one of the values that can be set in {@link Builder#setUsage(int)}
174     */
175    public int getUsage() {
176        return mUsage;
177    }
178
179    /**
180     * Return the flags.
181     * @return a combined mask of all flags
182     */
183    public int getFlags() {
184        // only return the flags that are public
185        return (mFlags & (FLAG_AUDIBILITY_ENFORCED));
186    }
187
188    /**
189     * @hide
190     * Return all the flags, even the non-public ones.
191     * Internal use only
192     * @return a combined mask of all flags
193     */
194    public int getAllFlags() {
195        return mFlags;
196    }
197
198    /**
199     * @hide
200     * Return the set of tags.
201     * @return a read-only set of all tags stored as strings.
202     */
203    public Set<String> getTags() {
204        return Collections.unmodifiableSet(mTags);
205    }
206
207    /**
208     * Builder class for {@link AudioAttributes} objects.
209     */
210    public static class Builder {
211        private int mUsage = USAGE_UNKNOWN;
212        private int mContentType = CONTENT_TYPE_UNKNOWN;
213        private int mFlags = 0x0;
214        private HashSet<String> mTags = new HashSet<String>();
215
216        /**
217         * Constructs a new Builder with the defaults.
218         */
219        public Builder() {
220        }
221
222        /**
223         * Constructs a new Builder from a given AudioAttributes
224         * @param aa the AudioAttributes object whose data will be reused in the new Builder.
225         */
226        @SuppressWarnings("unchecked") // for cloning of mTags
227        public Builder(AudioAttributes aa) {
228            mUsage = aa.mUsage;
229            mContentType = aa.mContentType;
230            mFlags = aa.mFlags;
231            mTags = (HashSet<String>) aa.mTags.clone();
232        }
233
234        /**
235         * Combines all of the attributes that have been set and return a new
236         * {@link AudioAttributes} object.
237         * @return a new {@link AudioAttributes} object
238         */
239        @SuppressWarnings("unchecked") // for cloning of mTags
240        public AudioAttributes build() {
241            AudioAttributes aa = new AudioAttributes();
242            aa.mContentType = mContentType;
243            aa.mUsage = mUsage;
244            aa.mFlags = mFlags;
245            aa.mTags = (HashSet<String>) mTags.clone();
246            final Iterator<String> tagIterator = mTags.iterator();
247            String allTagsInOne = new String();
248            while (tagIterator.hasNext()) {
249                allTagsInOne += tagIterator.next() + ";";
250            }
251            aa.mFormattedTags = allTagsInOne;
252            return aa;
253        }
254
255        /**
256         * Sets the attribute describing what is the intended use of the the audio signal,
257         * such as alarm or ringtone.
258         * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN},
259         *     {@link AudioAttributes#USAGE_MEDIA},
260         *     {@link AudioAttributes#USAGE_VOICE_COMMUNICATION},
261         *     {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING},
262         *     {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION},
263         *     {@link AudioAttributes#USAGE_NOTIFICATION_TELEPHONY_RINGTONE},
264         *     {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST},
265         *     {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT},
266         *     {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED},
267         *     {@link AudioAttributes#USAGE_NOTIFICATION_EVENT},
268         *     {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY},
269         *     {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE},
270         *     {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION},
271         *     {@link AudioAttributes#USAGE_GAME}.
272         * @return the same Builder instance.
273         */
274        public Builder setUsage(@AttributeUsage int usage) {
275            switch (usage) {
276                case USAGE_UNKNOWN:
277                case USAGE_MEDIA:
278                case USAGE_VOICE_COMMUNICATION:
279                case USAGE_VOICE_COMMUNICATION_SIGNALLING:
280                case USAGE_ALARM:
281                case USAGE_NOTIFICATION:
282                case USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
283                case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
284                case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
285                case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
286                case USAGE_NOTIFICATION_EVENT:
287                case USAGE_ASSISTANCE_ACCESSIBILITY:
288                case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
289                case USAGE_ASSISTANCE_SONIFICATION:
290                case USAGE_GAME:
291                     mUsage = usage;
292                     break;
293                default:
294                     mUsage = USAGE_UNKNOWN;
295            }
296            return this;
297        }
298
299        /**
300         * Sets the attribute describing the content type of the audio signal, such as speech,
301         * or music.
302         * @param contentType the content type values, one of
303         *     {@link AudioAttributes#CONTENT_TYPE_MOVIE},
304         *     {@link AudioAttributes#CONTENT_TYPE_MUSIC},
305         *     {@link AudioAttributes#CONTENT_TYPE_SONIFICATION},
306         *     {@link AudioAttributes#CONTENT_TYPE_SPEECH},
307         *     {@link AudioAttributes#CONTENT_TYPE_UNKNOWN}.
308         * @return the same Builder instance.
309         */
310        public Builder setContentType(@AttributeContentType int contentType) {
311            switch (contentType) {
312                case CONTENT_TYPE_UNKNOWN:
313                case CONTENT_TYPE_MOVIE:
314                case CONTENT_TYPE_MUSIC:
315                case CONTENT_TYPE_SONIFICATION:
316                case CONTENT_TYPE_SPEECH:
317                     mContentType = contentType;
318                     break;
319                default:
320                     mUsage = CONTENT_TYPE_UNKNOWN;
321            }
322            return this;
323        }
324
325        /**
326         * Sets the combination of flags.
327         * @param flags the {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED} flag.
328         * @return the same Builder instance.
329         */
330        public Builder setFlags(int flags) {
331            flags &= (AudioAttributes.FLAG_AUDIBILITY_ENFORCED | AudioAttributes.FLAG_SCO
332                    | AudioAttributes.FLAG_SECURE);
333            mFlags |= flags;
334            return this;
335        }
336
337        /**
338         * @hide
339         * Add a custom tag stored as a string
340         * @param tag
341         * @return the same Builder instance.
342         */
343        public Builder addTag(String tag) {
344            mTags.add(tag);
345            return this;
346        }
347
348        /**
349         * Adds attributes inferred from the legacy stream types.
350         * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL},
351         *   {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},
352         *   {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM},
353         *    or {@link AudioManager#STREAM_NOTIFICATION}.
354         * @return the same Builder instance.
355         */
356        public Builder setLegacyStreamType(int streamType) {
357            return setInternalLegacyStreamType(streamType);
358        }
359
360        /**
361         * @hide
362         * For internal framework use only, enables building from hidden stream types.
363         * @param streamType
364         * @return the same Builder instance.
365         */
366        public Builder setInternalLegacyStreamType(int streamType) {
367            switch(streamType) {
368                case AudioSystem.STREAM_VOICE_CALL:
369                    mContentType = CONTENT_TYPE_SPEECH;
370                    break;
371                case AudioSystem.STREAM_SYSTEM_ENFORCED:
372                    mFlags |= FLAG_AUDIBILITY_ENFORCED;
373                    // intended fall through, attributes in common with STREAM_SYSTEM
374                case AudioSystem.STREAM_SYSTEM:
375                    mContentType = CONTENT_TYPE_SONIFICATION;
376                    break;
377                case AudioSystem.STREAM_RING:
378                    mContentType = CONTENT_TYPE_SONIFICATION;
379                    break;
380                case AudioSystem.STREAM_MUSIC:
381                    mContentType = CONTENT_TYPE_MUSIC;
382                    break;
383                case AudioSystem.STREAM_ALARM:
384                    mContentType = CONTENT_TYPE_SONIFICATION;
385                    break;
386                case AudioSystem.STREAM_NOTIFICATION:
387                    mContentType = CONTENT_TYPE_SONIFICATION;
388                    break;
389                case AudioSystem.STREAM_BLUETOOTH_SCO:
390                    mContentType = CONTENT_TYPE_SPEECH;
391                    mFlags |= FLAG_SCO;
392                    break;
393                case AudioSystem.STREAM_DTMF:
394                    mContentType = CONTENT_TYPE_SONIFICATION;
395                    break;
396                case AudioSystem.STREAM_TTS:
397                    mContentType = CONTENT_TYPE_SPEECH;
398                    break;
399                default:
400                    Log.e(TAG, "Invalid stream type " + streamType + " in for AudioAttributes");
401            }
402            mUsage = usageForLegacyStreamType(streamType);
403            return this;
404        }
405    };
406
407    @Override
408    public int describeContents() {
409        return 0;
410    }
411
412    /**
413     * @hide
414     * Used to indicate that when parcelling, the tags should be parcelled through the flattened
415     * formatted string, not through the array of strings.
416     * Keep in sync with frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
417     * see definition of kAudioAttributesMarshallTagFlattenTags
418     */
419    public final static int FLATTEN_TAGS = 0x1;
420    /**
421     * When adding tags for writeToParcel(Parcel, int), add them in the list of flags (| NEW_FLAG)
422     */
423    private final static int ALL_PARCEL_FLAGS = FLATTEN_TAGS;
424    @Override
425    public void writeToParcel(Parcel dest, int flags) {
426        dest.writeInt(mUsage);
427        dest.writeInt(mContentType);
428        dest.writeInt(mFlags);
429        dest.writeInt(flags & ALL_PARCEL_FLAGS);
430        if ((flags & FLATTEN_TAGS) == 0) {
431            String[] tagsArray = new String[mTags.size()];
432            mTags.toArray(tagsArray);
433            dest.writeStringArray(tagsArray);
434        } else if ((flags & FLATTEN_TAGS) == FLATTEN_TAGS) {
435            dest.writeString(mFormattedTags);
436        }
437    }
438
439    private AudioAttributes(Parcel in) {
440        mUsage = in.readInt();
441        mContentType = in.readInt();
442        mFlags = in.readInt();
443        boolean hasFlattenedTags = ((in.readInt() & FLATTEN_TAGS) == FLATTEN_TAGS);
444        mTags = new HashSet<String>();
445        if (hasFlattenedTags) {
446            mTags.add(in.readString());
447        } else {
448            String[] tagsArray = in.readStringArray();
449            for (int i = tagsArray.length - 1 ; i >= 0 ; i--) {
450                mTags.add(tagsArray[i]);
451            }
452        }
453    }
454
455    /** @hide */
456    public static final Parcelable.Creator<AudioAttributes> CREATOR
457            = new Parcelable.Creator<AudioAttributes>() {
458        /**
459         * Rebuilds an AudioAttributes previously stored with writeToParcel().
460         * @param p Parcel object to read the AudioAttributes from
461         * @return a new AudioAttributes created from the data in the parcel
462         */
463        public AudioAttributes createFromParcel(Parcel p) {
464            return new AudioAttributes(p);
465        }
466        public AudioAttributes[] newArray(int size) {
467            return new AudioAttributes[size];
468        }
469    };
470
471    /** @hide */
472    @Override
473    public String toString () {
474        return new String("AudioAttributes:"
475                + " usage=" + mUsage
476                + " content=" + mContentType
477                + " flags=0x" + Integer.toHexString(mFlags).toUpperCase()
478                + " tags=" + mTags);
479    }
480
481    /** @hide */
482    public String usageToString() {
483        return usageToString(mUsage);
484    }
485
486    /** @hide */
487    public static String usageToString(int usage) {
488        switch(usage) {
489            case USAGE_UNKNOWN:
490                return new String("USAGE_UNKNOWN");
491            case USAGE_MEDIA:
492                return new String("USAGE_MEDIA");
493            case USAGE_VOICE_COMMUNICATION:
494                return new String("USAGE_VOICE_COMMUNICATION");
495            case USAGE_VOICE_COMMUNICATION_SIGNALLING:
496                return new String("USAGE_VOICE_COMMUNICATION");
497            case USAGE_ALARM:
498                return new String("USAGE_ALARM");
499            case USAGE_NOTIFICATION:
500                return new String("USAGE_NOTIFICATION");
501            case USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
502                return new String("USAGE_NOTIFICATION");
503            case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
504                return new String("USAGE_NOTIFICATION");
505            case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
506                return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT");
507            case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
508                return new String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED");
509            case USAGE_NOTIFICATION_EVENT:
510                return new String("USAGE_NOTIFICATION_EVENT");
511            case USAGE_ASSISTANCE_ACCESSIBILITY:
512                return new String("USAGE_ASSISTANCE_ACCESSIBILITY");
513            case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
514                return new String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE");
515            case USAGE_ASSISTANCE_SONIFICATION:
516                return new String("USAGE_ASSISTANCE_SONIFICATION");
517            case USAGE_GAME:
518                return new String("USAGE_GAME");
519            default:
520                return new String("unknown usage " + usage);
521        }
522    }
523
524    /** @hide */
525    public static int usageForLegacyStreamType(int streamType) {
526        switch(streamType) {
527            case AudioSystem.STREAM_VOICE_CALL:
528                return USAGE_VOICE_COMMUNICATION;
529            case AudioSystem.STREAM_SYSTEM_ENFORCED:
530            case AudioSystem.STREAM_SYSTEM:
531                return USAGE_ASSISTANCE_SONIFICATION;
532            case AudioSystem.STREAM_RING:
533                return USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
534            case AudioSystem.STREAM_MUSIC:
535                return USAGE_MEDIA;
536            case AudioSystem.STREAM_ALARM:
537                return USAGE_ALARM;
538            case AudioSystem.STREAM_NOTIFICATION:
539                return USAGE_NOTIFICATION;
540            case AudioSystem.STREAM_BLUETOOTH_SCO:
541                return USAGE_VOICE_COMMUNICATION;
542            case AudioSystem.STREAM_DTMF:
543                return USAGE_VOICE_COMMUNICATION_SIGNALLING;
544            case AudioSystem.STREAM_TTS:
545                return USAGE_ASSISTANCE_ACCESSIBILITY;
546            default:
547                return USAGE_UNKNOWN;
548        }
549    }
550
551    /** @hide */
552    public static int toLegacyStreamType(AudioAttributes aa) {
553        // flags to stream type mapping
554        if ((aa.getFlags() & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) {
555            return AudioSystem.STREAM_SYSTEM_ENFORCED;
556        }
557        if ((aa.getFlags() & FLAG_SCO) == FLAG_SCO) {
558            return AudioSystem.STREAM_BLUETOOTH_SCO;
559        }
560
561        // usage to stream type mapping
562        switch (aa.getUsage()) {
563            case USAGE_MEDIA:
564            case USAGE_GAME:
565            case USAGE_ASSISTANCE_ACCESSIBILITY:
566            case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
567                return AudioSystem.STREAM_MUSIC;
568            case USAGE_ASSISTANCE_SONIFICATION:
569                return AudioSystem.STREAM_SYSTEM;
570            case USAGE_VOICE_COMMUNICATION:
571                return AudioSystem.STREAM_VOICE_CALL;
572            case USAGE_VOICE_COMMUNICATION_SIGNALLING:
573                return AudioSystem.STREAM_DTMF;
574            case USAGE_ALARM:
575                return AudioSystem.STREAM_ALARM;
576            case USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
577                return AudioSystem.STREAM_RING;
578            case USAGE_NOTIFICATION:
579            case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
580            case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
581            case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
582            case USAGE_NOTIFICATION_EVENT:
583                return AudioSystem.STREAM_NOTIFICATION;
584            case USAGE_UNKNOWN:
585            default:
586                return AudioSystem.STREAM_MUSIC;
587        }
588    }
589
590    /** @hide */
591    @IntDef({
592        USAGE_UNKNOWN,
593        USAGE_MEDIA,
594        USAGE_VOICE_COMMUNICATION,
595        USAGE_VOICE_COMMUNICATION_SIGNALLING,
596        USAGE_ALARM,
597        USAGE_NOTIFICATION,
598        USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
599        USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
600        USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
601        USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
602        USAGE_NOTIFICATION_EVENT,
603        USAGE_ASSISTANCE_ACCESSIBILITY,
604        USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
605        USAGE_ASSISTANCE_SONIFICATION,
606        USAGE_GAME
607    })
608    @Retention(RetentionPolicy.SOURCE)
609    public @interface AttributeUsage {}
610
611    /** @hide */
612    @IntDef({
613        CONTENT_TYPE_UNKNOWN,
614        CONTENT_TYPE_SPEECH,
615        CONTENT_TYPE_MUSIC,
616        CONTENT_TYPE_MOVIE,
617        CONTENT_TYPE_SONIFICATION
618    })
619    @Retention(RetentionPolicy.SOURCE)
620    public @interface AttributeContentType {}
621}
622