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
17package android.media;
18
19import android.app.PendingIntent;
20import android.content.ComponentName;
21import android.content.Intent;
22import android.graphics.Bitmap;
23import android.media.session.MediaSessionLegacyHelper;
24import android.media.session.PlaybackState;
25import android.media.session.MediaSession;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Looper;
29import android.os.Message;
30import android.os.SystemClock;
31import android.util.Log;
32
33import 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     */
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     */
369    public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) {
370        mRcMediaIntent = mediaButtonIntent;
371    }
372
373    /**
374     * @hide
375     */
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     */
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     */
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
416        private MetadataEditor() { }
417        /**
418         * @hide
419         */
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         */
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         */
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
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
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
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         */
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     */
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     */
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     */
655    public void setPlaybackState(int state, long timeInMs, float playbackSpeed) {
656        setPlaybackStateInt(state, timeInMs, playbackSpeed, true);
657    }
658
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     */
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         */
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     */
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         */
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         */
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     */
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     */
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     */
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
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
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     */
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     */
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