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