1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.support.v4.media.session;
17 
18 import android.os.Build;
19 import android.os.Bundle;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.os.SystemClock;
23 import android.text.TextUtils;
24 
25 /**
26  * Playback state for a {@link MediaSessionCompat}. This includes a state like
27  * {@link PlaybackStateCompat#STATE_PLAYING}, the current playback position,
28  * and the current control capabilities.
29  */
30 public final class PlaybackStateCompat implements Parcelable {
31 
32     /**
33      * Indicates this session supports the stop command.
34      *
35      * @see Builder#setActions(long)
36      */
37     public static final long ACTION_STOP = 1 << 0;
38 
39     /**
40      * Indicates this session supports the pause command.
41      *
42      * @see Builder#setActions(long)
43      */
44     public static final long ACTION_PAUSE = 1 << 1;
45 
46     /**
47      * Indicates this session supports the play command.
48      *
49      * @see Builder#setActions(long)
50      */
51     public static final long ACTION_PLAY = 1 << 2;
52 
53     /**
54      * Indicates this session supports the rewind command.
55      *
56      * @see Builder#setActions(long)
57      */
58     public static final long ACTION_REWIND = 1 << 3;
59 
60     /**
61      * Indicates this session supports the previous command.
62      *
63      * @see Builder#setActions(long)
64      */
65     public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
66 
67     /**
68      * Indicates this session supports the next command.
69      *
70      * @see Builder#setActions(long)
71      */
72     public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
73 
74     /**
75      * Indicates this session supports the fast forward command.
76      *
77      * @see Builder#setActions(long)
78      */
79     public static final long ACTION_FAST_FORWARD = 1 << 6;
80 
81     /**
82      * Indicates this session supports the set rating command.
83      *
84      * @see Builder#setActions(long)
85      */
86     public static final long ACTION_SET_RATING = 1 << 7;
87 
88     /**
89      * Indicates this session supports the seek to command.
90      *
91      * @see Builder#setActions(long)
92      */
93     public static final long ACTION_SEEK_TO = 1 << 8;
94 
95     /**
96      * Indicates this session supports the play/pause toggle command.
97      *
98      * @see Builder#setActions(long)
99      */
100     public static final long ACTION_PLAY_PAUSE = 1 << 9;
101 
102     /**
103      * Indicates this session supports the play from media id command.
104      *
105      * @see Builder#setActions(long)
106      */
107     public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10;
108 
109     /**
110      * Indicates this session supports the play from search command.
111      *
112      * @see Builder#setActions(long)
113      */
114     public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11;
115 
116     /**
117      * Indicates this session supports the skip to queue item command.
118      *
119      * @see Builder#setActions(long)
120      */
121     public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
122 
123     /**
124      * This is the default playback state and indicates that no media has been
125      * added yet, or the performer has been reset and has no content to play.
126      *
127      * @see Builder#setState
128      */
129     public final static int STATE_NONE = 0;
130 
131     /**
132      * State indicating this item is currently stopped.
133      *
134      * @see Builder#setState
135      */
136     public final static int STATE_STOPPED = 1;
137 
138     /**
139      * State indicating this item is currently paused.
140      *
141      * @see Builder#setState
142      */
143     public final static int STATE_PAUSED = 2;
144 
145     /**
146      * State indicating this item is currently playing.
147      *
148      * @see Builder#setState
149      */
150     public final static int STATE_PLAYING = 3;
151 
152     /**
153      * State indicating this item is currently fast forwarding.
154      *
155      * @see Builder#setState
156      */
157     public final static int STATE_FAST_FORWARDING = 4;
158 
159     /**
160      * State indicating this item is currently rewinding.
161      *
162      * @see Builder#setState
163      */
164     public final static int STATE_REWINDING = 5;
165 
166     /**
167      * State indicating this item is currently buffering and will begin playing
168      * when enough data has buffered.
169      *
170      * @see Builder#setState
171      */
172     public final static int STATE_BUFFERING = 6;
173 
174     /**
175      * State indicating this item is currently in an error state. The error
176      * message should also be set when entering this state.
177      *
178      * @see Builder#setState
179      */
180     public final static int STATE_ERROR = 7;
181 
182     /**
183      * State indicating the class doing playback is currently connecting to a
184      * route. Depending on the implementation you may return to the previous
185      * state when the connection finishes or enter {@link #STATE_NONE}. If
186      * the connection failed {@link #STATE_ERROR} should be used.
187      * @hide
188      */
189     public final static int STATE_CONNECTING = 8;
190 
191     /**
192      * State indicating the player is currently skipping to the previous item.
193      *
194      * @see Builder#setState
195      */
196     public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
197 
198     /**
199      * State indicating the player is currently skipping to the next item.
200      *
201      * @see Builder#setState
202      */
203     public final static int STATE_SKIPPING_TO_NEXT = 10;
204 
205     /**
206      * Use this value for the position to indicate the position is not known.
207      */
208     public final static long PLAYBACK_POSITION_UNKNOWN = -1;
209 
210     private final int mState;
211     private final long mPosition;
212     private final long mBufferedPosition;
213     private final float mSpeed;
214     private final long mActions;
215     private final CharSequence mErrorMessage;
216     private final long mUpdateTime;
217 
218     private Object mStateObj;
219 
PlaybackStateCompat(int state, long position, long bufferedPosition, float rate, long actions, CharSequence errorMessage, long updateTime)220     private PlaybackStateCompat(int state, long position, long bufferedPosition,
221             float rate, long actions, CharSequence errorMessage, long updateTime) {
222         mState = state;
223         mPosition = position;
224         mBufferedPosition = bufferedPosition;
225         mSpeed = rate;
226         mActions = actions;
227         mErrorMessage = errorMessage;
228         mUpdateTime = updateTime;
229     }
230 
PlaybackStateCompat(Parcel in)231     private PlaybackStateCompat(Parcel in) {
232         mState = in.readInt();
233         mPosition = in.readLong();
234         mSpeed = in.readFloat();
235         mUpdateTime = in.readLong();
236         mBufferedPosition = in.readLong();
237         mActions = in.readLong();
238         mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
239     }
240 
241     @Override
toString()242     public String toString() {
243         StringBuilder bob = new StringBuilder("PlaybackState {");
244         bob.append("state=").append(mState);
245         bob.append(", position=").append(mPosition);
246         bob.append(", buffered position=").append(mBufferedPosition);
247         bob.append(", speed=").append(mSpeed);
248         bob.append(", updated=").append(mUpdateTime);
249         bob.append(", actions=").append(mActions);
250         bob.append(", error=").append(mErrorMessage);
251         bob.append("}");
252         return bob.toString();
253     }
254 
255     @Override
describeContents()256     public int describeContents() {
257         return 0;
258     }
259 
260     @Override
writeToParcel(Parcel dest, int flags)261     public void writeToParcel(Parcel dest, int flags) {
262         dest.writeInt(mState);
263         dest.writeLong(mPosition);
264         dest.writeFloat(mSpeed);
265         dest.writeLong(mUpdateTime);
266         dest.writeLong(mBufferedPosition);
267         dest.writeLong(mActions);
268         TextUtils.writeToParcel(mErrorMessage, dest, flags);
269     }
270 
271     /**
272      * Get the current state of playback. One of the following:
273      * <ul>
274      * <li> {@link PlaybackStateCompat#STATE_NONE}</li>
275      * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li>
276      * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li>
277      * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li>
278      * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li>
279      * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li>
280      * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li>
281      * <li> {@link PlaybackStateCompat#STATE_ERROR}</li>
282      */
getState()283     public int getState() {
284         return mState;
285     }
286 
287     /**
288      * Get the current playback position in ms.
289      */
getPosition()290     public long getPosition() {
291         return mPosition;
292     }
293 
294     /**
295      * Get the current buffered position in ms. This is the farthest playback
296      * point that can be reached from the current position using only buffered
297      * content.
298      */
getBufferedPosition()299     public long getBufferedPosition() {
300         return mBufferedPosition;
301     }
302 
303     /**
304      * Get the current playback speed as a multiple of normal playback. This
305      * should be negative when rewinding. A value of 1 means normal playback and
306      * 0 means paused.
307      *
308      * @return The current speed of playback.
309      */
getPlaybackSpeed()310     public float getPlaybackSpeed() {
311         return mSpeed;
312     }
313 
314     /**
315      * Get the current actions available on this session. This should use a
316      * bitmask of the available actions.
317      * <ul>
318      * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li>
319      * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li>
320      * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li>
321      * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li>
322      * <li> {@link PlaybackStateCompat#ACTION_STOP}</li>
323      * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li>
324      * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li>
325      * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li>
326      * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li>
327      * </ul>
328      */
getActions()329     public long getActions() {
330         return mActions;
331     }
332 
333     /**
334      * Get a user readable error message. This should be set when the state is
335      * {@link PlaybackStateCompat#STATE_ERROR}.
336      */
getErrorMessage()337     public CharSequence getErrorMessage() {
338         return mErrorMessage;
339     }
340 
341     /**
342      * Get the elapsed real time at which position was last updated. If the
343      * position has never been set this will return 0;
344      *
345      * @return The last time the position was updated.
346      */
getLastPositionUpdateTime()347     public long getLastPositionUpdateTime() {
348         return mUpdateTime;
349     }
350 
351     /**
352      * Creates an instance from a framework {@link android.media.session.PlaybackState} object.
353      * <p>
354      * This method is only supported on API 21+.
355      * </p>
356      *
357      * @param stateObj A {@link android.media.session.PlaybackState} object, or null if none.
358      * @return An equivalent {@link PlaybackStateCompat} object, or null if none.
359      */
fromPlaybackState(Object stateObj)360     public static PlaybackStateCompat fromPlaybackState(Object stateObj) {
361         if (stateObj == null || Build.VERSION.SDK_INT < 21) {
362             return null;
363         }
364 
365         PlaybackStateCompat state = new PlaybackStateCompat(
366                 PlaybackStateCompatApi21.getState(stateObj),
367                 PlaybackStateCompatApi21.getPosition(stateObj),
368                 PlaybackStateCompatApi21.getBufferedPosition(stateObj),
369                 PlaybackStateCompatApi21.getPlaybackSpeed(stateObj),
370                 PlaybackStateCompatApi21.getActions(stateObj),
371                 PlaybackStateCompatApi21.getErrorMessage(stateObj),
372                 PlaybackStateCompatApi21.getLastPositionUpdateTime(stateObj));
373         state.mStateObj = stateObj;
374         return state;
375     }
376 
377     /**
378      * Gets the underlying framework {@link android.media.session.PlaybackState} object.
379      * <p>
380      * This method is only supported on API 21+.
381      * </p>
382      *
383      * @return An equivalent {@link android.media.session.PlaybackState} object, or null if none.
384      */
getPlaybackState()385     public Object getPlaybackState() {
386         if (mStateObj != null || Build.VERSION.SDK_INT < 21) {
387             return mStateObj;
388         }
389 
390         mStateObj = PlaybackStateCompatApi21.newInstance(mState, mPosition, mBufferedPosition,
391                 mSpeed, mActions, mErrorMessage, mUpdateTime);
392         return mStateObj;
393     }
394 
395     public static final Parcelable.Creator<PlaybackStateCompat> CREATOR =
396             new Parcelable.Creator<PlaybackStateCompat>() {
397         @Override
398         public PlaybackStateCompat createFromParcel(Parcel in) {
399             return new PlaybackStateCompat(in);
400         }
401 
402         @Override
403         public PlaybackStateCompat[] newArray(int size) {
404             return new PlaybackStateCompat[size];
405         }
406     };
407 
408     /**
409      * {@link PlaybackStateCompat.CustomAction CustomActions} can be used to
410      * extend the capabilities of the standard transport controls by exposing
411      * app specific actions to {@link MediaControllerCompat Controllers}.
412      */
413     public static final class CustomAction implements Parcelable {
414         private final String mAction;
415         private final CharSequence mName;
416         private final int mIcon;
417         private final Bundle mExtras;
418 
419         /**
420          * Use {@link PlaybackStateCompat.CustomAction.Builder#build()}.
421          */
CustomAction(String action, CharSequence name, int icon, Bundle extras)422         private CustomAction(String action, CharSequence name, int icon, Bundle extras) {
423             mAction = action;
424             mName = name;
425             mIcon = icon;
426             mExtras = extras;
427         }
428 
CustomAction(Parcel in)429         private CustomAction(Parcel in) {
430             mAction = in.readString();
431             mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
432             mIcon = in.readInt();
433             mExtras = in.readBundle();
434         }
435 
436         @Override
writeToParcel(Parcel dest, int flags)437         public void writeToParcel(Parcel dest, int flags) {
438             dest.writeString(mAction);
439             TextUtils.writeToParcel(mName, dest, flags);
440             dest.writeInt(mIcon);
441             dest.writeBundle(mExtras);
442         }
443 
444         @Override
describeContents()445         public int describeContents() {
446             return 0;
447         }
448 
449         public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR
450                 = new Parcelable.Creator<PlaybackStateCompat.CustomAction>() {
451 
452                     @Override
453                     public PlaybackStateCompat.CustomAction createFromParcel(Parcel p) {
454                         return new PlaybackStateCompat.CustomAction(p);
455                     }
456 
457                     @Override
458                     public PlaybackStateCompat.CustomAction[] newArray(int size) {
459                         return new PlaybackStateCompat.CustomAction[size];
460                     }
461                 };
462 
463         /**
464          * Returns the action of the {@link CustomAction}.
465          *
466          * @return The action of the {@link CustomAction}.
467          */
getAction()468         public String getAction() {
469             return mAction;
470         }
471 
472         /**
473          * Returns the display name of this action. e.g. "Favorite"
474          *
475          * @return The display name of this {@link CustomAction}.
476          */
getName()477         public CharSequence getName() {
478             return mName;
479         }
480 
481         /**
482          * Returns the resource id of the icon in the {@link MediaSessionCompat
483          * Session's} package.
484          *
485          * @return The resource id of the icon in the {@link MediaSessionCompat
486          *         Session's} package.
487          */
getIcon()488         public int getIcon() {
489             return mIcon;
490         }
491 
492         /**
493          * Returns extras which provide additional application-specific
494          * information about the action, or null if none. These arguments are
495          * meant to be consumed by a {@link MediaControllerCompat} if it knows
496          * how to handle them.
497          *
498          * @return Optional arguments for the {@link CustomAction}.
499          */
getExtras()500         public Bundle getExtras() {
501             return mExtras;
502         }
503 
504         @Override
toString()505         public String toString() {
506             return "Action:" +
507                     "mName='" + mName +
508                     ", mIcon=" + mIcon +
509                     ", mExtras=" + mExtras;
510         }
511 
512         /**
513          * Builder for {@link CustomAction} objects.
514          */
515         public static final class Builder {
516             private final String mAction;
517             private final CharSequence mName;
518             private final int mIcon;
519             private Bundle mExtras;
520 
521             /**
522              * Creates a {@link CustomAction} builder with the id, name, and
523              * icon set.
524              *
525              * @param action The action of the {@link CustomAction}.
526              * @param name The display name of the {@link CustomAction}. This
527              *            name will be displayed along side the action if the UI
528              *            supports it.
529              * @param icon The icon resource id of the {@link CustomAction}.
530              *            This resource id must be in the same package as the
531              *            {@link MediaSessionCompat}. It will be displayed with
532              *            the custom action if the UI supports it.
533              */
Builder(String action, CharSequence name, int icon)534             public Builder(String action, CharSequence name, int icon) {
535                 if (TextUtils.isEmpty(action)) {
536                     throw new IllegalArgumentException(
537                             "You must specify an action to build a CustomAction.");
538                 }
539                 if (TextUtils.isEmpty(name)) {
540                     throw new IllegalArgumentException(
541                             "You must specify a name to build a CustomAction.");
542                 }
543                 if (icon == 0) {
544                     throw new IllegalArgumentException(
545                             "You must specify an icon resource id to build a CustomAction.");
546                 }
547                 mAction = action;
548                 mName = name;
549                 mIcon = icon;
550             }
551 
552             /**
553              * Set optional extras for the {@link CustomAction}. These extras
554              * are meant to be consumed by a {@link MediaControllerCompat} if it
555              * knows how to handle them. Keys should be fully qualified (e.g.
556              * "com.example.MY_ARG") to avoid collisions.
557              *
558              * @param extras Optional extras for the {@link CustomAction}.
559              * @return this.
560              */
setExtras(Bundle extras)561             public Builder setExtras(Bundle extras) {
562                 mExtras = extras;
563                 return this;
564             }
565 
566             /**
567              * Build and return the {@link CustomAction} instance with the
568              * specified values.
569              *
570              * @return A new {@link CustomAction} instance.
571              */
build()572             public CustomAction build() {
573                 return new CustomAction(mAction, mName, mIcon, mExtras);
574             }
575         }
576     }
577 
578     /**
579      * Builder for {@link PlaybackStateCompat} objects.
580      */
581     public static final class Builder {
582         private int mState;
583         private long mPosition;
584         private long mBufferedPosition;
585         private float mRate;
586         private long mActions;
587         private CharSequence mErrorMessage;
588         private long mUpdateTime;
589 
590         /**
591          * Create an empty Builder.
592          */
Builder()593         public Builder() {
594         }
595 
596         /**
597          * Create a Builder using a {@link PlaybackStateCompat} instance to set the
598          * initial values.
599          *
600          * @param source The playback state to copy.
601          */
Builder(PlaybackStateCompat source)602         public Builder(PlaybackStateCompat source) {
603             mState = source.mState;
604             mPosition = source.mPosition;
605             mRate = source.mSpeed;
606             mUpdateTime = source.mUpdateTime;
607             mBufferedPosition = source.mBufferedPosition;
608             mActions = source.mActions;
609             mErrorMessage = source.mErrorMessage;
610         }
611 
612         /**
613          * Set the current state of playback.
614          * <p>
615          * The position must be in ms and indicates the current playback
616          * position within the track. If the position is unknown use
617          * {@link #PLAYBACK_POSITION_UNKNOWN}.
618          * <p>
619          * The rate is a multiple of normal playback and should be 0 when paused
620          * and negative when rewinding. Normal playback rate is 1.0.
621          * <p>
622          * The state must be one of the following:
623          * <ul>
624          * <li> {@link PlaybackStateCompat#STATE_NONE}</li>
625          * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li>
626          * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li>
627          * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li>
628          * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li>
629          * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li>
630          * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li>
631          * <li> {@link PlaybackStateCompat#STATE_ERROR}</li>
632          * </ul>
633          *
634          * @param state The current state of playback.
635          * @param position The position in the current track in ms.
636          * @param playbackSpeed The current rate of playback as a multiple of
637          *            normal playback.
638          */
setState(int state, long position, float playbackSpeed)639         public Builder setState(int state, long position, float playbackSpeed) {
640             return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime());
641         }
642 
643         /**
644          * Set the current state of playback.
645          * <p>
646          * The position must be in ms and indicates the current playback
647          * position within the track. If the position is unknown use
648          * {@link #PLAYBACK_POSITION_UNKNOWN}.
649          * <p>
650          * The rate is a multiple of normal playback and should be 0 when paused
651          * and negative when rewinding. Normal playback rate is 1.0.
652          * <p>
653          * The state must be one of the following:
654          * <ul>
655          * <li> {@link PlaybackStateCompat#STATE_NONE}</li>
656          * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li>
657          * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li>
658          * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li>
659          * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li>
660          * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li>
661          * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li>
662          * <li> {@link PlaybackStateCompat#STATE_ERROR}</li>
663          * </ul>
664          *
665          * @param state The current state of playback.
666          * @param position The position in the current item in ms.
667          * @param playbackSpeed The current speed of playback as a multiple of
668          *            normal playback.
669          * @param updateTime The time in the {@link SystemClock#elapsedRealtime}
670          *            timebase that the position was updated at.
671          * @return this
672          */
setState(int state, long position, float playbackSpeed, long updateTime)673         public Builder setState(int state, long position, float playbackSpeed, long updateTime) {
674             mState = state;
675             mPosition = position;
676             mUpdateTime = updateTime;
677             mRate = playbackSpeed;
678             return this;
679         }
680 
681         /**
682          * Set the current buffered position in ms. This is the farthest
683          * playback point that can be reached from the current position using
684          * only buffered content.
685          *
686          * @return this
687          */
setBufferedPosition(long bufferPosition)688         public Builder setBufferedPosition(long bufferPosition) {
689             mBufferedPosition = bufferPosition;
690             return this;
691         }
692 
693         /**
694          * Set the current capabilities available on this session. This should
695          * use a bitmask of the available capabilities.
696          * <ul>
697          * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li>
698          * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li>
699          * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li>
700          * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li>
701          * <li> {@link PlaybackStateCompat#ACTION_STOP}</li>
702          * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li>
703          * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li>
704          * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li>
705          * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li>
706          * </ul>
707          *
708          * @return this
709          */
setActions(long capabilities)710         public Builder setActions(long capabilities) {
711             mActions = capabilities;
712             return this;
713         }
714 
715         /**
716          * Set a user readable error message. This should be set when the state
717          * is {@link PlaybackStateCompat#STATE_ERROR}.
718          *
719          * @return this
720          */
setErrorMessage(CharSequence errorMessage)721         public Builder setErrorMessage(CharSequence errorMessage) {
722             mErrorMessage = errorMessage;
723             return this;
724         }
725 
726         /**
727          * Creates the playback state object.
728          */
build()729         public PlaybackStateCompat build() {
730             return new PlaybackStateCompat(mState, mPosition, mBufferedPosition,
731                     mRate, mActions, mErrorMessage, mUpdateTime);
732         }
733     }
734 }
735