1 /* 2 * Copyright 2018 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.avrcp; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.util.Log; 21 22 import com.android.bluetooth.Utils; 23 import com.android.bluetooth.audio_util.ListItem; 24 import com.android.bluetooth.audio_util.Metadata; 25 import com.android.bluetooth.audio_util.PlayStatus; 26 import com.android.bluetooth.audio_util.PlayerInfo; 27 import com.android.bluetooth.audio_util.PlayerSettingsManager.PlayerSettingsValues; 28 import com.android.bluetooth.btservice.AdapterService; 29 import com.android.bluetooth.flags.Flags; 30 import com.android.internal.annotations.GuardedBy; 31 import com.android.internal.annotations.VisibleForTesting; 32 33 import java.util.List; 34 import java.util.Objects; 35 36 /** 37 * Native Interface to communicate with the JNI layer. This class should never be passed null data. 38 */ 39 public class AvrcpNativeInterface { 40 private static final String TAG = AvrcpNativeInterface.class.getSimpleName(); 41 42 @GuardedBy("INSTANCE_LOCK") 43 private static AvrcpNativeInterface sInstance; 44 45 private static final Object INSTANCE_LOCK = new Object(); 46 47 private AvrcpTargetService mAvrcpService; 48 private AdapterService mAdapterService; 49 AvrcpNativeInterface()50 private AvrcpNativeInterface() { 51 mAdapterService = 52 Objects.requireNonNull( 53 AdapterService.getAdapterService(), 54 "AdapterService cannot be null when AvrcpNativeInterface init"); 55 } 56 getInstance()57 static AvrcpNativeInterface getInstance() { 58 synchronized (INSTANCE_LOCK) { 59 if (sInstance == null) { 60 sInstance = new AvrcpNativeInterface(); 61 } 62 } 63 64 return sInstance; 65 } 66 67 /** Set singleton instance. */ 68 @VisibleForTesting setInstance(AvrcpNativeInterface instance)69 public static void setInstance(AvrcpNativeInterface instance) { 70 synchronized (INSTANCE_LOCK) { 71 sInstance = instance; 72 } 73 } 74 init(AvrcpTargetService service)75 void init(AvrcpTargetService service) { 76 d("Init AvrcpNativeInterface"); 77 mAvrcpService = service; 78 initNative(); 79 } 80 cleanup()81 void cleanup() { 82 d("Cleanup AvrcpNativeInterface"); 83 mAvrcpService = null; 84 cleanupNative(); 85 } 86 registerBipServer(int l2capPsm)87 void registerBipServer(int l2capPsm) { 88 d("Register our BIP server at psm=" + l2capPsm); 89 registerBipServerNative(l2capPsm); 90 } 91 unregisterBipServer()92 void unregisterBipServer() { 93 d("Unregister any BIP server"); 94 unregisterBipServerNative(); 95 } 96 setBipClientStatus(BluetoothDevice device, boolean connected)97 void setBipClientStatus(BluetoothDevice device, boolean connected) { 98 String identityAddress = 99 Flags.identityAddressNullIfUnknown() 100 ? Utils.getBrEdrAddress(device) 101 : mAdapterService.getIdentityAddress(device.getAddress()); 102 setBipClientStatusNative(identityAddress, connected); 103 } 104 getCurrentSongInfo()105 Metadata getCurrentSongInfo() { 106 d("getCurrentSongInfo"); 107 if (mAvrcpService == null) { 108 Log.w(TAG, "getCurrentSongInfo(): AvrcpTargetService is null"); 109 return null; 110 } 111 112 return mAvrcpService.getCurrentSongInfo(); 113 } 114 getPlayStatus()115 PlayStatus getPlayStatus() { 116 d("getPlayStatus"); 117 if (mAvrcpService == null) { 118 Log.w(TAG, "getPlayStatus(): AvrcpTargetService is null"); 119 return null; 120 } 121 122 return mAvrcpService.getPlayState(); 123 } 124 sendMediaKeyEvent(int keyEvent, boolean pushed)125 void sendMediaKeyEvent(int keyEvent, boolean pushed) { 126 d("sendMediaKeyEvent: keyEvent=" + keyEvent + " pushed=" + pushed); 127 if (mAvrcpService == null) { 128 Log.w(TAG, "sendMediaKeyEvent(): AvrcpTargetService is null"); 129 return; 130 } 131 132 mAvrcpService.sendMediaKeyEvent(keyEvent, pushed); 133 } 134 getCurrentMediaId()135 String getCurrentMediaId() { 136 d("getCurrentMediaId"); 137 if (mAvrcpService == null) { 138 Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null"); 139 return ""; 140 } 141 142 return mAvrcpService.getCurrentMediaId(); 143 } 144 getNowPlayingList()145 List<Metadata> getNowPlayingList() { 146 d("getNowPlayingList"); 147 if (mAvrcpService == null) { 148 Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null"); 149 return null; 150 } 151 152 return mAvrcpService.getNowPlayingList(); 153 } 154 getCurrentPlayerId()155 int getCurrentPlayerId() { 156 d("getCurrentPlayerId"); 157 if (mAvrcpService == null) { 158 Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null"); 159 return -1; 160 } 161 162 return mAvrcpService.getCurrentPlayerId(); 163 } 164 getMediaPlayerList()165 List<PlayerInfo> getMediaPlayerList() { 166 d("getMediaPlayerList"); 167 if (mAvrcpService == null) { 168 Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null"); 169 return null; 170 } 171 172 return mAvrcpService.getMediaPlayerList(); 173 } 174 175 // TODO(apanicke): This shouldn't be named setBrowsedPlayer as it doesn't actually connect 176 // anything internally. It just returns the number of items in the root folder. setBrowsedPlayer(int playerId)177 void setBrowsedPlayer(int playerId) { 178 d("setBrowsedPlayer: playerId=" + playerId); 179 mAvrcpService.getPlayerRoot(playerId, (a, b, c, d) -> setBrowsedPlayerResponse(a, b, c, d)); 180 } 181 setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems)182 void setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems) { 183 d( 184 "setBrowsedPlayerResponse: playerId=" 185 + playerId 186 + " success=" 187 + success 188 + " rootId=" 189 + rootId 190 + " numItems=" 191 + numItems); 192 setBrowsedPlayerResponseNative(playerId, success, rootId, numItems); 193 } 194 getFolderItemsRequest(int playerId, String mediaId)195 void getFolderItemsRequest(int playerId, String mediaId) { 196 d("getFolderItemsRequest: playerId=" + playerId + " mediaId=" + mediaId); 197 mAvrcpService.getFolderItems(playerId, mediaId, (a, b) -> getFolderItemsResponse(a, b)); 198 } 199 getFolderItemsResponse(String parentId, List<ListItem> items)200 void getFolderItemsResponse(String parentId, List<ListItem> items) { 201 d("getFolderItemsResponse: parentId=" + parentId + " items.size=" + items.size()); 202 getFolderItemsResponseNative(parentId, items); 203 } 204 sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue)205 void sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue) { 206 d( 207 "sendMediaUpdate: metadata=" 208 + metadata 209 + " playStatus=" 210 + playStatus 211 + " queue=" 212 + queue); 213 sendMediaUpdateNative(metadata, playStatus, queue); 214 } 215 sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids)216 void sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids) { 217 d( 218 "sendFolderUpdate: availablePlayers=" 219 + availablePlayers 220 + " addressedPlayers=" 221 + addressedPlayers 222 + " uids=" 223 + uids); 224 sendFolderUpdateNative(availablePlayers, addressedPlayers, uids); 225 } 226 playItem(int playerId, boolean nowPlaying, String mediaId)227 void playItem(int playerId, boolean nowPlaying, String mediaId) { 228 d("playItem: playerId=" + playerId + " nowPlaying=" + nowPlaying + " mediaId=" + mediaId); 229 if (mAvrcpService == null) { 230 Log.d(TAG, "playItem: AvrcpTargetService is null"); 231 return; 232 } 233 234 mAvrcpService.playItem(playerId, nowPlaying, mediaId); 235 } 236 disconnectDevice(BluetoothDevice device)237 boolean disconnectDevice(BluetoothDevice device) { 238 String identityAddress = 239 Flags.identityAddressNullIfUnknown() 240 ? Utils.getBrEdrAddress(device) 241 : mAdapterService.getIdentityAddress(device.getAddress()); 242 d("disconnectDevice: identityAddress=" + identityAddress); 243 return disconnectDeviceNative(identityAddress); 244 } 245 setActiveDevice(String bdaddr)246 void setActiveDevice(String bdaddr) { 247 BluetoothDevice device = 248 mAdapterService.getDeviceFromByte(Utils.getBytesFromAddress(bdaddr)); 249 d("setActiveDevice: device=" + device); 250 mAvrcpService.setActiveDevice(device); 251 } 252 deviceConnected(String bdaddr, boolean absoluteVolume)253 void deviceConnected(String bdaddr, boolean absoluteVolume) { 254 BluetoothDevice device = 255 mAdapterService.getDeviceFromByte(Utils.getBytesFromAddress(bdaddr)); 256 d("deviceConnected: device=" + device + " absoluteVolume=" + absoluteVolume); 257 if (mAvrcpService == null) { 258 Log.w(TAG, "deviceConnected: AvrcpTargetService is null"); 259 return; 260 } 261 262 mAvrcpService.deviceConnected(device, absoluteVolume); 263 } 264 deviceDisconnected(String bdaddr)265 void deviceDisconnected(String bdaddr) { 266 BluetoothDevice device = 267 mAdapterService.getDeviceFromByte(Utils.getBytesFromAddress(bdaddr)); 268 d("deviceDisconnected: device=" + device); 269 if (mAvrcpService == null) { 270 Log.w(TAG, "deviceDisconnected: AvrcpTargetService is null"); 271 return; 272 } 273 274 mAvrcpService.deviceDisconnected(device); 275 } 276 sendVolumeChanged(BluetoothDevice device, int volume)277 void sendVolumeChanged(BluetoothDevice device, int volume) { 278 d("sendVolumeChanged: volume=" + volume); 279 String identityAddress = 280 Flags.identityAddressNullIfUnknown() 281 ? Utils.getBrEdrAddress(device) 282 : mAdapterService.getIdentityAddress(device.getAddress()); 283 sendVolumeChangedNative(identityAddress, volume); 284 } 285 setVolume(int volume)286 void setVolume(int volume) { 287 d("setVolume: volume=" + volume); 288 if (mAvrcpService == null) { 289 Log.w(TAG, "setVolume: AvrcpTargetService is null"); 290 return; 291 } 292 293 mAvrcpService.setVolume(volume); 294 } 295 296 /** Request from remote to list supported player settings. */ listPlayerSettingsRequest()297 void listPlayerSettingsRequest() { 298 byte[] settingsArray = new byte[2]; 299 settingsArray[0] = (byte) PlayerSettingsValues.SETTING_REPEAT; 300 settingsArray[1] = (byte) PlayerSettingsValues.SETTING_SHUFFLE; 301 listPlayerSettingsResponseNative(settingsArray); 302 } 303 304 /** Request from remote to list supported values for player setting. */ listPlayerSettingValuesRequest(byte settingRequest)305 void listPlayerSettingValuesRequest(byte settingRequest) { 306 byte[] valuesArray; 307 switch (settingRequest) { 308 case (byte) PlayerSettingsValues.SETTING_REPEAT: 309 valuesArray = new byte[4]; 310 valuesArray[0] = PlayerSettingsValues.STATE_REPEAT_OFF; 311 valuesArray[1] = PlayerSettingsValues.STATE_REPEAT_SINGLE_TRACK; 312 valuesArray[2] = PlayerSettingsValues.STATE_REPEAT_ALL_TRACK; 313 valuesArray[3] = PlayerSettingsValues.STATE_REPEAT_GROUP; 314 break; 315 case (byte) PlayerSettingsValues.SETTING_SHUFFLE: 316 valuesArray = new byte[3]; 317 valuesArray[0] = PlayerSettingsValues.STATE_SHUFFLE_OFF; 318 valuesArray[1] = PlayerSettingsValues.STATE_SHUFFLE_ALL_TRACK; 319 valuesArray[2] = PlayerSettingsValues.STATE_SHUFFLE_GROUP; 320 break; 321 default: 322 // For settings we don't support yet, return only state off. 323 valuesArray = new byte[1]; 324 valuesArray[0] = PlayerSettingsValues.STATE_DEFAULT_OFF; 325 } 326 listPlayerSettingValuesResponseNative(settingRequest, valuesArray); 327 } 328 329 /** Request from remote current values for player settings. */ getCurrentPlayerSettingValuesRequest(byte[] settingsRequest)330 void getCurrentPlayerSettingValuesRequest(byte[] settingsRequest) { 331 byte[] valuesArray = new byte[settingsRequest.length]; 332 for (int i = 0; i < settingsRequest.length; i++) { 333 switch (settingsRequest[i]) { 334 case (byte) PlayerSettingsValues.SETTING_REPEAT: 335 valuesArray[i] = (byte) mAvrcpService.getRepeatMode(); 336 break; 337 case (byte) PlayerSettingsValues.SETTING_SHUFFLE: 338 valuesArray[i] = (byte) mAvrcpService.getShuffleMode(); 339 break; 340 default: 341 valuesArray[i] = (byte) PlayerSettingsValues.STATE_DEFAULT_OFF; 342 break; 343 } 344 } 345 getPlayerSettingsResponseNative(settingsRequest, valuesArray); 346 } 347 348 /** Request from remote to set current values for player settings. */ setPlayerSettingsRequest(byte[] settingsRequest, byte[] valuesRequest)349 void setPlayerSettingsRequest(byte[] settingsRequest, byte[] valuesRequest) { 350 boolean success = true; 351 if (settingsRequest.length != valuesRequest.length) { 352 success = false; 353 } else { 354 for (int i = 0; i < settingsRequest.length; i++) { 355 if (settingsRequest[i] == (byte) PlayerSettingsValues.SETTING_REPEAT 356 && !mAvrcpService.setRepeatMode(valuesRequest[i])) { 357 success = false; 358 } else if (settingsRequest[i] == (byte) PlayerSettingsValues.SETTING_SHUFFLE 359 && !mAvrcpService.setShuffleMode(valuesRequest[i])) { 360 success = false; 361 } 362 } 363 } 364 365 setPlayerSettingsResponseNative(success); 366 } 367 sendPlayerSettings(int repeatMode, int shuffleMode)368 void sendPlayerSettings(int repeatMode, int shuffleMode) { 369 byte[] settingsArray = new byte[2]; 370 byte[] valuesArray = new byte[2]; 371 settingsArray[0] = (byte) PlayerSettingsValues.SETTING_REPEAT; 372 settingsArray[1] = (byte) PlayerSettingsValues.SETTING_SHUFFLE; 373 valuesArray[0] = (byte) repeatMode; 374 valuesArray[1] = (byte) shuffleMode; 375 sendPlayerSettingsNative(settingsArray, valuesArray); 376 } 377 initNative()378 private native void initNative(); 379 registerBipServerNative(int l2capPsm)380 private native void registerBipServerNative(int l2capPsm); 381 unregisterBipServerNative()382 private native void unregisterBipServerNative(); 383 sendMediaUpdateNative( boolean trackChanged, boolean playState, boolean playPos)384 private native void sendMediaUpdateNative( 385 boolean trackChanged, boolean playState, boolean playPos); 386 sendFolderUpdateNative( boolean availablePlayers, boolean addressedPlayers, boolean uids)387 private native void sendFolderUpdateNative( 388 boolean availablePlayers, boolean addressedPlayers, boolean uids); 389 setBrowsedPlayerResponseNative( int playerId, boolean success, String rootId, int numItems)390 private native void setBrowsedPlayerResponseNative( 391 int playerId, boolean success, String rootId, int numItems); 392 getFolderItemsResponseNative(String parentId, List<ListItem> list)393 private native void getFolderItemsResponseNative(String parentId, List<ListItem> list); 394 cleanupNative()395 private native void cleanupNative(); 396 connectDeviceNative(String bdaddr)397 private native boolean connectDeviceNative(String bdaddr); 398 disconnectDeviceNative(String bdaddr)399 private native boolean disconnectDeviceNative(String bdaddr); 400 sendVolumeChangedNative(String bdaddr, int volume)401 private native void sendVolumeChangedNative(String bdaddr, int volume); 402 setBipClientStatusNative(String bdaddr, boolean connected)403 private native void setBipClientStatusNative(String bdaddr, boolean connected); 404 listPlayerSettingsResponseNative(byte[] attributes)405 private native void listPlayerSettingsResponseNative(byte[] attributes); 406 listPlayerSettingValuesResponseNative(byte attribute, byte[] values)407 private native void listPlayerSettingValuesResponseNative(byte attribute, byte[] values); 408 getPlayerSettingsResponseNative(byte[] attributes, byte[] values)409 private native void getPlayerSettingsResponseNative(byte[] attributes, byte[] values); 410 setPlayerSettingsResponseNative(boolean success)411 private native void setPlayerSettingsResponseNative(boolean success); 412 sendPlayerSettingsNative(byte[] attributes, byte[] values)413 private native void sendPlayerSettingsNative(byte[] attributes, byte[] values); 414 d(String msg)415 private static void d(String msg) { 416 Log.d(TAG, msg); 417 } 418 } 419