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.class.getSimpleName(); 33 34 public static final int DEFAULT_ID = -1; 35 36 public static final int TYPE_UNKNOWN = -1; 37 public static final int TYPE_AUDIO = 0; 38 public static final int TYPE_VIDEO = 1; 39 public static final int TYPE_BROADCASTING_AUDIO = 2; 40 public static final int TYPE_BROADCASTING_VIDEO = 3; 41 42 public static final int SUB_TYPE_UNKNOWN = -1; 43 public static final int SUB_TYPE_AUDIO_BOOK = 0; 44 public static final int SUB_TYPE_PODCAST = 1; 45 46 public static final int FEATURE_PLAY = 40; 47 public static final int FEATURE_STOP = 41; 48 public static final int FEATURE_PAUSE = 42; 49 public static final int FEATURE_REWIND = 44; 50 public static final int FEATURE_FAST_FORWARD = 45; 51 public static final int FEATURE_FORWARD = 47; 52 public static final int FEATURE_PREVIOUS = 48; 53 public static final int FEATURE_BROWSING = 59; 54 public static final int FEATURE_NOW_PLAYING = 65; 55 56 private BluetoothDevice mDevice; 57 private int mPlayStatus = PlaybackStateCompat.STATE_NONE; 58 private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN; 59 private float mPlaySpeed = 1; 60 private int mId; 61 private String mName = ""; 62 private byte[] mPlayerFeatures = new byte[16]; 63 private long mAvailableActions = PlaybackStateCompat.ACTION_PREPARE; 64 private AvrcpItem mCurrentTrack; 65 private PlaybackStateCompat mPlaybackStateCompat; 66 private PlayerApplicationSettings mSupportedPlayerApplicationSettings = 67 new PlayerApplicationSettings(); 68 private PlayerApplicationSettings mCurrentPlayerApplicationSettings; 69 AvrcpPlayer( BluetoothDevice device, int id, String name, byte[] playerFeatures, int playStatus)70 private AvrcpPlayer( 71 BluetoothDevice device, int id, String name, byte[] playerFeatures, int playStatus) { 72 mDevice = device; 73 mId = id; 74 mName = name; 75 mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length); 76 PlaybackStateCompat.Builder playbackStateBuilder = 77 new PlaybackStateCompat.Builder().setActions(mAvailableActions); 78 mPlaybackStateCompat = playbackStateBuilder.build(); 79 updateAvailableActions(); 80 setPlayStatus(playStatus); 81 } 82 getDevice()83 public BluetoothDevice getDevice() { 84 return mDevice; 85 } 86 getId()87 public int getId() { 88 return mId; 89 } 90 getName()91 public String getName() { 92 return mName; 93 } 94 setPlayTime(int playTime)95 public void setPlayTime(int playTime) { 96 mPlayTime = playTime; 97 mPlaybackStateCompat = 98 new PlaybackStateCompat.Builder(mPlaybackStateCompat) 99 .setState(mPlayStatus, mPlayTime, mPlaySpeed) 100 .build(); 101 } 102 getPlayTime()103 public long getPlayTime() { 104 return mPlayTime; 105 } 106 setPlayStatus(int playStatus)107 public void setPlayStatus(int playStatus) { 108 if (mPlayTime != PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN) { 109 mPlayTime += 110 mPlaySpeed 111 * (SystemClock.elapsedRealtime() 112 - mPlaybackStateCompat.getLastPositionUpdateTime()); 113 } 114 mPlayStatus = playStatus; 115 switch (mPlayStatus) { 116 case PlaybackStateCompat.STATE_STOPPED: 117 mPlaySpeed = 0; 118 break; 119 case PlaybackStateCompat.STATE_PLAYING: 120 mPlaySpeed = 1; 121 break; 122 case PlaybackStateCompat.STATE_PAUSED: 123 mPlaySpeed = 0; 124 break; 125 case PlaybackStateCompat.STATE_FAST_FORWARDING: 126 mPlaySpeed = 3; 127 break; 128 case PlaybackStateCompat.STATE_REWINDING: 129 mPlaySpeed = -3; 130 break; 131 } 132 133 mPlaybackStateCompat = 134 new PlaybackStateCompat.Builder(mPlaybackStateCompat) 135 .setState(mPlayStatus, mPlayTime, mPlaySpeed) 136 .build(); 137 } 138 setSupportedPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)139 public void setSupportedPlayerApplicationSettings( 140 PlayerApplicationSettings playerApplicationSettings) { 141 mSupportedPlayerApplicationSettings = playerApplicationSettings; 142 updateAvailableActions(); 143 } 144 setCurrentPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)145 public void setCurrentPlayerApplicationSettings( 146 PlayerApplicationSettings playerApplicationSettings) { 147 Log.d(TAG, "Play application settings changed, settings=" + playerApplicationSettings); 148 mCurrentPlayerApplicationSettings = playerApplicationSettings; 149 MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); 150 session.setRepeatMode( 151 mCurrentPlayerApplicationSettings.getSetting( 152 PlayerApplicationSettings.REPEAT_STATUS)); 153 session.setShuffleMode( 154 mCurrentPlayerApplicationSettings.getSetting( 155 PlayerApplicationSettings.SHUFFLE_STATUS)); 156 } 157 getPlayStatus()158 public int getPlayStatus() { 159 return mPlayStatus; 160 } 161 supportsFeature(int featureId)162 public boolean supportsFeature(int featureId) { 163 int byteNumber = featureId / 8; 164 byte bitMask = (byte) (1 << (featureId % 8)); 165 return (mPlayerFeatures[byteNumber] & bitMask) == bitMask; 166 } 167 supportsSetting(int settingType, int settingValue)168 public boolean supportsSetting(int settingType, int settingValue) { 169 return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue); 170 } 171 getPlaybackState()172 public PlaybackStateCompat getPlaybackState() { 173 Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime); 174 return mPlaybackStateCompat; 175 } 176 updateCurrentTrack(AvrcpItem update)177 public synchronized void updateCurrentTrack(AvrcpItem update) { 178 if (update != null) { 179 long trackNumber = update.getTrackNumber(); 180 mPlaybackStateCompat = 181 new PlaybackStateCompat.Builder(mPlaybackStateCompat) 182 .setActiveQueueItemId(trackNumber - 1) 183 .build(); 184 } 185 mCurrentTrack = update; 186 } 187 notifyImageDownload(String uuid, Uri imageUri)188 public synchronized boolean notifyImageDownload(String uuid, Uri imageUri) { 189 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 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 = 235 new PlaybackStateCompat.Builder(mPlaybackStateCompat) 236 .setActions(mAvailableActions) 237 .build(); 238 239 Log.d(TAG, "Supported Actions = " + mAvailableActions); 240 } 241 242 @Override toString()243 public String toString() { 244 return "<AvrcpPlayer id=" 245 + mId 246 + " name=" 247 + mName 248 + " track=" 249 + mCurrentTrack 250 + " playState=" 251 + AvrcpControllerUtils.playbackStateCompatToString(mPlaybackStateCompat) 252 + ">"; 253 } 254 255 /** A Builder object for an AvrcpPlayer */ 256 public static class Builder { 257 private BluetoothDevice mDevice = null; 258 private int mPlayerId = AvrcpPlayer.DEFAULT_ID; 259 private String mPlayerName = null; 260 private byte[] mSupportedFeatures = new byte[16]; 261 262 private int mPlayStatus = PlaybackStateCompat.STATE_NONE; 263 264 private AvrcpItem mTrack = null; 265 266 /** 267 * Set the device that this Player came from 268 * 269 * @param device The BleutoothDevice representing the remote device 270 * @return This object, so you can continue building 271 */ setDevice(BluetoothDevice device)272 public Builder setDevice(BluetoothDevice device) { 273 mDevice = device; 274 return this; 275 } 276 277 /** 278 * Set the Player ID for this Player 279 * 280 * @param playerId The ID for this player, defined in AVRCP 6.10.2.1 281 * @return This object, so you can continue building 282 */ setPlayerId(int playerId)283 public Builder setPlayerId(int playerId) { 284 mPlayerId = playerId; 285 return this; 286 } 287 288 /** 289 * Set the name for this Player. This is what users will see when browsing. 290 * 291 * @param name The name for this player, defined in AVRCP 6.10.2.1 292 * @return This object, so you can continue building 293 */ setName(String name)294 public Builder setName(String name) { 295 mPlayerName = name; 296 return this; 297 } 298 299 /** 300 * Set the entire set of supported features for this Player. 301 * 302 * @param supportedFeatures The feature set for this player, defined in AVRCP 6.10.2.1 303 * @return This object, so you can continue building 304 */ setSupportedFeatures(byte[] supportedFeatures)305 public Builder setSupportedFeatures(byte[] supportedFeatures) { 306 mSupportedFeatures = supportedFeatures; 307 return this; 308 } 309 310 /** 311 * Set a single features as supported for this Player. 312 * 313 * @param feature The feature for this player, defined in AVRCP 6.10.2.1 314 * @return This object, so you can continue building 315 */ setSupportedFeature(int feature)316 public Builder setSupportedFeature(int feature) { 317 int byteNumber = feature / 8; 318 byte bitMask = (byte) (1 << (feature % 8)); 319 mSupportedFeatures[byteNumber] = (byte) (mSupportedFeatures[byteNumber] | bitMask); 320 return this; 321 } 322 323 /** 324 * Set the initial play status of the Player. 325 * 326 * @param playStatus The play state for this player as a PlaybackStateCompat.STATE_* value 327 * @return This object, so you can continue building 328 */ setPlayStatus(int playStatus)329 public Builder setPlayStatus(int playStatus) { 330 mPlayStatus = playStatus; 331 return this; 332 } 333 334 /** 335 * Set the initial play status of the Player. 336 * 337 * @param track The initial track for this player 338 * @return This object, so you can continue building 339 */ setCurrentTrack(AvrcpItem track)340 public Builder setCurrentTrack(AvrcpItem track) { 341 mTrack = track; 342 return this; 343 } 344 build()345 public AvrcpPlayer build() { 346 AvrcpPlayer player = 347 new AvrcpPlayer( 348 mDevice, mPlayerId, mPlayerName, mSupportedFeatures, mPlayStatus); 349 player.updateCurrentTrack(mTrack); 350 return player; 351 } 352 } 353 } 354