1 /*
2  * Copyright 2023 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.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.support.v4.media.session.PlaybackStateCompat;
22 import android.util.Log;
23 
24 import com.android.internal.annotations.GuardedBy;
25 import com.android.internal.annotations.VisibleForTesting;
26 
27 import java.util.Arrays;
28 import java.util.UUID;
29 
30 /** Provides Bluetooth AVRCP Controller native interface for the AVRCP Controller service */
31 public class AvrcpControllerNativeInterface {
32     static final String TAG = AvrcpControllerNativeInterface.class.getSimpleName();
33 
34     private AvrcpControllerService mAvrcpController;
35 
36     @GuardedBy("INSTANCE_LOCK")
37     private static AvrcpControllerNativeInterface sInstance;
38 
39     private static final Object INSTANCE_LOCK = new Object();
40 
getInstance()41     static AvrcpControllerNativeInterface getInstance() {
42         synchronized (INSTANCE_LOCK) {
43             if (sInstance == null) {
44                 sInstance = new AvrcpControllerNativeInterface();
45             }
46             return sInstance;
47         }
48     }
49 
50     /** Set singleton instance. */
51     @VisibleForTesting
setInstance(AvrcpControllerNativeInterface instance)52     public static void setInstance(AvrcpControllerNativeInterface instance) {
53         synchronized (INSTANCE_LOCK) {
54             sInstance = instance;
55         }
56     }
57 
init(AvrcpControllerService controller)58     void init(AvrcpControllerService controller) {
59         mAvrcpController = controller;
60         initNative();
61     }
62 
cleanup()63     void cleanup() {
64         cleanupNative();
65     }
66 
sendPassThroughCommand(byte[] address, int keyCode, int keyState)67     boolean sendPassThroughCommand(byte[] address, int keyCode, int keyState) {
68         return sendPassThroughCommandNative(address, keyCode, keyState);
69     }
70 
setPlayerApplicationSettingValues( byte[] address, byte numAttrib, byte[] attribIds, byte[] attribVal)71     void setPlayerApplicationSettingValues(
72             byte[] address, byte numAttrib, byte[] attribIds, byte[] attribVal) {
73         setPlayerApplicationSettingValuesNative(address, numAttrib, attribIds, attribVal);
74     }
75 
sendAbsVolRsp(byte[] address, int absVol, int label)76     void sendAbsVolRsp(byte[] address, int absVol, int label) {
77         sendAbsVolRspNative(address, absVol, label);
78     }
79 
sendRegisterAbsVolRsp(byte[] address, byte rspType, int absVol, int label)80     void sendRegisterAbsVolRsp(byte[] address, byte rspType, int absVol, int label) {
81         sendRegisterAbsVolRspNative(address, rspType, absVol, label);
82     }
83 
getCurrentMetadata(byte[] address)84     void getCurrentMetadata(byte[] address) {
85         getCurrentMetadataNative(address);
86     }
87 
getPlaybackState(byte[] address)88     void getPlaybackState(byte[] address) {
89         getPlaybackStateNative(address);
90     }
91 
getNowPlayingList(byte[] address, int start, int end)92     void getNowPlayingList(byte[] address, int start, int end) {
93         getNowPlayingListNative(address, start, end);
94     }
95 
getFolderList(byte[] address, int start, int end)96     void getFolderList(byte[] address, int start, int end) {
97         getFolderListNative(address, start, end);
98     }
99 
getPlayerList(byte[] address, int start, int end)100     void getPlayerList(byte[] address, int start, int end) {
101         getPlayerListNative(address, start, end);
102     }
103 
changeFolderPath(byte[] address, byte direction, long uid)104     void changeFolderPath(byte[] address, byte direction, long uid) {
105         changeFolderPathNative(address, direction, uid);
106     }
107 
playItem(byte[] address, byte scope, long uid, int uidCounter)108     void playItem(byte[] address, byte scope, long uid, int uidCounter) {
109         playItemNative(address, scope, uid, uidCounter);
110     }
111 
setBrowsedPlayer(byte[] address, int playerId)112     void setBrowsedPlayer(byte[] address, int playerId) {
113         setBrowsedPlayerNative(address, playerId);
114     }
115 
116     /**********************************************************************************************/
117     /*********************************** callbacks from native ************************************/
118     /**********************************************************************************************/
119 
120     // Called by JNI when a device has connected or disconnected.
onConnectionStateChanged( boolean remoteControlConnected, boolean browsingConnected, byte[] address)121     void onConnectionStateChanged(
122             boolean remoteControlConnected, boolean browsingConnected, byte[] address) {
123         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
124         Log.d(
125                 TAG,
126                 "onConnectionStateChanged: "
127                         + (" remoteControlConnected=" + remoteControlConnected)
128                         + (" browsingConnected=" + browsingConnected)
129                         + (" device=" + device));
130 
131         mAvrcpController.onConnectionStateChanged(
132                 remoteControlConnected, browsingConnected, device);
133     }
134 
135     // Called by JNI to notify Avrcp of a remote device's Cover Art PSM
136     @VisibleForTesting
getRcPsm(byte[] address, int psm)137     void getRcPsm(byte[] address, int psm) {
138         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
139         Log.d(TAG, "getRcPsm: device=" + device + " psm=" + psm);
140 
141         mAvrcpController.getRcPsm(device, psm);
142     }
143 
144     // Called by JNI to report remote Player's capabilities
handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen)145     void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen) {
146         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
147         Log.d(TAG, "handlePlayerAppSetting: device=" + device + " rspLen=" + rspLen);
148 
149         mAvrcpController.handlePlayerAppSetting(device, playerAttribRsp, rspLen);
150     }
151 
152     @VisibleForTesting
onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen)153     void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen) {
154         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
155         Log.d(TAG, "onPlayerAppSettingChanged: device=" + device);
156 
157         mAvrcpController.onPlayerAppSettingChanged(device, playerAttribRsp, rspLen);
158     }
159 
160     // Called by JNI when remote wants to set absolute volume.
handleSetAbsVolume(byte[] address, byte absVol, byte label)161     void handleSetAbsVolume(byte[] address, byte absVol, byte label) {
162         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
163         Log.d(TAG, "handleSetAbsVolume: device=" + device);
164 
165         mAvrcpController.handleSetAbsVolume(device, absVol, label);
166     }
167 
168     // Called by JNI when remote wants to receive absolute volume notifications.
handleRegisterNotificationAbsVol(byte[] address, byte label)169     void handleRegisterNotificationAbsVol(byte[] address, byte label) {
170         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
171         Log.d(TAG, "handleRegisterNotificationAbsVol: device=" + device);
172 
173         mAvrcpController.handleRegisterNotificationAbsVol(device, label);
174     }
175 
176     // Called by JNI when a track changes and local AvrcpController is registered for updates.
onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals)177     void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals) {
178         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
179         Log.d(TAG, "onTrackChanged: device=" + device);
180 
181         mAvrcpController.onTrackChanged(device, numAttributes, attributes, attribVals);
182     }
183 
184     // Called by JNI periodically based upon timer to update play position
onPlayPositionChanged(byte[] address, int songLen, int currSongPosition)185     void onPlayPositionChanged(byte[] address, int songLen, int currSongPosition) {
186         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
187         Log.d(TAG, "onPlayPositionChanged: device=" + device + " pos=" + currSongPosition);
188 
189         mAvrcpController.onPlayPositionChanged(device, songLen, currSongPosition);
190     }
191 
192     // Called by JNI on changes of play status
onPlayStatusChanged(byte[] address, byte playStatus)193     void onPlayStatusChanged(byte[] address, byte playStatus) {
194         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
195         Log.d(TAG, "onPlayStatusChanged: device=" + device + " playStatus=" + playStatus);
196 
197         mAvrcpController.onPlayStatusChanged(device, toPlaybackStateFromJni(playStatus));
198     }
199 
200     // Browsing related JNI callbacks.
handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items)201     void handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items) {
202         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
203         Log.d(
204                 TAG,
205                 "handleGetFolderItemsRsp:"
206                         + (" device=" + device)
207                         + (" status=" + status)
208                         + (" NumberOfItems=" + items.length));
209 
210         mAvrcpController.handleGetFolderItemsRsp(device, status, items);
211     }
212 
handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items)213     void handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items) {
214         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
215         Log.d(
216                 TAG,
217                 "handleGetFolderItemsRsp:"
218                         + (" device=" + device)
219                         + (" NumberOfItems=" + items.length));
220 
221         mAvrcpController.handleGetPlayerItemsRsp(device, Arrays.asList(items));
222     }
223 
224     // JNI Helper functions to convert native objects to java.
createFromNativeMediaItem( byte[] address, long uid, int type, String name, int[] attrIds, String[] attrVals)225     static AvrcpItem createFromNativeMediaItem(
226             byte[] address, long uid, int type, String name, int[] attrIds, String[] attrVals) {
227         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
228         Log.d(
229                 TAG,
230                 "createFromNativeMediaItem:"
231                         + (" device=" + device)
232                         + (" uid=" + uid)
233                         + (" type=" + type)
234                         + (" name=" + name)
235                         + (" attrids=" + Arrays.toString(attrIds))
236                         + (" attrVals=" + Arrays.toString(attrVals)));
237 
238         return new AvrcpItem.Builder()
239                 .fromAvrcpAttributeArray(attrIds, attrVals)
240                 .setDevice(device)
241                 .setItemType(AvrcpItem.TYPE_MEDIA)
242                 .setType(type)
243                 .setUid(uid)
244                 .setUuid(UUID.randomUUID().toString())
245                 .setPlayable(true)
246                 .build();
247     }
248 
createFromNativeFolderItem( byte[] address, long uid, int type, String name, int playable)249     static AvrcpItem createFromNativeFolderItem(
250             byte[] address, long uid, int type, String name, int playable) {
251         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
252         Log.d(
253                 TAG,
254                 "createFromNativeFolderItem:"
255                         + (" device=" + device)
256                         + (" uid=" + uid)
257                         + (" type=" + type)
258                         + (" name=" + name)
259                         + (" playable=" + playable));
260 
261         return new AvrcpItem.Builder()
262                 .setDevice(device)
263                 .setItemType(AvrcpItem.TYPE_FOLDER)
264                 .setType(type)
265                 .setUid(uid)
266                 .setUuid(UUID.randomUUID().toString())
267                 .setDisplayableName(name)
268                 .setPlayable(playable == 0x01)
269                 .setBrowsable(true)
270                 .build();
271     }
272 
createFromNativePlayerItem( byte[] address, int id, String name, byte[] transportFlags, int playStatus, int playerType)273     static AvrcpPlayer createFromNativePlayerItem(
274             byte[] address,
275             int id,
276             String name,
277             byte[] transportFlags,
278             int playStatus,
279             int playerType) {
280         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
281         Log.d(
282                 TAG,
283                 "createFromNativePlayerItem:"
284                         + (" device=" + device)
285                         + (" name=" + name)
286                         + (" transportFlags=" + Arrays.toString(transportFlags))
287                         + (" playStatus=" + playStatus)
288                         + (" playerType=" + playerType));
289 
290         return new AvrcpPlayer.Builder()
291                 .setDevice(device)
292                 .setPlayerId(id)
293                 .setSupportedFeatures(transportFlags)
294                 .setName(name)
295                 .setPlayStatus(toPlaybackStateFromJni(playStatus))
296                 .build();
297     }
298 
handleChangeFolderRsp(byte[] address, int count)299     void handleChangeFolderRsp(byte[] address, int count) {
300         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
301         Log.d(TAG, "handleChangeFolderRsp: device=" + device + " count=" + count);
302 
303         mAvrcpController.handleChangeFolderRsp(device, count);
304     }
305 
handleSetBrowsedPlayerRsp(byte[] address, int items, int depth)306     void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) {
307         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
308         Log.d(TAG, "handleSetBrowsedPlayerRsp: device=" + device + " depth=" + depth);
309 
310         mAvrcpController.handleSetBrowsedPlayerRsp(device, items, depth);
311     }
312 
handleSetAddressedPlayerRsp(byte[] address, int status)313     void handleSetAddressedPlayerRsp(byte[] address, int status) {
314         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
315         Log.d(TAG, "handleSetAddressedPlayerRsp device=" + device + " status=" + status);
316 
317         mAvrcpController.handleSetAddressedPlayerRsp(device, status);
318     }
319 
handleAddressedPlayerChanged(byte[] address, int id)320     void handleAddressedPlayerChanged(byte[] address, int id) {
321         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
322         Log.d(TAG, "handleAddressedPlayerChanged: device=" + device + " id=" + id);
323 
324         mAvrcpController.handleAddressedPlayerChanged(device, id);
325     }
326 
handleNowPlayingContentChanged(byte[] address)327     void handleNowPlayingContentChanged(byte[] address) {
328         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
329         Log.d(TAG, "handleNowPlayingContentChanged: device=" + device);
330         mAvrcpController.handleNowPlayingContentChanged(device);
331     }
332 
onAvailablePlayerChanged(byte[] address)333     void onAvailablePlayerChanged(byte[] address) {
334         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
335         Log.d(TAG, "onAvailablePlayerChanged: device=" + device);
336         mAvrcpController.onAvailablePlayerChanged(device);
337     }
338 
339     /*
340      *  Play State Values from JNI
341      */
342     private static final byte JNI_PLAY_STATUS_STOPPED = 0x00;
343     private static final byte JNI_PLAY_STATUS_PLAYING = 0x01;
344     private static final byte JNI_PLAY_STATUS_PAUSED = 0x02;
345     private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03;
346     private static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04;
347 
toPlaybackStateFromJni(int fromJni)348     private static int toPlaybackStateFromJni(int fromJni) {
349         switch (fromJni) {
350             case JNI_PLAY_STATUS_STOPPED:
351                 return PlaybackStateCompat.STATE_STOPPED;
352             case JNI_PLAY_STATUS_PLAYING:
353                 return PlaybackStateCompat.STATE_PLAYING;
354             case JNI_PLAY_STATUS_PAUSED:
355                 return PlaybackStateCompat.STATE_PAUSED;
356             case JNI_PLAY_STATUS_FWD_SEEK:
357                 return PlaybackStateCompat.STATE_FAST_FORWARDING;
358             case JNI_PLAY_STATUS_REV_SEEK:
359                 return PlaybackStateCompat.STATE_REWINDING;
360             default:
361                 return PlaybackStateCompat.STATE_NONE;
362         }
363     }
364 
365     /**********************************************************************************************/
366     /******************************************* native *******************************************/
367     /**********************************************************************************************/
368 
initNative()369     private native void initNative();
370 
cleanupNative()371     private native void cleanupNative();
372 
373     /**
374      * Send button press commands to addressed device
375      *
376      * @param keyCode key code as defined in AVRCP specification
377      * @param keyState 0 = key pressed, 1 = key released
378      * @return command was sent
379      */
sendPassThroughCommandNative(byte[] address, int keyCode, int keyState)380     private native boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState);
381 
382     /**
383      * TODO DELETE: This method is not used Send group navigation commands
384      *
385      * @param keyCode next/previous
386      * @param keyState state
387      * @return command was sent
388      */
sendGroupNavigationCommandNative( byte[] address, int keyCode, int keyState)389     private native boolean sendGroupNavigationCommandNative(
390             byte[] address, int keyCode, int keyState);
391 
392     /**
393      * Change player specific settings such as shuffle
394      *
395      * @param numAttrib number of settings being sent
396      * @param attribIds list of settings to be changed
397      * @param attribVal list of settings values
398      */
setPlayerApplicationSettingValuesNative( byte[] address, byte numAttrib, byte[] attribIds, byte[] attribVal)399     private native void setPlayerApplicationSettingValuesNative(
400             byte[] address, byte numAttrib, byte[] attribIds, byte[] attribVal);
401 
402     /**
403      * Send response to set absolute volume
404      *
405      * @param absVol new volume
406      * @param label label
407      */
sendAbsVolRspNative(byte[] address, int absVol, int label)408     private native void sendAbsVolRspNative(byte[] address, int absVol, int label);
409 
410     /**
411      * Register for any volume level changes
412      *
413      * @param rspType type of response
414      * @param absVol current volume
415      * @param label label
416      */
sendRegisterAbsVolRspNative( byte[] address, byte rspType, int absVol, int label)417     private native void sendRegisterAbsVolRspNative(
418             byte[] address, byte rspType, int absVol, int label);
419 
420     /**
421      * Fetch the current track's metadata
422      *
423      * <p>This method is specifically meant to allow us to fetch image handles that may not have
424      * been sent to us yet, prior to having a BIP client connection. See the AVRCP 1.6+
425      * specification, section 4.1.7, for more details.
426      */
getCurrentMetadataNative(byte[] address)427     private native void getCurrentMetadataNative(byte[] address);
428 
429     /** Fetch the playback state */
getPlaybackStateNative(byte[] address)430     private native void getPlaybackStateNative(byte[] address);
431 
432     /**
433      * Fetch the current now playing list
434      *
435      * @param start first index to retrieve
436      * @param end last index to retrieve
437      */
getNowPlayingListNative(byte[] address, int start, int end)438     private native void getNowPlayingListNative(byte[] address, int start, int end);
439 
440     /**
441      * Fetch the current folder's listing
442      *
443      * @param start first index to retrieve
444      * @param end last index to retrieve
445      */
getFolderListNative(byte[] address, int start, int end)446     private native void getFolderListNative(byte[] address, int start, int end);
447 
448     /**
449      * Fetch the listing of players
450      *
451      * @param start first index to retrieve
452      * @param end last index to retrieve
453      */
getPlayerListNative(byte[] address, int start, int end)454     private native void getPlayerListNative(byte[] address, int start, int end);
455 
456     /**
457      * Change the current browsed folder
458      *
459      * @param direction up/down
460      * @param uid folder unique id
461      */
changeFolderPathNative(byte[] address, byte direction, long uid)462     private native void changeFolderPathNative(byte[] address, byte direction, long uid);
463 
464     /**
465      * Play item with provided uid
466      *
467      * @param scope scope of item to played
468      * @param uid song unique id
469      * @param uidCounter counter
470      */
playItemNative(byte[] address, byte scope, long uid, int uidCounter)471     private native void playItemNative(byte[] address, byte scope, long uid, int uidCounter);
472 
473     /**
474      * Set a specific player for browsing
475      *
476      * @param playerId player number
477      */
setBrowsedPlayerNative(byte[] address, int playerId)478     private native void setBrowsedPlayerNative(byte[] address, int playerId);
479 
480     /**
481      * TODO DELETE: This method is not used Set a specific player for handling playback commands
482      *
483      * @param playerId player number
484      */
setAddressedPlayerNative(byte[] address, int playerId)485     private native void setAddressedPlayerNative(byte[] address, int playerId);
486 }
487