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 final BluetoothDevice mDevice; 57 private int mPlayStatus = PlaybackStateCompat.STATE_NONE; 58 private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN; 59 private float mPlaySpeed = 1; 60 private final 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 (long) 111 (mPlayTime 112 + mPlaySpeed 113 * (SystemClock.elapsedRealtime() 114 - mPlaybackStateCompat 115 .getLastPositionUpdateTime())); 116 } 117 mPlayStatus = playStatus; 118 switch (mPlayStatus) { 119 case PlaybackStateCompat.STATE_STOPPED: 120 mPlaySpeed = 0; 121 break; 122 case PlaybackStateCompat.STATE_PLAYING: 123 mPlaySpeed = 1; 124 break; 125 case PlaybackStateCompat.STATE_PAUSED: 126 mPlaySpeed = 0; 127 break; 128 case PlaybackStateCompat.STATE_FAST_FORWARDING: 129 mPlaySpeed = 3; 130 break; 131 case PlaybackStateCompat.STATE_REWINDING: 132 mPlaySpeed = -3; 133 break; 134 } 135 136 mPlaybackStateCompat = 137 new PlaybackStateCompat.Builder(mPlaybackStateCompat) 138 .setState(mPlayStatus, mPlayTime, mPlaySpeed) 139 .build(); 140 } 141 setSupportedPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)142 public void setSupportedPlayerApplicationSettings( 143 PlayerApplicationSettings playerApplicationSettings) { 144 mSupportedPlayerApplicationSettings = playerApplicationSettings; 145 updateAvailableActions(); 146 } 147 setCurrentPlayerApplicationSettings( PlayerApplicationSettings playerApplicationSettings)148 public void setCurrentPlayerApplicationSettings( 149 PlayerApplicationSettings playerApplicationSettings) { 150 Log.d(TAG, "Play application settings changed, settings=" + playerApplicationSettings); 151 mCurrentPlayerApplicationSettings = playerApplicationSettings; 152 MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); 153 session.setRepeatMode( 154 mCurrentPlayerApplicationSettings.getSetting( 155 PlayerApplicationSettings.REPEAT_STATUS)); 156 session.setShuffleMode( 157 mCurrentPlayerApplicationSettings.getSetting( 158 PlayerApplicationSettings.SHUFFLE_STATUS)); 159 } 160 getPlayStatus()161 public int getPlayStatus() { 162 return mPlayStatus; 163 } 164 supportsFeature(int featureId)165 public boolean supportsFeature(int featureId) { 166 int byteNumber = featureId / 8; 167 byte bitMask = (byte) (1 << (featureId % 8)); 168 return (mPlayerFeatures[byteNumber] & bitMask) == bitMask; 169 } 170 supportsSetting(int settingType, int settingValue)171 public boolean supportsSetting(int settingType, int settingValue) { 172 return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue); 173 } 174 getPlaybackState()175 public PlaybackStateCompat getPlaybackState() { 176 Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime); 177 return mPlaybackStateCompat; 178 } 179 updateCurrentTrack(AvrcpItem update)180 public synchronized void updateCurrentTrack(AvrcpItem update) { 181 if (update != null) { 182 long trackNumber = update.getTrackNumber(); 183 mPlaybackStateCompat = 184 new PlaybackStateCompat.Builder(mPlaybackStateCompat) 185 .setActiveQueueItemId(trackNumber - 1) 186 .build(); 187 } 188 mCurrentTrack = update; 189 } 190 notifyImageDownload(String uuid, Uri imageUri)191 public synchronized boolean notifyImageDownload(String uuid, Uri imageUri) { 192 Log.d(TAG, "Got an image download -- uuid=" + uuid + ", uri=" + imageUri); 193 if (uuid == null || imageUri == null || mCurrentTrack == null) return false; 194 if (uuid.equals(mCurrentTrack.getCoverArtUuid())) { 195 mCurrentTrack.setCoverArtLocation(imageUri); 196 Log.d(TAG, "Image UUID '" + uuid + "' was added to current track."); 197 return true; 198 } 199 return false; 200 } 201 getCurrentTrack()202 public synchronized AvrcpItem getCurrentTrack() { 203 return mCurrentTrack; 204 } 205 updateAvailableActions()206 private void updateAvailableActions() { 207 mAvailableActions = PlaybackStateCompat.ACTION_PREPARE; 208 if (supportsFeature(FEATURE_PLAY)) { 209 mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY; 210 } 211 if (supportsFeature(FEATURE_STOP)) { 212 mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP; 213 } 214 if (supportsFeature(FEATURE_PAUSE)) { 215 mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE; 216 } 217 if (supportsFeature(FEATURE_REWIND)) { 218 mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND; 219 } 220 if (supportsFeature(FEATURE_FAST_FORWARD)) { 221 mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD; 222 } 223 if (supportsFeature(FEATURE_FORWARD)) { 224 mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT; 225 } 226 if (supportsFeature(FEATURE_PREVIOUS)) { 227 mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; 228 } 229 if (mSupportedPlayerApplicationSettings.supportsSetting( 230 PlayerApplicationSettings.REPEAT_STATUS)) { 231 mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE; 232 } 233 if (mSupportedPlayerApplicationSettings.supportsSetting( 234 PlayerApplicationSettings.SHUFFLE_STATUS)) { 235 mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE; 236 } 237 mPlaybackStateCompat = 238 new PlaybackStateCompat.Builder(mPlaybackStateCompat) 239 .setActions(mAvailableActions) 240 .build(); 241 242 Log.d(TAG, "Supported Actions = " + mAvailableActions); 243 } 244 245 @Override toString()246 public String toString() { 247 return "<AvrcpPlayer id=" 248 + mId 249 + " name=" 250 + mName 251 + " track=" 252 + mCurrentTrack 253 + " playState=" 254 + AvrcpControllerUtils.playbackStateCompatToString(mPlaybackStateCompat) 255 + ">"; 256 } 257 258 /** A Builder object for an AvrcpPlayer */ 259 public static class Builder { 260 private BluetoothDevice mDevice = null; 261 private int mPlayerId = AvrcpPlayer.DEFAULT_ID; 262 private String mPlayerName = null; 263 private byte[] mSupportedFeatures = new byte[16]; 264 265 private int mPlayStatus = PlaybackStateCompat.STATE_NONE; 266 267 private AvrcpItem mTrack = null; 268 269 /** 270 * Set the device that this Player came from 271 * 272 * @param device The BluetoothDevice representing the remote device 273 * @return This object, so you can continue building 274 */ setDevice(BluetoothDevice device)275 public Builder setDevice(BluetoothDevice device) { 276 mDevice = device; 277 return this; 278 } 279 280 /** 281 * Set the Player ID for this Player 282 * 283 * @param playerId The ID for this player, defined in AVRCP 6.10.2.1 284 * @return This object, so you can continue building 285 */ setPlayerId(int playerId)286 public Builder setPlayerId(int playerId) { 287 mPlayerId = playerId; 288 return this; 289 } 290 291 /** 292 * Set the name for this Player. This is what users will see when browsing. 293 * 294 * @param name The name for this player, defined in AVRCP 6.10.2.1 295 * @return This object, so you can continue building 296 */ setName(String name)297 public Builder setName(String name) { 298 mPlayerName = name; 299 return this; 300 } 301 302 /** 303 * Set the entire set of supported features for this Player. 304 * 305 * @param supportedFeatures The feature set for this player, defined in AVRCP 6.10.2.1 306 * @return This object, so you can continue building 307 */ setSupportedFeatures(byte[] supportedFeatures)308 public Builder setSupportedFeatures(byte[] supportedFeatures) { 309 mSupportedFeatures = supportedFeatures; 310 return this; 311 } 312 313 /** 314 * Set a single features as supported for this Player. 315 * 316 * @param feature The feature for this player, defined in AVRCP 6.10.2.1 317 * @return This object, so you can continue building 318 */ setSupportedFeature(int feature)319 public Builder setSupportedFeature(int feature) { 320 int byteNumber = feature / 8; 321 byte bitMask = (byte) (1 << (feature % 8)); 322 mSupportedFeatures[byteNumber] = (byte) (mSupportedFeatures[byteNumber] | bitMask); 323 return this; 324 } 325 326 /** 327 * Set the initial play status of the Player. 328 * 329 * @param playStatus The play state for this player as a PlaybackStateCompat.STATE_* value 330 * @return This object, so you can continue building 331 */ setPlayStatus(int playStatus)332 public Builder setPlayStatus(int playStatus) { 333 mPlayStatus = playStatus; 334 return this; 335 } 336 337 /** 338 * Set the initial play status of the Player. 339 * 340 * @param track The initial track for this player 341 * @return This object, so you can continue building 342 */ setCurrentTrack(AvrcpItem track)343 public Builder setCurrentTrack(AvrcpItem track) { 344 mTrack = track; 345 return this; 346 } 347 build()348 public AvrcpPlayer build() { 349 AvrcpPlayer player = 350 new AvrcpPlayer( 351 mDevice, mPlayerId, mPlayerName, mSupportedFeatures, mPlayStatus); 352 player.updateCurrentTrack(mTrack); 353 return player; 354 } 355 } 356 } 357