1 /*
2  * Copyright (C) 2016 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 com.android.bluetooth.avrcpcontroller;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.net.Uri;
21 import android.os.SystemClock;
22 import android.support.v4.media.session.MediaSessionCompat;
23 import android.support.v4.media.session.PlaybackStateCompat;
24 import android.util.Log;
25 
26 import java.util.Arrays;
27 
28 /*
29  * Contains information about remote player
30  */
31 class AvrcpPlayer {
32     private static final String TAG = "AvrcpPlayer";
33     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
34 
35     public static final int DEFAULT_ID = -1;
36 
37     public static final int TYPE_UNKNOWN = -1;
38     public static final int TYPE_AUDIO = 0;
39     public static final int TYPE_VIDEO = 1;
40     public static final int TYPE_BROADCASTING_AUDIO = 2;
41     public static final int TYPE_BROADCASTING_VIDEO = 3;
42 
43     public static final int SUB_TYPE_UNKNOWN = -1;
44     public static final int SUB_TYPE_AUDIO_BOOK = 0;
45     public static final int SUB_TYPE_PODCAST = 1;
46 
47     public static final int FEATURE_PLAY = 40;
48     public static final int FEATURE_STOP = 41;
49     public static final int FEATURE_PAUSE = 42;
50     public static final int FEATURE_REWIND = 44;
51     public static final int FEATURE_FAST_FORWARD = 45;
52     public static final int FEATURE_FORWARD = 47;
53     public static final int FEATURE_PREVIOUS = 48;
54     public static final int FEATURE_BROWSING = 59;
55     public static final int FEATURE_NOW_PLAYING = 65;
56 
57     private BluetoothDevice mDevice;
58     private int mPlayStatus = PlaybackStateCompat.STATE_NONE;
59     private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
60     private long mPlayTimeUpdate = 0;
61     private float mPlaySpeed = 1;
62     private int mId;
63     private String mName = "";
64     private int mPlayerType;
65     private byte[] mPlayerFeatures = new byte[16];
66     private long mAvailableActions = PlaybackStateCompat.ACTION_PREPARE;
67     private AvrcpItem mCurrentTrack;
68     private PlaybackStateCompat mPlaybackStateCompat;
69     private PlayerApplicationSettings mSupportedPlayerApplicationSettings =
70             new PlayerApplicationSettings();
71     private PlayerApplicationSettings mCurrentPlayerApplicationSettings;
72 
AvrcpPlayer(BluetoothDevice device, int id, int playerType, int playerSubType, String name, byte[] playerFeatures, int playStatus)73     private AvrcpPlayer(BluetoothDevice device, int id, int playerType, int playerSubType,
74             String name, byte[] playerFeatures, int playStatus) {
75         mDevice = device;
76         mId = id;
77         mName = name;
78         mPlayerType = playerType;
79         mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length);
80         PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
81                 .setActions(mAvailableActions);
82         mPlaybackStateCompat = playbackStateBuilder.build();
83         updateAvailableActions();
84         setPlayStatus(playStatus);
85     }
86 
getDevice()87     public BluetoothDevice getDevice() {
88         return mDevice;
89     }
90 
getId()91     public int getId() {
92         return mId;
93     }
94 
getName()95     public String getName() {
96         return mName;
97     }
98 
setPlayTime(int playTime)99     public void setPlayTime(int playTime) {
100         mPlayTime = playTime;
101         mPlayTimeUpdate = SystemClock.elapsedRealtime();
102         mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
103                 mPlayStatus, mPlayTime,
104                 mPlaySpeed).build();
105     }
106 
getPlayTime()107     public long getPlayTime() {
108         return mPlayTime;
109     }
110 
setPlayStatus(int playStatus)111     public void setPlayStatus(int playStatus) {
112         if (mPlayTime != PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN) {
113             mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime()
114                     - mPlaybackStateCompat.getLastPositionUpdateTime());
115         }
116         mPlayStatus = playStatus;
117         switch (mPlayStatus) {
118             case PlaybackStateCompat.STATE_STOPPED:
119                 mPlaySpeed = 0;
120                 break;
121             case PlaybackStateCompat.STATE_PLAYING:
122                 mPlaySpeed = 1;
123                 break;
124             case PlaybackStateCompat.STATE_PAUSED:
125                 mPlaySpeed = 0;
126                 break;
127             case PlaybackStateCompat.STATE_FAST_FORWARDING:
128                 mPlaySpeed = 3;
129                 break;
130             case PlaybackStateCompat.STATE_REWINDING:
131                 mPlaySpeed = -3;
132                 break;
133         }
134 
135         mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
136                 mPlayStatus, mPlayTime,
137                 mPlaySpeed).build();
138     }
139 
setSupportedPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)140     public void setSupportedPlayerApplicationSettings(
141             PlayerApplicationSettings playerApplicationSettings) {
142         mSupportedPlayerApplicationSettings = playerApplicationSettings;
143         updateAvailableActions();
144     }
145 
setCurrentPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)146     public void setCurrentPlayerApplicationSettings(
147             PlayerApplicationSettings playerApplicationSettings) {
148         Log.d(TAG, "Settings changed");
149         mCurrentPlayerApplicationSettings = playerApplicationSettings;
150         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
151         session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting(
152                 PlayerApplicationSettings.REPEAT_STATUS));
153         session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting(
154                 PlayerApplicationSettings.SHUFFLE_STATUS));
155     }
156 
getPlayStatus()157     public int getPlayStatus() {
158         return mPlayStatus;
159     }
160 
supportsFeature(int featureId)161     public boolean supportsFeature(int featureId) {
162         int byteNumber = featureId / 8;
163         byte bitMask = (byte) (1 << (featureId % 8));
164         return (mPlayerFeatures[byteNumber] & bitMask) == bitMask;
165     }
166 
supportsSetting(int settingType, int settingValue)167     public boolean supportsSetting(int settingType, int settingValue) {
168         return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue);
169     }
170 
getPlaybackState()171     public PlaybackStateCompat getPlaybackState() {
172         if (DBG) {
173             Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime);
174         }
175         return mPlaybackStateCompat;
176     }
177 
updateCurrentTrack(AvrcpItem update)178     public synchronized void updateCurrentTrack(AvrcpItem update) {
179         if (update != null) {
180             long trackNumber = update.getTrackNumber();
181             mPlaybackStateCompat = new PlaybackStateCompat.Builder(
182                     mPlaybackStateCompat).setActiveQueueItemId(
183                     trackNumber - 1).build();
184         }
185         mCurrentTrack = update;
186     }
187 
notifyImageDownload(String uuid, Uri imageUri)188     public synchronized boolean notifyImageDownload(String uuid, Uri imageUri) {
189         if (DBG) Log.d(TAG, "Got an image download -- uuid=" + uuid + ", uri=" + imageUri);
190         if (uuid == null || imageUri == null || mCurrentTrack == null) return false;
191         if (uuid.equals(mCurrentTrack.getCoverArtUuid())) {
192             mCurrentTrack.setCoverArtLocation(imageUri);
193             if (DBG) Log.d(TAG, "Image UUID '" + uuid + "' was added to current track.");
194             return true;
195         }
196         return false;
197     }
198 
getCurrentTrack()199     public synchronized AvrcpItem getCurrentTrack() {
200         return mCurrentTrack;
201     }
202 
updateAvailableActions()203     private void updateAvailableActions() {
204         mAvailableActions = PlaybackStateCompat.ACTION_PREPARE;
205         if (supportsFeature(FEATURE_PLAY)) {
206             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY;
207         }
208         if (supportsFeature(FEATURE_STOP)) {
209             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP;
210         }
211         if (supportsFeature(FEATURE_PAUSE)) {
212             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE;
213         }
214         if (supportsFeature(FEATURE_REWIND)) {
215             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND;
216         }
217         if (supportsFeature(FEATURE_FAST_FORWARD)) {
218             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD;
219         }
220         if (supportsFeature(FEATURE_FORWARD)) {
221             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
222         }
223         if (supportsFeature(FEATURE_PREVIOUS)) {
224             mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
225         }
226         if (mSupportedPlayerApplicationSettings.supportsSetting(
227                 PlayerApplicationSettings.REPEAT_STATUS)) {
228             mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
229         }
230         if (mSupportedPlayerApplicationSettings.supportsSetting(
231                 PlayerApplicationSettings.SHUFFLE_STATUS)) {
232             mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
233         }
234         mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat)
235                 .setActions(mAvailableActions).build();
236 
237         if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
238     }
239 
240     @Override
toString()241     public String toString() {
242         return "<AvrcpPlayer id=" + mId + " name=" + mName + " track=" + mCurrentTrack
243                 + " playState=" + mPlaybackStateCompat + ">";
244     }
245 
246     /**
247      * A Builder object for an AvrcpPlayer
248      */
249     public static class Builder {
250         private static final String TAG = "AvrcpPlayer.Builder";
251         private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
252 
253         private BluetoothDevice mDevice = null;
254         private int mPlayerId = AvrcpPlayer.DEFAULT_ID;
255         private int mPlayerType = AvrcpPlayer.TYPE_UNKNOWN;
256         private int mPlayerSubType = AvrcpPlayer.SUB_TYPE_UNKNOWN;
257         private String mPlayerName = null;
258         private byte[] mSupportedFeatures = new byte[16];
259 
260         private int mPlayStatus = PlaybackStateCompat.STATE_NONE;
261         private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
262         private float mPlaySpeed = 1;
263         private long mPlayTimeUpdate = 0;
264 
265         private AvrcpItem mTrack = null;
266 
267         /**
268          * Set the device that this Player came from
269          *
270          * @param device The BleutoothDevice representing the remote device
271          * @return This object, so you can continue building
272          */
setDevice(BluetoothDevice device)273         public Builder setDevice(BluetoothDevice device) {
274             mDevice = device;
275             return this;
276         }
277 
278         /**
279          * Set the Player ID for this Player
280          *
281          * @param playerId The ID for this player, defined in AVRCP 6.10.2.1
282          * @return This object, so you can continue building
283          */
setPlayerId(int playerId)284         public Builder setPlayerId(int playerId) {
285             mPlayerId = playerId;
286             return this;
287         }
288 
289         /**
290          * Set the Player Type for this Player
291          *
292          * @param playerType The type for this player, defined in AVRCP 6.10.2.1
293          * @return This object, so you can continue building
294          */
setPlayerType(int playerType)295         public Builder setPlayerType(int playerType) {
296             mPlayerType = playerType;
297             return this;
298         }
299 
300         /**
301          * Set the Player Sub-type for this Player
302          *
303          * @param playerSubType The sub-type for this player, defined in AVRCP 6.10.2.1
304          * @return This object, so you can continue building
305          */
setPlayerSubType(int playerSubType)306         public Builder setPlayerSubType(int playerSubType) {
307             mPlayerSubType = playerSubType;
308             return this;
309         }
310 
311         /**
312          * Set the name for this Player. This is what users will see when browsing.
313          *
314          * @param name The name for this player, defined in AVRCP 6.10.2.1
315          * @return This object, so you can continue building
316          */
setName(String name)317         public Builder setName(String name) {
318             mPlayerName = name;
319             return this;
320         }
321 
322         /**
323          * Set the entire set of supported features for this Player.
324          *
325          * @param features The feature set for this player, defined in AVRCP 6.10.2.1
326          * @return This object, so you can continue building
327          */
setSupportedFeatures(byte[] supportedFeatures)328         public Builder setSupportedFeatures(byte[] supportedFeatures) {
329             mSupportedFeatures = supportedFeatures;
330             return this;
331         }
332 
333         /**
334          * Set a single features as supported for this Player.
335          *
336          * @param feature The feature for this player, defined in AVRCP 6.10.2.1
337          * @return This object, so you can continue building
338          */
setSupportedFeature(int feature)339         public Builder setSupportedFeature(int feature) {
340             int byteNumber = feature / 8;
341             byte bitMask = (byte) (1 << (feature % 8));
342             mSupportedFeatures[byteNumber] = (byte) (mSupportedFeatures[byteNumber] | bitMask);
343             return this;
344         }
345 
346         /**
347          * Set the initial play status of the Player.
348          *
349          * @param playStatus The play state for this player as a PlaybackStateCompat.STATE_* value
350          * @return This object, so you can continue building
351          */
setPlayStatus(int playStatus)352         public Builder setPlayStatus(int playStatus) {
353             mPlayStatus = playStatus;
354             return this;
355         }
356 
357         /**
358          * Set the initial play status of the Player.
359          *
360          * @param track The initial track for this player
361          * @return This object, so you can continue building
362          */
setCurrentTrack(AvrcpItem track)363         public Builder setCurrentTrack(AvrcpItem track) {
364             mTrack = track;
365             return this;
366         }
367 
build()368         public AvrcpPlayer build() {
369             AvrcpPlayer player = new AvrcpPlayer(mDevice, mPlayerId, mPlayerType, mPlayerSubType,
370                     mPlayerName, mSupportedFeatures, mPlayStatus);
371             player.updateCurrentTrack(mTrack);
372             return player;
373         }
374     }
375 }
376