• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media;
18 
19 import android.app.PendingIntent;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.ComponentName;
22 import android.content.Intent;
23 import android.graphics.Bitmap;
24 import android.media.session.MediaSession;
25 import android.media.session.MediaSessionLegacyHelper;
26 import android.media.session.PlaybackState;
27 import android.os.Build;
28 import android.os.Bundle;
29 import android.os.Looper;
30 import android.os.SystemClock;
31 import android.util.Log;
32 
33 /**
34  * RemoteControlClient enables exposing information meant to be consumed by remote controls
35  * capable of displaying metadata, artwork and media transport control buttons.
36  *
37  * <p>A remote control client object is associated with a media button event receiver. This
38  * event receiver must have been previously registered with
39  * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the
40  * RemoteControlClient can be registered through
41  * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
42  *
43  * <p>Here is an example of creating a RemoteControlClient instance after registering a media
44  * button event receiver:
45  * <pre>ComponentName myEventReceiver = new ComponentName(getPackageName(), MyRemoteControlEventReceiver.class.getName());
46  * AudioManager myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
47  * myAudioManager.registerMediaButtonEventReceiver(myEventReceiver);
48  * // build the PendingIntent for the remote control client
49  * Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
50  * mediaButtonIntent.setComponent(myEventReceiver);
51  * PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
52  * // create and register the remote control client
53  * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
54  * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre>
55  *
56  * @deprecated Use {@link MediaSession} instead.
57  */
58 @Deprecated public class RemoteControlClient
59 {
60     private final static String TAG = "RemoteControlClient";
61     private final static boolean DEBUG = false;
62 
63     /**
64      * Playback state of a RemoteControlClient which is stopped.
65      *
66      * @see #setPlaybackState(int)
67      */
68     public final static int PLAYSTATE_STOPPED            = 1;
69     /**
70      * Playback state of a RemoteControlClient which is paused.
71      *
72      * @see #setPlaybackState(int)
73      */
74     public final static int PLAYSTATE_PAUSED             = 2;
75     /**
76      * Playback state of a RemoteControlClient which is playing media.
77      *
78      * @see #setPlaybackState(int)
79      */
80     public final static int PLAYSTATE_PLAYING            = 3;
81     /**
82      * Playback state of a RemoteControlClient which is fast forwarding in the media
83      *    it is currently playing.
84      *
85      * @see #setPlaybackState(int)
86      */
87     public final static int PLAYSTATE_FAST_FORWARDING    = 4;
88     /**
89      * Playback state of a RemoteControlClient which is fast rewinding in the media
90      *    it is currently playing.
91      *
92      * @see #setPlaybackState(int)
93      */
94     public final static int PLAYSTATE_REWINDING          = 5;
95     /**
96      * Playback state of a RemoteControlClient which is skipping to the next
97      *    logical chapter (such as a song in a playlist) in the media it is currently playing.
98      *
99      * @see #setPlaybackState(int)
100      */
101     public final static int PLAYSTATE_SKIPPING_FORWARDS  = 6;
102     /**
103      * Playback state of a RemoteControlClient which is skipping back to the previous
104      *    logical chapter (such as a song in a playlist) in the media it is currently playing.
105      *
106      * @see #setPlaybackState(int)
107      */
108     public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7;
109     /**
110      * Playback state of a RemoteControlClient which is buffering data to play before it can
111      *    start or resume playback.
112      *
113      * @see #setPlaybackState(int)
114      */
115     public final static int PLAYSTATE_BUFFERING          = 8;
116     /**
117      * Playback state of a RemoteControlClient which cannot perform any playback related
118      *    operation because of an internal error. Examples of such situations are no network
119      *    connectivity when attempting to stream data from a server, or expired user credentials
120      *    when trying to play subscription-based content.
121      *
122      * @see #setPlaybackState(int)
123      */
124     public final static int PLAYSTATE_ERROR              = 9;
125     /**
126      * @hide
127      * The value of a playback state when none has been declared.
128      * Intentionally hidden as an application shouldn't set such a playback state value.
129      */
130     public final static int PLAYSTATE_NONE               = 0;
131 
132     /**
133      * @hide
134      * The default playback type, "local", indicating the presentation of the media is happening on
135      * the same device (e.g. a phone, a tablet) as where it is controlled from.
136      */
137     public final static int PLAYBACK_TYPE_LOCAL = 0;
138     /**
139      * @hide
140      * A playback type indicating the presentation of the media is happening on
141      * a different device (i.e. the remote device) than where it is controlled from.
142      */
143     public final static int PLAYBACK_TYPE_REMOTE = 1;
144     private final static int PLAYBACK_TYPE_MIN = PLAYBACK_TYPE_LOCAL;
145     private final static int PLAYBACK_TYPE_MAX = PLAYBACK_TYPE_REMOTE;
146     /**
147      * @hide
148      * Playback information indicating the playback volume is fixed, i.e. it cannot be controlled
149      * from this object. An example of fixed playback volume is a remote player, playing over HDMI
150      * where the user prefer to control the volume on the HDMI sink, rather than attenuate at the
151      * source.
152      * @see #PLAYBACKINFO_VOLUME_HANDLING.
153      */
154     public final static int PLAYBACK_VOLUME_FIXED = 0;
155     /**
156      * @hide
157      * Playback information indicating the playback volume is variable and can be controlled from
158      * this object.
159      * @see #PLAYBACKINFO_VOLUME_HANDLING.
160      */
161     public final static int PLAYBACK_VOLUME_VARIABLE = 1;
162     /**
163      * @hide (to be un-hidden)
164      * The playback information value indicating the value of a given information type is invalid.
165      * @see #PLAYBACKINFO_VOLUME_HANDLING.
166      */
167     public final static int PLAYBACKINFO_INVALID_VALUE = Integer.MIN_VALUE;
168 
169     /**
170      * @hide
171      * An unknown or invalid playback position value.
172      */
173     public final static long PLAYBACK_POSITION_INVALID = -1;
174     /**
175      * @hide
176      * An invalid playback position value associated with the use of {@link #setPlaybackState(int)}
177      * used to indicate that playback position will remain unknown.
178      */
179     public final static long PLAYBACK_POSITION_ALWAYS_UNKNOWN = 0x8019771980198300L;
180     /**
181      * @hide
182      * The default playback speed, 1x.
183      */
184     public final static float PLAYBACK_SPEED_1X = 1.0f;
185 
186     //==========================================
187     // Public keys for playback information
188     /**
189      * @hide
190      * Playback information that defines the type of playback associated with this
191      * RemoteControlClient. See {@link #PLAYBACK_TYPE_LOCAL} and {@link #PLAYBACK_TYPE_REMOTE}.
192      */
193     public final static int PLAYBACKINFO_PLAYBACK_TYPE = 1;
194     /**
195      * @hide
196      * Playback information that defines at what volume the playback associated with this
197      * RemoteControlClient is performed. This information is only used when the playback type is not
198      * local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}).
199      */
200     public final static int PLAYBACKINFO_VOLUME = 2;
201     /**
202      * @hide
203      * Playback information that defines the maximum volume volume value that is supported
204      * by the playback associated with this RemoteControlClient. This information is only used
205      * when the playback type is not local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}).
206      */
207     public final static int PLAYBACKINFO_VOLUME_MAX = 3;
208     /**
209      * @hide
210      * Playback information that defines how volume is handled for the presentation of the media.
211      * @see #PLAYBACK_VOLUME_FIXED
212      * @see #PLAYBACK_VOLUME_VARIABLE
213      */
214     public final static int PLAYBACKINFO_VOLUME_HANDLING = 4;
215     /**
216      * @hide
217      * Playback information that defines over what stream type the media is presented.
218      */
219     public final static int PLAYBACKINFO_USES_STREAM = 5;
220 
221     //==========================================
222     // Public flags for the supported transport control capabilities
223     /**
224      * Flag indicating a RemoteControlClient makes use of the "previous" media key.
225      *
226      * @see #setTransportControlFlags(int)
227      * @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS
228      */
229     public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
230     /**
231      * Flag indicating a RemoteControlClient makes use of the "rewind" media key.
232      *
233      * @see #setTransportControlFlags(int)
234      * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND
235      */
236     public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
237     /**
238      * Flag indicating a RemoteControlClient makes use of the "play" media key.
239      *
240      * @see #setTransportControlFlags(int)
241      * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY
242      */
243     public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
244     /**
245      * Flag indicating a RemoteControlClient makes use of the "play/pause" media key.
246      *
247      * @see #setTransportControlFlags(int)
248      * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE
249      */
250     public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
251     /**
252      * Flag indicating a RemoteControlClient makes use of the "pause" media key.
253      *
254      * @see #setTransportControlFlags(int)
255      * @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE
256      */
257     public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
258     /**
259      * Flag indicating a RemoteControlClient makes use of the "stop" media key.
260      *
261      * @see #setTransportControlFlags(int)
262      * @see android.view.KeyEvent#KEYCODE_MEDIA_STOP
263      */
264     public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
265     /**
266      * Flag indicating a RemoteControlClient makes use of the "fast forward" media key.
267      *
268      * @see #setTransportControlFlags(int)
269      * @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD
270      */
271     public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
272     /**
273      * Flag indicating a RemoteControlClient makes use of the "next" media key.
274      *
275      * @see #setTransportControlFlags(int)
276      * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT
277      */
278     public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
279     /**
280      * Flag indicating a RemoteControlClient can receive changes in the media playback position
281      * through the {@link OnPlaybackPositionUpdateListener} interface. This flag must be set
282      * in order for components that display the RemoteControlClient information, to display and
283      * let the user control media playback position.
284      * @see #setTransportControlFlags(int)
285      * @see #setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener)
286      * @see #setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener)
287      */
288     public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8;
289     /**
290      * Flag indicating a RemoteControlClient supports ratings.
291      * This flag must be set in order for components that display the RemoteControlClient
292      * information, to display ratings information, and, if ratings are declared editable
293      * (by calling {@link MediaMetadataEditor#addEditableKey(int)} with the
294      * {@link MediaMetadataEditor#RATING_KEY_BY_USER} key), it will enable the user to rate
295      * the media, with values being received through the interface set with
296      * {@link #setMetadataUpdateListener(OnMetadataUpdateListener)}.
297      * @see #setTransportControlFlags(int)
298      */
299     public final static int FLAG_KEY_MEDIA_RATING = 1 << 9;
300 
301     /**
302      * @hide
303      * The flags for when no media keys are declared supported.
304      * Intentionally hidden as an application shouldn't set the transport control flags
305      *     to this value.
306      */
307     public final static int FLAGS_KEY_MEDIA_NONE = 0;
308 
309     /**
310      * @hide
311      * Flag used to signal some type of metadata exposed by the RemoteControlClient is requested.
312      */
313     public final static int FLAG_INFORMATION_REQUEST_METADATA = 1 << 0;
314     /**
315      * @hide
316      * Flag used to signal that the transport control buttons supported by the
317      *     RemoteControlClient are requested.
318      * This can for instance happen when playback is at the end of a playlist, and the "next"
319      * operation is not supported anymore.
320      */
321     public final static int FLAG_INFORMATION_REQUEST_KEY_MEDIA = 1 << 1;
322     /**
323      * @hide
324      * Flag used to signal that the playback state of the RemoteControlClient is requested.
325      */
326     public final static int FLAG_INFORMATION_REQUEST_PLAYSTATE = 1 << 2;
327     /**
328      * @hide
329      * Flag used to signal that the album art for the RemoteControlClient is requested.
330      */
331     public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3;
332 
333     private MediaSession mSession;
334 
335     /**
336      * Class constructor.
337      * @param mediaButtonIntent The intent that will be sent for the media button events sent
338      *     by remote controls.
339      *     This intent needs to have been constructed with the {@link Intent#ACTION_MEDIA_BUTTON}
340      *     action, and have a component that will handle the intent (set with
341      *     {@link Intent#setComponent(ComponentName)}) registered with
342      *     {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)}
343      *     before this new RemoteControlClient can itself be registered with
344      *     {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
345      * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
346      * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
347      */
RemoteControlClient(PendingIntent mediaButtonIntent)348     public RemoteControlClient(PendingIntent mediaButtonIntent) {
349         mRcMediaIntent = mediaButtonIntent;
350     }
351 
352     /**
353      * Class constructor for a remote control client whose internal event handling
354      * happens on a user-provided Looper.
355      * @param mediaButtonIntent The intent that will be sent for the media button events sent
356      *     by remote controls.
357      *     This intent needs to have been constructed with the {@link Intent#ACTION_MEDIA_BUTTON}
358      *     action, and have a component that will handle the intent (set with
359      *     {@link Intent#setComponent(ComponentName)}) registered with
360      *     {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)}
361      *     before this new RemoteControlClient can itself be registered with
362      *     {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
363      * @param looper The Looper running the event loop.
364      * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
365      * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
366      */
RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper)367     public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) {
368         mRcMediaIntent = mediaButtonIntent;
369     }
370 
371     /**
372      * @hide
373      */
registerWithSession(MediaSessionLegacyHelper helper)374     public void registerWithSession(MediaSessionLegacyHelper helper) {
375         helper.addRccListener(mRcMediaIntent, mTransportListener);
376         mSession = helper.getSession(mRcMediaIntent);
377         setTransportControlFlags(mTransportControlFlags);
378     }
379 
380     /**
381      * @hide
382      */
unregisterWithSession(MediaSessionLegacyHelper helper)383     public void unregisterWithSession(MediaSessionLegacyHelper helper) {
384         helper.removeRccListener(mRcMediaIntent);
385         mSession = null;
386     }
387 
388     /**
389      * Get a {@link MediaSession} associated with this RCC. It will only have a
390      * session while it is registered with
391      * {@link AudioManager#registerRemoteControlClient}. The session returned
392      * should not be modified directly by the application but may be used with
393      * other APIs that require a session.
394      *
395      * @return A media session object or null.
396      */
getMediaSession()397     public MediaSession getMediaSession() {
398         return mSession;
399     }
400 
401     /**
402      * Class used to modify metadata in a {@link RemoteControlClient} object.
403      * Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor,
404      * on which you set the metadata for the RemoteControlClient instance. Once all the information
405      * has been set, use {@link #apply()} to make it the new metadata that should be displayed
406      * for the associated client. Once the metadata has been "applied", you cannot reuse this
407      * instance of the MetadataEditor.
408      *
409      * @deprecated Use {@link MediaMetadata} and {@link MediaSession} instead.
410      */
411     @Deprecated public class MetadataEditor extends MediaMetadataEditor {
412 
413         // only use RemoteControlClient.editMetadata() to get a MetadataEditor instance
MetadataEditor()414         private MetadataEditor() { }
415         /**
416          * @hide
417          */
clone()418         public Object clone() throws CloneNotSupportedException {
419             throw new CloneNotSupportedException();
420         }
421 
422         /**
423          * The metadata key for the content artwork / album art.
424          */
425         public final static int BITMAP_KEY_ARTWORK = 100;
426 
427         /**
428          * @hide
429          * TODO(jmtrivi) have lockscreen move to the new key name and remove
430          */
431         public final static int METADATA_KEY_ARTWORK = BITMAP_KEY_ARTWORK;
432 
433         /**
434          * Adds textual information to be displayed.
435          * Note that none of the information added after {@link #apply()} has been called,
436          * will be displayed.
437          * @param key The identifier of a the metadata field to set. Valid values are
438          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
439          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
440          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
441          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
442          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
443          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
444          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
445          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
446          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
447          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
448          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}.
449          * @param value The text for the given key, or {@code null} to signify there is no valid
450          *      information for the field.
451          * @return Returns a reference to the same MetadataEditor object, so you can chain put
452          *      calls together.
453          */
putString(int key, String value)454         public synchronized MetadataEditor putString(int key, String value)
455                 throws IllegalArgumentException {
456             super.putString(key, value);
457             if (mMetadataBuilder != null) {
458                 // MediaMetadata supports all the same fields as MetadataEditor
459                 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
460                 // But just in case, don't add things we don't understand
461                 if (metadataKey != null) {
462                     mMetadataBuilder.putText(metadataKey, value);
463                 }
464             }
465 
466             return this;
467         }
468 
469         /**
470          * Adds numerical information to be displayed.
471          * Note that none of the information added after {@link #apply()} has been called,
472          * will be displayed.
473          * @param key the identifier of a the metadata field to set. Valid values are
474          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
475          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
476          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value
477          *      expressed in milliseconds),
478          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
479          * @param value The long value for the given key
480          * @return Returns a reference to the same MetadataEditor object, so you can chain put
481          *      calls together.
482          * @throws IllegalArgumentException
483          */
putLong(int key, long value)484         public synchronized MetadataEditor putLong(int key, long value)
485                 throws IllegalArgumentException {
486             super.putLong(key, value);
487             if (mMetadataBuilder != null) {
488                 // MediaMetadata supports all the same fields as MetadataEditor
489                 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
490                 // But just in case, don't add things we don't understand
491                 if (metadataKey != null) {
492                     mMetadataBuilder.putLong(metadataKey, value);
493                 }
494             }
495             return this;
496         }
497 
498         /**
499          * Sets the album / artwork picture to be displayed on the remote control.
500          * @param key the identifier of the bitmap to set. The only valid value is
501          *      {@link #BITMAP_KEY_ARTWORK}
502          * @param bitmap The bitmap for the artwork, or null if there isn't any.
503          * @return Returns a reference to the same MetadataEditor object, so you can chain put
504          *      calls together.
505          * @throws IllegalArgumentException
506          * @see android.graphics.Bitmap
507          */
508         @Override
putBitmap(int key, Bitmap bitmap)509         public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap)
510                 throws IllegalArgumentException {
511             super.putBitmap(key, bitmap);
512             if (mMetadataBuilder != null) {
513                 // MediaMetadata supports all the same fields as MetadataEditor
514                 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
515                 // But just in case, don't add things we don't understand
516                 if (metadataKey != null) {
517                     mMetadataBuilder.putBitmap(metadataKey, bitmap);
518                 }
519             }
520             return this;
521         }
522 
523         @Override
putObject(int key, Object object)524         public synchronized MetadataEditor putObject(int key, Object object)
525                 throws IllegalArgumentException {
526             super.putObject(key, object);
527             if (mMetadataBuilder != null &&
528                     (key == MediaMetadataEditor.RATING_KEY_BY_USER ||
529                     key == MediaMetadataEditor.RATING_KEY_BY_OTHERS)) {
530                 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
531                 if (metadataKey != null) {
532                     mMetadataBuilder.putRating(metadataKey, (Rating) object);
533                 }
534             }
535             return this;
536         }
537 
538         /**
539          * Clears all the metadata that has been set since the MetadataEditor instance was created
540          * (with {@link RemoteControlClient#editMetadata(boolean)}).
541          * Note that clearing the metadata doesn't reset the editable keys
542          * (use {@link MediaMetadataEditor#removeEditableKeys()} instead).
543          */
544         @Override
clear()545         public synchronized void clear() {
546             super.clear();
547         }
548 
549         /**
550          * Associates all the metadata that has been set since the MetadataEditor instance was
551          *     created with {@link RemoteControlClient#editMetadata(boolean)}, or since
552          *     {@link #clear()} was called, with the RemoteControlClient. Once "applied",
553          *     this MetadataEditor cannot be reused to edit the RemoteControlClient's metadata.
554          */
apply()555         public synchronized void apply() {
556             if (mApplied) {
557                 Log.e(TAG, "Can't apply a previously applied MetadataEditor");
558                 return;
559             }
560             synchronized (mCacheLock) {
561                 // Still build the old metadata so when creating a new editor
562                 // you get the expected values.
563                 // assign the edited data
564                 mMetadata = new Bundle(mEditorMetadata);
565                 // add the information about editable keys
566                 mMetadata.putLong(String.valueOf(KEY_EDITABLE_MASK), mEditableKeys);
567                 if ((mOriginalArtwork != null) && (!mOriginalArtwork.equals(mEditorArtwork))) {
568                     mOriginalArtwork.recycle();
569                 }
570                 mOriginalArtwork = mEditorArtwork;
571                 mEditorArtwork = null;
572 
573                 // USE_SESSIONS
574                 if (mSession != null && mMetadataBuilder != null) {
575                     mMediaMetadata = mMetadataBuilder.build();
576                     mSession.setMetadata(mMediaMetadata);
577                 }
578                 mApplied = true;
579             }
580         }
581     }
582 
583     /**
584      * Creates a {@link MetadataEditor}.
585      * @param startEmpty Set to false if you want the MetadataEditor to contain the metadata that
586      *     was previously applied to the RemoteControlClient, or true if it is to be created empty.
587      * @return a new MetadataEditor instance.
588      */
editMetadata(boolean startEmpty)589     public MetadataEditor editMetadata(boolean startEmpty) {
590         MetadataEditor editor = new MetadataEditor();
591         if (startEmpty) {
592             editor.mEditorMetadata = new Bundle();
593             editor.mEditorArtwork = null;
594             editor.mMetadataChanged = true;
595             editor.mArtworkChanged = true;
596             editor.mEditableKeys = 0;
597         } else {
598             editor.mEditorMetadata = new Bundle(mMetadata);
599             editor.mEditorArtwork = mOriginalArtwork;
600             editor.mMetadataChanged = false;
601             editor.mArtworkChanged = false;
602         }
603         // USE_SESSIONS
604         if (startEmpty || mMediaMetadata == null) {
605             editor.mMetadataBuilder = new MediaMetadata.Builder();
606         } else {
607             editor.mMetadataBuilder = new MediaMetadata.Builder(mMediaMetadata);
608         }
609         return editor;
610     }
611 
612     /**
613      * Sets the current playback state.
614      * @param state The current playback state, one of the following values:
615      *       {@link #PLAYSTATE_STOPPED},
616      *       {@link #PLAYSTATE_PAUSED},
617      *       {@link #PLAYSTATE_PLAYING},
618      *       {@link #PLAYSTATE_FAST_FORWARDING},
619      *       {@link #PLAYSTATE_REWINDING},
620      *       {@link #PLAYSTATE_SKIPPING_FORWARDS},
621      *       {@link #PLAYSTATE_SKIPPING_BACKWARDS},
622      *       {@link #PLAYSTATE_BUFFERING},
623      *       {@link #PLAYSTATE_ERROR}.
624      */
setPlaybackState(int state)625     public void setPlaybackState(int state) {
626         setPlaybackStateInt(state, PLAYBACK_POSITION_ALWAYS_UNKNOWN, PLAYBACK_SPEED_1X,
627                 false /* legacy API, converting to method with position and speed */);
628     }
629 
630     /**
631      * Sets the current playback state and the matching media position for the current playback
632      *   speed.
633      * @param state The current playback state, one of the following values:
634      *       {@link #PLAYSTATE_STOPPED},
635      *       {@link #PLAYSTATE_PAUSED},
636      *       {@link #PLAYSTATE_PLAYING},
637      *       {@link #PLAYSTATE_FAST_FORWARDING},
638      *       {@link #PLAYSTATE_REWINDING},
639      *       {@link #PLAYSTATE_SKIPPING_FORWARDS},
640      *       {@link #PLAYSTATE_SKIPPING_BACKWARDS},
641      *       {@link #PLAYSTATE_BUFFERING},
642      *       {@link #PLAYSTATE_ERROR}.
643      * @param timeInMs a 0 or positive value for the current media position expressed in ms
644      *    (same unit as for when sending the media duration, if applicable, with
645      *    {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} in the
646      *    {@link RemoteControlClient.MetadataEditor}). Negative values imply that position is not
647      *    known (e.g. listening to a live stream of a radio) or not applicable (e.g. when state
648      *    is {@link #PLAYSTATE_BUFFERING} and nothing had played yet).
649      * @param playbackSpeed a value expressed as a ratio of 1x playback: 1.0f is normal playback,
650      *    2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
651      *    playing (e.g. when state is {@link #PLAYSTATE_ERROR}).
652      */
setPlaybackState(int state, long timeInMs, float playbackSpeed)653     public void setPlaybackState(int state, long timeInMs, float playbackSpeed) {
654         setPlaybackStateInt(state, timeInMs, playbackSpeed, true);
655     }
656 
setPlaybackStateInt(int state, long timeInMs, float playbackSpeed, boolean hasPosition)657     private void setPlaybackStateInt(int state, long timeInMs, float playbackSpeed,
658             boolean hasPosition) {
659         synchronized(mCacheLock) {
660             if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs)
661                     || (mPlaybackSpeed != playbackSpeed)) {
662                 // store locally
663                 mPlaybackState = state;
664                 // distinguish between an application not knowing the current playback position
665                 // at the moment and an application using the API where only the playback state
666                 // is passed, not the playback position.
667                 if (hasPosition) {
668                     if (timeInMs < 0) {
669                         mPlaybackPositionMs = PLAYBACK_POSITION_INVALID;
670                     } else {
671                         mPlaybackPositionMs = timeInMs;
672                     }
673                 } else {
674                     mPlaybackPositionMs = PLAYBACK_POSITION_ALWAYS_UNKNOWN;
675                 }
676                 mPlaybackSpeed = playbackSpeed;
677                 // keep track of when the state change occurred
678                 mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime();
679 
680                 // USE_SESSIONS
681                 if (mSession != null) {
682                     int pbState = getStateFromRccState(state);
683                     long position = hasPosition ? mPlaybackPositionMs
684                             : PlaybackState.PLAYBACK_POSITION_UNKNOWN;
685 
686                     PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
687                     bob.setState(pbState, position, playbackSpeed, SystemClock.elapsedRealtime());
688                     bob.setErrorMessage(null);
689                     mSessionPlaybackState = bob.build();
690                     mSession.setPlaybackState(mSessionPlaybackState);
691                 }
692             }
693         }
694     }
695 
696     /**
697      * Sets the flags for the media transport control buttons that this client supports.
698      * @param transportControlFlags A combination of the following flags:
699      *      {@link #FLAG_KEY_MEDIA_PREVIOUS},
700      *      {@link #FLAG_KEY_MEDIA_REWIND},
701      *      {@link #FLAG_KEY_MEDIA_PLAY},
702      *      {@link #FLAG_KEY_MEDIA_PLAY_PAUSE},
703      *      {@link #FLAG_KEY_MEDIA_PAUSE},
704      *      {@link #FLAG_KEY_MEDIA_STOP},
705      *      {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
706      *      {@link #FLAG_KEY_MEDIA_NEXT},
707      *      {@link #FLAG_KEY_MEDIA_POSITION_UPDATE},
708      *      {@link #FLAG_KEY_MEDIA_RATING}.
709      */
setTransportControlFlags(int transportControlFlags)710     public void setTransportControlFlags(int transportControlFlags) {
711         synchronized(mCacheLock) {
712             // store locally
713             mTransportControlFlags = transportControlFlags;
714 
715             // USE_SESSIONS
716             if (mSession != null) {
717                 PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
718                 bob.setActions(getActionsFromRccControlFlags(transportControlFlags));
719                 mSessionPlaybackState = bob.build();
720                 mSession.setPlaybackState(mSessionPlaybackState);
721             }
722         }
723     }
724 
725     /**
726      * Interface definition for a callback to be invoked when one of the metadata values has
727      * been updated.
728      * Implement this interface to receive metadata updates after registering your listener
729      * through {@link RemoteControlClient#setMetadataUpdateListener(OnMetadataUpdateListener)}.
730      */
731     public interface OnMetadataUpdateListener {
732         /**
733          * Called on the implementer to notify that the metadata field for the given key has
734          * been updated to the new value.
735          * @param key the identifier of the updated metadata field.
736          * @param newValue the Object storing the new value for the key.
737          */
onMetadataUpdate(int key, Object newValue)738         public abstract void onMetadataUpdate(int key, Object newValue);
739     }
740 
741     /**
742      * Sets the listener to be called whenever the metadata is updated.
743      * New metadata values will be received in the same thread as the one in which
744      * RemoteControlClient was created.
745      * @param l the metadata update listener
746      */
setMetadataUpdateListener(OnMetadataUpdateListener l)747     public void setMetadataUpdateListener(OnMetadataUpdateListener l) {
748         synchronized(mCacheLock) {
749             mMetadataUpdateListener = l;
750         }
751     }
752 
753 
754     /**
755      * Interface definition for a callback to be invoked when the media playback position is
756      * requested to be updated.
757      * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE
758      */
759     public interface OnPlaybackPositionUpdateListener {
760         /**
761          * Called on the implementer to notify it that the playback head should be set at the given
762          * position. If the position can be changed from its current value, the implementor of
763          * the interface must also update the playback position using
764          * {@link #setPlaybackState(int, long, float)} to reflect the actual new
765          * position being used, regardless of whether it differs from the requested position.
766          * Failure to do so would cause the system to not know the new actual playback position,
767          * and user interface components would fail to show the user where playback resumed after
768          * the position was updated.
769          * @param newPositionMs the new requested position in the current media, expressed in ms.
770          */
onPlaybackPositionUpdate(long newPositionMs)771         void onPlaybackPositionUpdate(long newPositionMs);
772     }
773 
774     /**
775      * Interface definition for a callback to be invoked when the media playback position is
776      * queried.
777      * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE
778      */
779     public interface OnGetPlaybackPositionListener {
780         /**
781          * Called on the implementer of the interface to query the current playback position.
782          * @return a negative value if the current playback position (or the last valid playback
783          *     position) is not known, or a zero or positive value expressed in ms indicating the
784          *     current position, or the last valid known position.
785          */
onGetPlaybackPosition()786         long onGetPlaybackPosition();
787     }
788 
789     /**
790      * Sets the listener to be called whenever the media playback position is requested
791      * to be updated.
792      * Notifications will be received in the same thread as the one in which RemoteControlClient
793      * was created.
794      * @param l the position update listener to be called
795      */
setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l)796     public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
797         synchronized(mCacheLock) {
798             mPositionUpdateListener = l;
799         }
800     }
801 
802     /**
803      * Sets the listener to be called whenever the media current playback position is needed.
804      * Queries will be received in the same thread as the one in which RemoteControlClient
805      * was created.
806      * @param l the listener to be called to retrieve the playback position
807      */
setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l)808     public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) {
809         synchronized(mCacheLock) {
810             mPositionProvider = l;
811         }
812     }
813 
814     /**
815      * @hide
816      * Flag to reflect that the application controlling this RemoteControlClient sends playback
817      * position updates. The playback position being "readable" is considered from the application's
818      * point of view.
819      */
820     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
821     public static int MEDIA_POSITION_READABLE = 1 << 0;
822     /**
823      * @hide
824      * Flag to reflect that the application controlling this RemoteControlClient can receive
825      * playback position updates. The playback position being "writable"
826      * is considered from the application's point of view.
827      */
828     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
829     public static int MEDIA_POSITION_WRITABLE = 1 << 1;
830 
831     /** @hide */
832     public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE;
833     /** @hide */
834     // hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC]
835     public final static int DEFAULT_PLAYBACK_VOLUME = 15;
836 
837     /**
838      * Lock for all cached data
839      */
840     private final Object mCacheLock = new Object();
841     /**
842      * Cache for the playback state.
843      * Access synchronized on mCacheLock
844      */
845     private int mPlaybackState = PLAYSTATE_NONE;
846     /**
847      * Time of last play state change
848      * Access synchronized on mCacheLock
849      */
850     private long mPlaybackStateChangeTimeMs = 0;
851     /**
852      * Last playback position in ms reported by the user
853      */
854     private long mPlaybackPositionMs = PLAYBACK_POSITION_INVALID;
855     /**
856      * Last playback speed reported by the user
857      */
858     private float mPlaybackSpeed = PLAYBACK_SPEED_1X;
859     /**
860      * Cache for the artwork bitmap.
861      * Access synchronized on mCacheLock
862      * Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be
863      * accessed to be resized, in which case a copy will be made. This would add overhead in
864      * Bundle operations.
865      */
866     private Bitmap mOriginalArtwork;
867     /**
868      * Cache for the transport control mask.
869      * Access synchronized on mCacheLock
870      */
871     private int mTransportControlFlags = FLAGS_KEY_MEDIA_NONE;
872     /**
873      * Cache for the metadata strings.
874      * Access synchronized on mCacheLock
875      * This is re-initialized in apply() and so cannot be final.
876      */
877     private Bundle mMetadata = new Bundle();
878     /**
879      * Listener registered by user of RemoteControlClient to receive requests for playback position
880      * update requests.
881      */
882     private OnPlaybackPositionUpdateListener mPositionUpdateListener;
883     /**
884      * Provider registered by user of RemoteControlClient to provide the current playback position.
885      */
886     private OnGetPlaybackPositionListener mPositionProvider;
887     /**
888      * Listener registered by user of RemoteControlClient to receive edit changes to metadata
889      * it exposes.
890      */
891     private OnMetadataUpdateListener mMetadataUpdateListener;
892     /**
893      * The current remote control client generation ID across the system, as known by this object
894      */
895     private int mCurrentClientGenId = -1;
896 
897     /**
898      * The media button intent description associated with this remote control client
899      * (can / should include target component for intent handling, used when persisting media
900      *    button event receiver across reboots).
901      */
902     private final PendingIntent mRcMediaIntent;
903 
904     /**
905      * Reflects whether any "plugged in" IRemoteControlDisplay has mWantsPositonSync set to true.
906      */
907     // TODO consider using a ref count for IRemoteControlDisplay requiring sync instead
908     private boolean mNeedsPositionSync = false;
909 
910     /**
911      * Cache for the current playback state using Session APIs.
912      */
913     private PlaybackState mSessionPlaybackState = null;
914 
915     /**
916      * Cache for metadata using Session APIs. This is re-initialized in apply().
917      */
918     private MediaMetadata mMediaMetadata;
919 
920     /**
921      * @hide
922      * Accessor to media button intent description (includes target component)
923      */
getRcMediaIntent()924     public PendingIntent getRcMediaIntent() {
925         return mRcMediaIntent;
926     }
927 
928     /**
929      * @hide
930      * Default value for the unique identifier
931      */
932     public final static int RCSE_ID_UNREGISTERED = -1;
933 
934     // USE_SESSIONS
935     private MediaSession.Callback mTransportListener = new MediaSession.Callback() {
936 
937         @Override
938         public void onSeekTo(long pos) {
939             RemoteControlClient.this.onSeekTo(mCurrentClientGenId, pos);
940         }
941 
942         @Override
943         public void onSetRating(Rating rating) {
944             if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) {
945                 onUpdateMetadata(mCurrentClientGenId, MetadataEditor.RATING_KEY_BY_USER, rating);
946             }
947         }
948     };
949 
950     //===========================================================
951     // Message handlers
952 
onSeekTo(int generationId, long timeMs)953     private void onSeekTo(int generationId, long timeMs) {
954         synchronized (mCacheLock) {
955             if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) {
956                 mPositionUpdateListener.onPlaybackPositionUpdate(timeMs);
957             }
958         }
959     }
960 
onUpdateMetadata(int generationId, int key, Object value)961     private void onUpdateMetadata(int generationId, int key, Object value) {
962         synchronized (mCacheLock) {
963             if ((mCurrentClientGenId == generationId) && (mMetadataUpdateListener != null)) {
964                 mMetadataUpdateListener.onMetadataUpdate(key, value);
965             }
966         }
967     }
968 
969     //===========================================================
970     // Internal utilities
971 
972     /**
973      * Returns whether, for the given playback state, the playback position is expected to
974      * be changing.
975      * @param playstate the playback state to evaluate
976      * @return true during any form of playback, false if it's not playing anything while in this
977      *     playback state
978      */
playbackPositionShouldMove(int playstate)979     static boolean playbackPositionShouldMove(int playstate) {
980         switch(playstate) {
981             case PLAYSTATE_STOPPED:
982             case PLAYSTATE_PAUSED:
983             case PLAYSTATE_BUFFERING:
984             case PLAYSTATE_ERROR:
985             case PLAYSTATE_SKIPPING_FORWARDS:
986             case PLAYSTATE_SKIPPING_BACKWARDS:
987                 return false;
988             case PLAYSTATE_PLAYING:
989             case PLAYSTATE_FAST_FORWARDING:
990             case PLAYSTATE_REWINDING:
991             default:
992                 return true;
993         }
994     }
995 
996     /**
997      * Period for playback position drift checks, 15s when playing at 1x or slower.
998      */
999     private final static long POSITION_REFRESH_PERIOD_PLAYING_MS = 15000;
1000 
1001     /**
1002      * Minimum period for playback position drift checks, never more often when every 2s, when
1003      * fast forwarding or rewinding.
1004      */
1005     private final static long POSITION_REFRESH_PERIOD_MIN_MS = 2000;
1006 
1007     /**
1008      * The value above which the difference between client-reported playback position and
1009      * estimated position is considered a drift.
1010      */
1011     private final static long POSITION_DRIFT_MAX_MS = 500;
1012 
1013     /**
1014      * Compute the period at which the estimated playback position should be compared against the
1015      * actual playback position. Is a funciton of playback speed.
1016      * @param speed 1.0f is normal playback speed
1017      * @return the period in ms
1018      */
getCheckPeriodFromSpeed(float speed)1019     private static long getCheckPeriodFromSpeed(float speed) {
1020         if (Math.abs(speed) <= 1.0f) {
1021             return POSITION_REFRESH_PERIOD_PLAYING_MS;
1022         } else {
1023             return Math.max((long)(POSITION_REFRESH_PERIOD_PLAYING_MS / Math.abs(speed)),
1024                     POSITION_REFRESH_PERIOD_MIN_MS);
1025         }
1026     }
1027 
1028     /**
1029      * Get the {@link PlaybackState} state for the given
1030      * {@link RemoteControlClient} state.
1031      *
1032      * @param rccState The state used by {@link RemoteControlClient}.
1033      * @return The equivalent state used by {@link PlaybackState}.
1034      */
getStateFromRccState(int rccState)1035     private static int getStateFromRccState(int rccState) {
1036         switch (rccState) {
1037             case PLAYSTATE_BUFFERING:
1038                 return PlaybackState.STATE_BUFFERING;
1039             case PLAYSTATE_ERROR:
1040                 return PlaybackState.STATE_ERROR;
1041             case PLAYSTATE_FAST_FORWARDING:
1042                 return PlaybackState.STATE_FAST_FORWARDING;
1043             case PLAYSTATE_NONE:
1044                 return PlaybackState.STATE_NONE;
1045             case PLAYSTATE_PAUSED:
1046                 return PlaybackState.STATE_PAUSED;
1047             case PLAYSTATE_PLAYING:
1048                 return PlaybackState.STATE_PLAYING;
1049             case PLAYSTATE_REWINDING:
1050                 return PlaybackState.STATE_REWINDING;
1051             case PLAYSTATE_SKIPPING_BACKWARDS:
1052                 return PlaybackState.STATE_SKIPPING_TO_PREVIOUS;
1053             case PLAYSTATE_SKIPPING_FORWARDS:
1054                 return PlaybackState.STATE_SKIPPING_TO_NEXT;
1055             case PLAYSTATE_STOPPED:
1056                 return PlaybackState.STATE_STOPPED;
1057             default:
1058                 return -1;
1059         }
1060     }
1061 
1062     /**
1063      * Get the {@link RemoteControlClient} state for the given
1064      * {@link PlaybackState} state.
1065      *
1066      * @param state The state used by {@link PlaybackState}.
1067      * @return The equivalent state used by {@link RemoteControlClient}.
1068      */
getRccStateFromState(int state)1069     static int getRccStateFromState(int state) {
1070         switch (state) {
1071             case PlaybackState.STATE_BUFFERING:
1072                 return PLAYSTATE_BUFFERING;
1073             case PlaybackState.STATE_ERROR:
1074                 return PLAYSTATE_ERROR;
1075             case PlaybackState.STATE_FAST_FORWARDING:
1076                 return PLAYSTATE_FAST_FORWARDING;
1077             case PlaybackState.STATE_NONE:
1078                 return PLAYSTATE_NONE;
1079             case PlaybackState.STATE_PAUSED:
1080                 return PLAYSTATE_PAUSED;
1081             case PlaybackState.STATE_PLAYING:
1082                 return PLAYSTATE_PLAYING;
1083             case PlaybackState.STATE_REWINDING:
1084                 return PLAYSTATE_REWINDING;
1085             case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
1086                 return PLAYSTATE_SKIPPING_BACKWARDS;
1087             case PlaybackState.STATE_SKIPPING_TO_NEXT:
1088                 return PLAYSTATE_SKIPPING_FORWARDS;
1089             case PlaybackState.STATE_STOPPED:
1090                 return PLAYSTATE_STOPPED;
1091             default:
1092                 return -1;
1093         }
1094     }
1095 
getActionsFromRccControlFlags(int rccFlags)1096     private static long getActionsFromRccControlFlags(int rccFlags) {
1097         long actions = 0;
1098         long flag = 1;
1099         while (flag <= rccFlags) {
1100             if ((flag & rccFlags) != 0) {
1101                 actions |= getActionForRccFlag((int) flag);
1102             }
1103             flag = flag << 1;
1104         }
1105         return actions;
1106     }
1107 
getRccControlFlagsFromActions(long actions)1108     static int getRccControlFlagsFromActions(long actions) {
1109         int rccFlags = 0;
1110         long action = 1;
1111         while (action <= actions && action < Integer.MAX_VALUE) {
1112             if ((action & actions) != 0) {
1113                 rccFlags |= getRccFlagForAction(action);
1114             }
1115             action = action << 1;
1116         }
1117         return rccFlags;
1118     }
1119 
getActionForRccFlag(int flag)1120     private static long getActionForRccFlag(int flag) {
1121         switch (flag) {
1122             case FLAG_KEY_MEDIA_PREVIOUS:
1123                 return PlaybackState.ACTION_SKIP_TO_PREVIOUS;
1124             case FLAG_KEY_MEDIA_REWIND:
1125                 return PlaybackState.ACTION_REWIND;
1126             case FLAG_KEY_MEDIA_PLAY:
1127                 return PlaybackState.ACTION_PLAY;
1128             case FLAG_KEY_MEDIA_PLAY_PAUSE:
1129                 return PlaybackState.ACTION_PLAY_PAUSE;
1130             case FLAG_KEY_MEDIA_PAUSE:
1131                 return PlaybackState.ACTION_PAUSE;
1132             case FLAG_KEY_MEDIA_STOP:
1133                 return PlaybackState.ACTION_STOP;
1134             case FLAG_KEY_MEDIA_FAST_FORWARD:
1135                 return PlaybackState.ACTION_FAST_FORWARD;
1136             case FLAG_KEY_MEDIA_NEXT:
1137                 return PlaybackState.ACTION_SKIP_TO_NEXT;
1138             case FLAG_KEY_MEDIA_POSITION_UPDATE:
1139                 return PlaybackState.ACTION_SEEK_TO;
1140             case FLAG_KEY_MEDIA_RATING:
1141                 return PlaybackState.ACTION_SET_RATING;
1142         }
1143         return 0;
1144     }
1145 
getRccFlagForAction(long action)1146     private static int getRccFlagForAction(long action) {
1147         // We only care about the lower set of actions that can map to rcc
1148         // flags.
1149         int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
1150         switch (testAction) {
1151             case (int) PlaybackState.ACTION_SKIP_TO_PREVIOUS:
1152                 return FLAG_KEY_MEDIA_PREVIOUS;
1153             case (int) PlaybackState.ACTION_REWIND:
1154                 return FLAG_KEY_MEDIA_REWIND;
1155             case (int) PlaybackState.ACTION_PLAY:
1156                 return FLAG_KEY_MEDIA_PLAY;
1157             case (int) PlaybackState.ACTION_PLAY_PAUSE:
1158                 return FLAG_KEY_MEDIA_PLAY_PAUSE;
1159             case (int) PlaybackState.ACTION_PAUSE:
1160                 return FLAG_KEY_MEDIA_PAUSE;
1161             case (int) PlaybackState.ACTION_STOP:
1162                 return FLAG_KEY_MEDIA_STOP;
1163             case (int) PlaybackState.ACTION_FAST_FORWARD:
1164                 return FLAG_KEY_MEDIA_FAST_FORWARD;
1165             case (int) PlaybackState.ACTION_SKIP_TO_NEXT:
1166                 return FLAG_KEY_MEDIA_NEXT;
1167             case (int) PlaybackState.ACTION_SEEK_TO:
1168                 return FLAG_KEY_MEDIA_POSITION_UPDATE;
1169             case (int) PlaybackState.ACTION_SET_RATING:
1170                 return FLAG_KEY_MEDIA_RATING;
1171         }
1172         return 0;
1173     }
1174 }
1175