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