• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.BluetoothAdapter;
20 import android.bluetooth.BluetoothAvrcpPlayerSettings;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothProfile;
23 import android.bluetooth.IBluetoothAvrcpController;
24 import android.media.MediaDescription;
25 import android.media.MediaMetadata;
26 import android.media.browse.MediaBrowser;
27 import android.media.browse.MediaBrowser.MediaItem;
28 import android.media.session.PlaybackState;
29 import android.os.Bundle;
30 import android.os.HandlerThread;
31 import android.os.Message;
32 import android.util.Log;
33 
34 import com.android.bluetooth.Utils;
35 import com.android.bluetooth.btservice.ProfileService;
36 
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.UUID;
41 
42 /**
43  * Provides Bluetooth AVRCP Controller profile, as a service in the Bluetooth application.
44  */
45 public class AvrcpControllerService extends ProfileService {
46     static final String TAG = "AvrcpControllerService";
47     static final boolean DBG = false;
48     static final boolean VDBG = false;
49     /*
50      *  Play State Values from JNI
51      */
52     private static final byte JNI_PLAY_STATUS_STOPPED = 0x00;
53     private static final byte JNI_PLAY_STATUS_PLAYING = 0x01;
54     private static final byte JNI_PLAY_STATUS_PAUSED = 0x02;
55     private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03;
56     private static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04;
57     private static final byte JNI_PLAY_STATUS_ERROR = -1;
58 
59     /*
60      * Browsing Media Item Attribute IDs
61      * This should be kept in sync with BTRC_MEDIA_ATTR_ID_* in bt_rc.h
62      */
63     private static final int JNI_MEDIA_ATTR_ID_INVALID = -1;
64     private static final int JNI_MEDIA_ATTR_ID_TITLE = 0x00000001;
65     private static final int JNI_MEDIA_ATTR_ID_ARTIST = 0x00000002;
66     private static final int JNI_MEDIA_ATTR_ID_ALBUM = 0x00000003;
67     private static final int JNI_MEDIA_ATTR_ID_TRACK_NUM = 0x00000004;
68     private static final int JNI_MEDIA_ATTR_ID_NUM_TRACKS = 0x00000005;
69     private static final int JNI_MEDIA_ATTR_ID_GENRE = 0x00000006;
70     private static final int JNI_MEDIA_ATTR_ID_PLAYING_TIME = 0x00000007;
71 
72     /*
73      * Browsing folder types
74      * This should be kept in sync with BTRC_FOLDER_TYPE_* in bt_rc.h
75      */
76     private static final int JNI_FOLDER_TYPE_TITLES = 0x01;
77     private static final int JNI_FOLDER_TYPE_ALBUMS = 0x02;
78     private static final int JNI_FOLDER_TYPE_ARTISTS = 0x03;
79     private static final int JNI_FOLDER_TYPE_GENRES = 0x04;
80     private static final int JNI_FOLDER_TYPE_PLAYLISTS = 0x05;
81     private static final int JNI_FOLDER_TYPE_YEARS = 0x06;
82 
83     /*
84      * AVRCP Error types as defined in spec. Also they should be in sync with btrc_status_t.
85      * NOTE: Not all may be defined.
86      */
87     private static final int JNI_AVRC_STS_NO_ERROR = 0x04;
88     private static final int JNI_AVRC_INV_RANGE = 0x0b;
89 
90     /**
91      * Intent used to broadcast the change in browse connection state of the AVRCP Controller
92      * profile.
93      *
94      * <p>This intent will have 2 extras:
95      * <ul>
96      *   <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
97      *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
98      * </ul>
99      *
100      * <p>{@link #EXTRA_STATE} can be any of
101      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
102      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
103      *
104      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
105      * receive.
106      */
107     public static final String ACTION_BROWSE_CONNECTION_STATE_CHANGED =
108             "android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED";
109 
110     /**
111      * intent used to broadcast the change in metadata state of playing track on the avrcp
112      * ag.
113      *
114      * <p>this intent will have the two extras:
115      * <ul>
116      *    <li> {@link #extra_metadata} - {@link mediametadata} containing the current metadata.</li>
117      *    <li> {@link #extra_playback} - {@link playbackstate} containing the current playback
118      *    state. </li>
119      * </ul>
120      */
121     public static final String ACTION_TRACK_EVENT =
122             "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT";
123 
124     /**
125      * Intent used to broadcast the change of folder list.
126      *
127      * <p>This intent will have the one extra:
128      * <ul>
129      *    <li> {@link #EXTRA_FOLDER_LIST} - array of {@link MediaBrowser#MediaItem}
130      *    containing the folder listing of currently selected folder.
131      * </ul>
132      */
133     public static final String ACTION_FOLDER_LIST =
134             "android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST";
135 
136     public static final String EXTRA_FOLDER_LIST =
137             "android.bluetooth.avrcp-controller.profile.extra.FOLDER_LIST";
138 
139     public static final String EXTRA_FOLDER_ID = "com.android.bluetooth.avrcp.EXTRA_FOLDER_ID";
140     public static final String EXTRA_FOLDER_BT_ID =
141             "com.android.bluetooth.avrcp-controller.EXTRA_FOLDER_BT_ID";
142 
143     public static final String EXTRA_METADATA =
144             "android.bluetooth.avrcp-controller.profile.extra.METADATA";
145 
146     public static final String EXTRA_PLAYBACK =
147             "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK";
148 
149     public static final String MEDIA_ITEM_UID_KEY = "media-item-uid-key";
150 
151     /*
152      * KeyCoded for Pass Through Commands
153      */
154     public static final int PASS_THRU_CMD_ID_PLAY = 0x44;
155     public static final int PASS_THRU_CMD_ID_PAUSE = 0x46;
156     public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41;
157     public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42;
158     public static final int PASS_THRU_CMD_ID_STOP = 0x45;
159     public static final int PASS_THRU_CMD_ID_FF = 0x49;
160     public static final int PASS_THRU_CMD_ID_REWIND = 0x48;
161     public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B;
162     public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C;
163 
164     /* Key State Variables */
165     public static final int KEY_STATE_PRESSED = 0;
166     public static final int KEY_STATE_RELEASED = 1;
167 
168     /* Group Navigation Key Codes */
169     public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00;
170     public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01;
171 
172     /* Folder navigation directions
173      * This is borrowed from AVRCP 1.6 spec and must be kept with same values
174      */
175     public static final int FOLDER_NAVIGATION_DIRECTION_UP = 0x00;
176     public static final int FOLDER_NAVIGATION_DIRECTION_DOWN = 0x01;
177 
178     /* Folder/Media Item scopes.
179      * Keep in sync with AVRCP 1.6 sec. 6.10.1
180      */
181     public static final int BROWSE_SCOPE_PLAYER_LIST = 0x00;
182     public static final int BROWSE_SCOPE_VFS = 0x01;
183     public static final int BROWSE_SCOPE_SEARCH = 0x02;
184     public static final int BROWSE_SCOPE_NOW_PLAYING = 0x03;
185 
186     private AvrcpControllerStateMachine mAvrcpCtSm;
187     private static AvrcpControllerService sAvrcpControllerService;
188     // UID size is 8 bytes (AVRCP 1.6 spec)
189     private static final byte[] EMPTY_UID = {0, 0, 0, 0, 0, 0, 0, 0};
190 
191     // We only support one device.
192     private BluetoothDevice mConnectedDevice = null;
193     // If browse is supported (only valid if mConnectedDevice != null).
194     private boolean mBrowseConnected = false;
195     // Caches the current browse folder. If this is null then root is the currently browsed folder
196     // (which also has no UID).
197     private String mCurrentBrowseFolderUID = null;
198 
199     static {
classInitNative()200         classInitNative();
201     }
202 
AvrcpControllerService()203     public AvrcpControllerService() {
204         initNative();
205     }
206 
207     @Override
initBinder()208     protected IProfileServiceBinder initBinder() {
209         return new BluetoothAvrcpControllerBinder(this);
210     }
211 
212     @Override
start()213     protected boolean start() {
214         HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
215         thread.start();
216         mAvrcpCtSm = new AvrcpControllerStateMachine(this);
217         mAvrcpCtSm.start();
218 
219         setAvrcpControllerService(this);
220         return true;
221     }
222 
223     @Override
stop()224     protected boolean stop() {
225         setAvrcpControllerService(null);
226         if (mAvrcpCtSm != null) {
227             mAvrcpCtSm.doQuit();
228         }
229         return true;
230     }
231 
232     //API Methods
233 
getAvrcpControllerService()234     public static synchronized AvrcpControllerService getAvrcpControllerService() {
235         if (sAvrcpControllerService == null) {
236             Log.w(TAG, "getAvrcpControllerService(): service is null");
237             return null;
238         }
239         if (!sAvrcpControllerService.isAvailable()) {
240             Log.w(TAG, "getAvrcpControllerService(): service is not available ");
241             return null;
242         }
243         return sAvrcpControllerService;
244     }
245 
setAvrcpControllerService(AvrcpControllerService instance)246     private static synchronized void setAvrcpControllerService(AvrcpControllerService instance) {
247         if (DBG) {
248             Log.d(TAG, "setAvrcpControllerService(): set to: " + instance);
249         }
250         sAvrcpControllerService = instance;
251     }
252 
getConnectedDevices()253     public synchronized List<BluetoothDevice> getConnectedDevices() {
254         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
255         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
256         if (mConnectedDevice != null) {
257             devices.add(mConnectedDevice);
258         }
259         return devices;
260     }
261 
262     /**
263      * This function only supports STATE_CONNECTED
264      */
getDevicesMatchingConnectionStates(int[] states)265     public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
266         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
267         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
268         for (int i = 0; i < states.length; i++) {
269             if (states[i] == BluetoothProfile.STATE_CONNECTED && mConnectedDevice != null) {
270                 devices.add(mConnectedDevice);
271             }
272         }
273         return devices;
274     }
275 
getConnectionState(BluetoothDevice device)276     public synchronized int getConnectionState(BluetoothDevice device) {
277         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
278         return (mConnectedDevice != null ? BluetoothProfile.STATE_CONNECTED
279                 : BluetoothProfile.STATE_DISCONNECTED);
280     }
281 
sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)282     public synchronized void sendGroupNavigationCmd(BluetoothDevice device, int keyCode,
283             int keyState) {
284         Log.v(TAG, "sendGroupNavigationCmd keyCode: " + keyCode + " keyState: " + keyState);
285         if (device == null) {
286             Log.e(TAG, "sendGroupNavigationCmd device is null");
287         }
288 
289         if (!(device.equals(mConnectedDevice))) {
290             Log.e(TAG, " Device does not match " + device + " connected " + mConnectedDevice);
291             return;
292         }
293         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
294         Message msg = mAvrcpCtSm.obtainMessage(
295                 AvrcpControllerStateMachine.MESSAGE_SEND_GROUP_NAVIGATION_CMD,
296                 keyCode, keyState, device);
297         mAvrcpCtSm.sendMessage(msg);
298     }
299 
sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState)300     public synchronized void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
301         Log.v(TAG, "sendPassThroughCmd keyCode: " + keyCode + " keyState: " + keyState);
302         if (device == null) {
303             Log.e(TAG, "sendPassThroughCmd Device is null");
304             return;
305         }
306 
307         if (!device.equals(mConnectedDevice)) {
308             Log.w(TAG, " Device does not match device " + device + " conn " + mConnectedDevice);
309             return;
310         }
311 
312         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
313         Message msg =
314                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_SEND_PASS_THROUGH_CMD,
315                         keyCode, keyState, device);
316         mAvrcpCtSm.sendMessage(msg);
317     }
318 
startAvrcpUpdates()319     public void startAvrcpUpdates() {
320         mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_START_METADATA_BROADCASTS)
321                 .sendToTarget();
322     }
323 
stopAvrcpUpdates()324     public void stopAvrcpUpdates() {
325         mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_STOP_METADATA_BROADCASTS)
326                 .sendToTarget();
327     }
328 
getMetaData(BluetoothDevice device)329     public synchronized MediaMetadata getMetaData(BluetoothDevice device) {
330         if (DBG) {
331             Log.d(TAG, "getMetaData");
332         }
333         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
334         if (device == null) {
335             Log.e(TAG, "getMetadata device is null");
336             return null;
337         }
338 
339         if (!device.equals(mConnectedDevice)) {
340             return null;
341         }
342         return mAvrcpCtSm.getCurrentMetaData();
343     }
344 
getPlaybackState(BluetoothDevice device)345     public PlaybackState getPlaybackState(BluetoothDevice device) {
346         // Get the cached state by default.
347         return getPlaybackState(device, true);
348     }
349 
350     // cached can be used to force a getPlaybackState command. Useful for PTS testing.
getPlaybackState(BluetoothDevice device, boolean cached)351     public synchronized PlaybackState getPlaybackState(BluetoothDevice device, boolean cached) {
352         if (DBG) {
353             Log.d(TAG, "getPlayBackState device = " + device);
354         }
355 
356         if (device == null) {
357             Log.e(TAG, "getPlaybackState device is null");
358             return null;
359         }
360 
361         if (!device.equals(mConnectedDevice)) {
362             Log.e(TAG, "Device " + device + " does not match connected deivce " + mConnectedDevice);
363             return null;
364 
365         }
366         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
367         return mAvrcpCtSm.getCurrentPlayBackState(cached);
368     }
369 
getPlayerSettings(BluetoothDevice device)370     public synchronized BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
371         if (DBG) {
372             Log.d(TAG, "getPlayerApplicationSetting ");
373         }
374 
375         if (device == null) {
376             Log.e(TAG, "getPlayerSettings device is null");
377             return null;
378         }
379 
380         if (!device.equals(mConnectedDevice)) {
381             Log.e(TAG, "device " + device + " does not match connected device " + mConnectedDevice);
382             return null;
383         }
384 
385         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
386 
387         /* Do nothing */
388         return null;
389     }
390 
setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)391     public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
392         if (DBG) {
393             Log.d(TAG, "getPlayerApplicationSetting");
394         }
395 
396         /* Do nothing */
397         return false;
398     }
399 
400     /**
401      * Fetches the list of children for the parentID node.
402      *
403      * This function manages the overall tree for browsing structure.
404      *
405      * Arguments:
406      * device - Device to browse content for.
407      * parentMediaId - ID of the parent that we need to browse content for. Since most
408      * of the players are database unware, fetching a root invalidates all the children.
409      * start - number of item to start scanning from
410      * items - number of items to fetch
411      */
getChildren(BluetoothDevice device, String parentMediaId, int start, int items)412     public synchronized boolean getChildren(BluetoothDevice device, String parentMediaId, int start,
413             int items) {
414         if (DBG) {
415             Log.d(TAG, "getChildren device = " + device + " parent " + parentMediaId);
416         }
417 
418         if (device == null) {
419             Log.e(TAG, "getChildren device is null");
420             return false;
421         }
422 
423         if (!device.equals(mConnectedDevice)) {
424             Log.e(TAG, "getChildren device " + device + " does not match " + mConnectedDevice);
425             return false;
426         }
427 
428         if (!mBrowseConnected) {
429             Log.e(TAG, "getChildren browse not yet connected");
430             return false;
431         }
432 
433         if (!mAvrcpCtSm.isConnected()) {
434             return false;
435         }
436         mAvrcpCtSm.getChildren(parentMediaId, start, items);
437         return true;
438     }
439 
getNowPlayingList(BluetoothDevice device, String id, int start, int items)440     public synchronized boolean getNowPlayingList(BluetoothDevice device, String id, int start,
441             int items) {
442         if (DBG) {
443             Log.d(TAG, "getNowPlayingList device = " + device + " start = " + start + "items = "
444                     + items);
445         }
446 
447         if (device == null) {
448             Log.e(TAG, "getNowPlayingList device is null");
449             return false;
450         }
451 
452         if (!device.equals(mConnectedDevice)) {
453             Log.e(TAG,
454                     "getNowPlayingList device " + device + " does not match " + mConnectedDevice);
455             return false;
456         }
457 
458         if (!mBrowseConnected) {
459             Log.e(TAG, "getNowPlayingList browse not yet connected");
460             return false;
461         }
462 
463         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
464 
465         Message msg =
466                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_NOW_PLAYING_LIST,
467                         start, items, id);
468         mAvrcpCtSm.sendMessage(msg);
469         return true;
470     }
471 
getFolderList(BluetoothDevice device, String id, int start, int items)472     public synchronized boolean getFolderList(BluetoothDevice device, String id, int start,
473             int items) {
474         if (DBG) {
475             Log.d(TAG, "getFolderListing device = " + device + " start = " + start + "items = "
476                     + items);
477         }
478 
479         if (device == null) {
480             Log.e(TAG, "getFolderListing device is null");
481             return false;
482         }
483 
484         if (!device.equals(mConnectedDevice)) {
485             Log.e(TAG, "getFolderListing device " + device + " does not match " + mConnectedDevice);
486             return false;
487         }
488 
489         if (!mBrowseConnected) {
490             Log.e(TAG, "getFolderListing browse not yet connected");
491             return false;
492         }
493 
494         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
495 
496         Message msg =
497                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_FOLDER_LIST, start,
498                         items, id);
499         mAvrcpCtSm.sendMessage(msg);
500         return true;
501     }
502 
getPlayerList(BluetoothDevice device, int start, int items)503     public synchronized boolean getPlayerList(BluetoothDevice device, int start, int items) {
504         if (DBG) {
505             Log.d(TAG,
506                     "getPlayerList device = " + device + " start = " + start + "items = " + items);
507         }
508 
509         if (device == null) {
510             Log.e(TAG, "getPlayerList device is null");
511             return false;
512         }
513 
514         if (!device.equals(mConnectedDevice)) {
515             Log.e(TAG, "getPlayerList device " + device + " does not match " + mConnectedDevice);
516             return false;
517         }
518 
519         if (!mBrowseConnected) {
520             Log.e(TAG, "getPlayerList browse not yet connected");
521             return false;
522         }
523 
524         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
525 
526         Message msg =
527                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_PLAYER_LIST, start,
528                         items);
529         mAvrcpCtSm.sendMessage(msg);
530         return true;
531     }
532 
changeFolderPath(BluetoothDevice device, int direction, String uid, String fid)533     public synchronized boolean changeFolderPath(BluetoothDevice device, int direction, String uid,
534             String fid) {
535         if (DBG) {
536             Log.d(TAG, "changeFolderPath device = " + device + " direction " + direction + " uid "
537                     + uid);
538         }
539 
540         if (device == null) {
541             Log.e(TAG, "changeFolderPath device is null");
542             return false;
543         }
544 
545         if (!device.equals(mConnectedDevice)) {
546             Log.e(TAG, "changeFolderPath device " + device + " does not match " + mConnectedDevice);
547             return false;
548         }
549 
550         if (!mBrowseConnected) {
551             Log.e(TAG, "changeFolderPath browse not yet connected");
552             return false;
553         }
554 
555         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
556 
557         Bundle b = new Bundle();
558         b.putString(EXTRA_FOLDER_ID, fid);
559         b.putString(EXTRA_FOLDER_BT_ID, uid);
560         Message msg =
561                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_CHANGE_FOLDER_PATH,
562                         direction, 0, b);
563         mAvrcpCtSm.sendMessage(msg);
564         return true;
565     }
566 
setBrowsedPlayer(BluetoothDevice device, int id, String fid)567     public synchronized boolean setBrowsedPlayer(BluetoothDevice device, int id, String fid) {
568         if (DBG) {
569             Log.d(TAG, "setBrowsedPlayer device = " + device + " id" + id + " fid " + fid);
570         }
571 
572         if (device == null) {
573             Log.e(TAG, "setBrowsedPlayer device is null");
574             return false;
575         }
576 
577         if (!device.equals(mConnectedDevice)) {
578             Log.e(TAG, "changeFolderPath device " + device + " does not match " + mConnectedDevice);
579             return false;
580         }
581 
582         if (!mBrowseConnected) {
583             Log.e(TAG, "setBrowsedPlayer browse not yet connected");
584             return false;
585         }
586 
587         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
588 
589         Message msg =
590                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_SET_BROWSED_PLAYER, id,
591                         0, fid);
592         mAvrcpCtSm.sendMessage(msg);
593         return true;
594     }
595 
fetchAttrAndPlayItem(BluetoothDevice device, String uid)596     public synchronized void fetchAttrAndPlayItem(BluetoothDevice device, String uid) {
597         if (DBG) {
598             Log.d(TAG, "fetchAttrAndPlayItem device = " + device + " uid " + uid);
599         }
600 
601         if (device == null) {
602             Log.e(TAG, "fetchAttrAndPlayItem device is null");
603             return;
604         }
605 
606         if (!device.equals(mConnectedDevice)) {
607             Log.e(TAG, "fetchAttrAndPlayItem device " + device + " does not match "
608                     + mConnectedDevice);
609             return;
610         }
611 
612         if (!mBrowseConnected) {
613             Log.e(TAG, "fetchAttrAndPlayItem browse not yet connected");
614             return;
615         }
616         mAvrcpCtSm.fetchAttrAndPlayItem(uid);
617     }
618 
619     //Binder object: Must be static class or memory leak may occur
620     private static class BluetoothAvrcpControllerBinder extends IBluetoothAvrcpController.Stub
621             implements IProfileServiceBinder {
622 
623         private AvrcpControllerService mService;
624 
getService()625         private AvrcpControllerService getService() {
626             if (!Utils.checkCaller()) {
627                 Log.w(TAG, "AVRCP call not allowed for non-active user");
628                 return null;
629             }
630 
631             if (mService != null && mService.isAvailable()) {
632                 return mService;
633             }
634             return null;
635         }
636 
BluetoothAvrcpControllerBinder(AvrcpControllerService svc)637         BluetoothAvrcpControllerBinder(AvrcpControllerService svc) {
638             mService = svc;
639         }
640 
641         @Override
cleanup()642         public void cleanup() {
643             mService = null;
644         }
645 
646         @Override
getConnectedDevices()647         public List<BluetoothDevice> getConnectedDevices() {
648             AvrcpControllerService service = getService();
649             if (service == null) {
650                 return new ArrayList<BluetoothDevice>(0);
651             }
652             return service.getConnectedDevices();
653         }
654 
655         @Override
getDevicesMatchingConnectionStates(int[] states)656         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
657             AvrcpControllerService service = getService();
658             if (service == null) {
659                 return new ArrayList<BluetoothDevice>(0);
660             }
661             return service.getDevicesMatchingConnectionStates(states);
662         }
663 
664         @Override
getConnectionState(BluetoothDevice device)665         public int getConnectionState(BluetoothDevice device) {
666             AvrcpControllerService service = getService();
667             if (service == null) {
668                 return BluetoothProfile.STATE_DISCONNECTED;
669             }
670 
671             if (device == null) {
672                 throw new IllegalStateException("Device cannot be null!");
673             }
674 
675             return service.getConnectionState(device);
676         }
677 
678         @Override
sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)679         public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
680             Log.v(TAG, "Binder Call: sendGroupNavigationCmd");
681             AvrcpControllerService service = getService();
682             if (service == null) {
683                 return;
684             }
685 
686             if (device == null) {
687                 throw new IllegalStateException("Device cannot be null!");
688             }
689 
690             service.sendGroupNavigationCmd(device, keyCode, keyState);
691         }
692 
693         @Override
getPlayerSettings(BluetoothDevice device)694         public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
695             Log.v(TAG, "Binder Call: getPlayerApplicationSetting ");
696             AvrcpControllerService service = getService();
697             if (service == null) {
698                 return null;
699             }
700 
701             if (device == null) {
702                 throw new IllegalStateException("Device cannot be null!");
703             }
704 
705             return service.getPlayerSettings(device);
706         }
707 
708         @Override
setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)709         public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
710             Log.v(TAG, "Binder Call: setPlayerApplicationSetting ");
711             AvrcpControllerService service = getService();
712             if (service == null) {
713                 return false;
714             }
715             return service.setPlayerApplicationSetting(plAppSetting);
716         }
717     }
718 
719     // Called by JNI when a passthrough key was received.
handlePassthroughRsp(int id, int keyState, byte[] address)720     private void handlePassthroughRsp(int id, int keyState, byte[] address) {
721         Log.d(TAG,
722                 "passthrough response received as: key: " + id + " state: " + keyState + "address:"
723                         + address);
724     }
725 
handleGroupNavigationRsp(int id, int keyState)726     private void handleGroupNavigationRsp(int id, int keyState) {
727         Log.d(TAG, "group navigation response received as: key: " + id + " state: " + keyState);
728     }
729 
730     // Called by JNI when a device has connected or disconnected.
onConnectionStateChanged(boolean rcConnected, boolean brConnected, byte[] address)731     private synchronized void onConnectionStateChanged(boolean rcConnected, boolean brConnected,
732             byte[] address) {
733         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
734         Log.d(TAG, "onConnectionStateChanged " + rcConnected + " " + brConnected + device
735                 + " conn device " + mConnectedDevice);
736         if (device == null) {
737             Log.e(TAG, "onConnectionStateChanged Device is null");
738             return;
739         }
740 
741         // Adjust the AVRCP connection state.
742         int oldState = (device.equals(mConnectedDevice) ? BluetoothProfile.STATE_CONNECTED
743                 : BluetoothProfile.STATE_DISCONNECTED);
744         int newState = (rcConnected ? BluetoothProfile.STATE_CONNECTED
745                 : BluetoothProfile.STATE_DISCONNECTED);
746 
747         if (rcConnected && oldState == BluetoothProfile.STATE_DISCONNECTED) {
748             /* AVRCPControllerService supports single connection */
749             if (mConnectedDevice != null) {
750                 Log.d(TAG, "A Connection already exists, returning");
751                 return;
752             }
753             mConnectedDevice = device;
754             Message msg = mAvrcpCtSm.obtainMessage(
755                     AvrcpControllerStateMachine.MESSAGE_PROCESS_CONNECTION_CHANGE, newState,
756                     oldState, device);
757             mAvrcpCtSm.sendMessage(msg);
758         } else if (!rcConnected && oldState == BluetoothProfile.STATE_CONNECTED) {
759             mConnectedDevice = null;
760             Message msg = mAvrcpCtSm.obtainMessage(
761                     AvrcpControllerStateMachine.MESSAGE_PROCESS_CONNECTION_CHANGE, newState,
762                     oldState, device);
763             mAvrcpCtSm.sendMessage(msg);
764         }
765 
766         // Adjust the browse connection state. If RC is connected we should have already sent the
767         // connection status out.
768         if (rcConnected && brConnected) {
769             mBrowseConnected = true;
770             Message msg = mAvrcpCtSm.obtainMessage(
771                     AvrcpControllerStateMachine.MESSAGE_PROCESS_BROWSE_CONNECTION_CHANGE);
772             msg.arg1 = 1;
773             msg.obj = device;
774             mAvrcpCtSm.sendMessage(msg);
775         }
776     }
777 
778     // Called by JNI to notify Avrcp of features supported by the Remote device.
getRcFeatures(byte[] address, int features)779     private void getRcFeatures(byte[] address, int features) {
780         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
781         Message msg =
782                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_RC_FEATURES,
783                         features, 0, device);
784         mAvrcpCtSm.sendMessage(msg);
785     }
786 
787     // Called by JNI
setPlayerAppSettingRsp(byte[] address, byte accepted)788     private void setPlayerAppSettingRsp(byte[] address, byte accepted) {
789               /* Do Nothing. */
790     }
791 
792     // Called by JNI when remote wants to receive absolute volume notifications.
handleRegisterNotificationAbsVol(byte[] address, byte label)793     private synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) {
794         Log.d(TAG, "handleRegisterNotificationAbsVol ");
795         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
796         if (device != null && !device.equals(mConnectedDevice)) {
797             Log.e(TAG, "handleRegisterNotificationAbsVol device not found " + address);
798             return;
799         }
800         Message msg = mAvrcpCtSm.obtainMessage(
801                 AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION,
802                 (int) label, 0);
803         mAvrcpCtSm.sendMessage(msg);
804     }
805 
806     // Called by JNI when remote wants to set absolute volume.
handleSetAbsVolume(byte[] address, byte absVol, byte label)807     private synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) {
808         Log.d(TAG, "handleSetAbsVolume ");
809         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
810         if (device != null && !device.equals(mConnectedDevice)) {
811             Log.e(TAG, "handleSetAbsVolume device not found " + address);
812             return;
813         }
814         Message msg = mAvrcpCtSm.obtainMessage(
815                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, absVol, label);
816         mAvrcpCtSm.sendMessage(msg);
817     }
818 
819     // Called by JNI when a track changes and local AvrcpController is registered for updates.
onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals)820     private synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes,
821             String[] attribVals) {
822         if (DBG) {
823             Log.d(TAG, "onTrackChanged");
824         }
825         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
826         if (device != null && !device.equals(mConnectedDevice)) {
827             Log.e(TAG, "onTrackChanged device not found " + address);
828             return;
829         }
830 
831         List<Integer> attrList = new ArrayList<>();
832         for (int attr : attributes) {
833             attrList.add(attr);
834         }
835         List<String> attrValList = Arrays.asList(attribVals);
836         TrackInfo trackInfo = new TrackInfo(attrList, attrValList);
837         if (VDBG) {
838             Log.d(TAG, "onTrackChanged " + trackInfo);
839         }
840         Message msg = mAvrcpCtSm.obtainMessage(
841                 AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED, trackInfo);
842         mAvrcpCtSm.sendMessage(msg);
843     }
844 
845     // Called by JNI periodically based upon timer to update play position
onPlayPositionChanged(byte[] address, int songLen, int currSongPosition)846     private synchronized void onPlayPositionChanged(byte[] address, int songLen,
847             int currSongPosition) {
848         if (DBG) {
849             Log.d(TAG, "onPlayPositionChanged pos " + currSongPosition);
850         }
851         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
852         if (device != null && !device.equals(mConnectedDevice)) {
853             Log.e(TAG, "onPlayPositionChanged not found device not found " + address);
854             return;
855         }
856         Message msg = mAvrcpCtSm.obtainMessage(
857                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_POS_CHANGED,
858                 songLen, currSongPosition);
859         mAvrcpCtSm.sendMessage(msg);
860     }
861 
862     // Called by JNI on changes of play status
onPlayStatusChanged(byte[] address, byte playStatus)863     private synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {
864         if (DBG) {
865             Log.d(TAG, "onPlayStatusChanged " + playStatus);
866         }
867         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
868         if (device != null && !device.equals(mConnectedDevice)) {
869             Log.e(TAG, "onPlayStatusChanged not found device not found " + address);
870             return;
871         }
872         int playbackState = PlaybackState.STATE_NONE;
873         switch (playStatus) {
874             case JNI_PLAY_STATUS_STOPPED:
875                 playbackState = PlaybackState.STATE_STOPPED;
876                 break;
877             case JNI_PLAY_STATUS_PLAYING:
878                 playbackState = PlaybackState.STATE_PLAYING;
879                 break;
880             case JNI_PLAY_STATUS_PAUSED:
881                 playbackState = PlaybackState.STATE_PAUSED;
882                 break;
883             case JNI_PLAY_STATUS_FWD_SEEK:
884                 playbackState = PlaybackState.STATE_FAST_FORWARDING;
885                 break;
886             case JNI_PLAY_STATUS_REV_SEEK:
887                 playbackState = PlaybackState.STATE_FAST_FORWARDING;
888                 break;
889             default:
890                 playbackState = PlaybackState.STATE_NONE;
891         }
892         Message msg = mAvrcpCtSm.obtainMessage(
893                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, playbackState);
894         mAvrcpCtSm.sendMessage(msg);
895     }
896 
897     // Called by JNI to report remote Player's capabilities
handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen)898     private synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp,
899             int rspLen) {
900         if (DBG) {
901             Log.d(TAG, "handlePlayerAppSetting rspLen = " + rspLen);
902         }
903         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
904         if (device != null && !device.equals(mConnectedDevice)) {
905             Log.e(TAG, "handlePlayerAppSetting not found device not found " + address);
906             return;
907         }
908         PlayerApplicationSettings supportedSettings =
909                 PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp);
910         /* Do nothing */
911     }
912 
onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen)913     private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp,
914             int rspLen) {
915         if (DBG) {
916             Log.d(TAG, "onPlayerAppSettingChanged ");
917         }
918         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
919         if (device != null && !device.equals(mConnectedDevice)) {
920             Log.e(TAG, "onPlayerAppSettingChanged not found device not found " + address);
921             return;
922         }
923         PlayerApplicationSettings desiredSettings =
924                 PlayerApplicationSettings.makeSettings(playerAttribRsp);
925         /* Do nothing */
926     }
927 
928     // Browsing related JNI callbacks.
handleGetFolderItemsRsp(int status, MediaItem[] items)929     void handleGetFolderItemsRsp(int status, MediaItem[] items) {
930         if (DBG) {
931             Log.d(TAG, "handleGetFolderItemsRsp called with status " + status + " items "
932                     + items.length + " items.");
933         }
934 
935         if (status == JNI_AVRC_INV_RANGE) {
936             Log.w(TAG, "Sending out of range message.");
937             // Send a special message since this could be used by state machine
938             // to take as a signal that fetch is finished.
939             Message msg = mAvrcpCtSm.obtainMessage(
940                     AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
941             mAvrcpCtSm.sendMessage(msg);
942             return;
943         }
944 
945         for (MediaItem item : items) {
946             if (VDBG) {
947                 Log.d(TAG, "media item: " + item + " uid: " + item.getDescription().getMediaId());
948             }
949         }
950         ArrayList<MediaItem> itemsList = new ArrayList<>();
951         for (MediaItem item : items) {
952             itemsList.add(item);
953         }
954         Message msg = mAvrcpCtSm.obtainMessage(
955                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS, itemsList);
956         mAvrcpCtSm.sendMessage(msg);
957     }
958 
handleGetPlayerItemsRsp(AvrcpPlayer[] items)959     void handleGetPlayerItemsRsp(AvrcpPlayer[] items) {
960         if (DBG) {
961             Log.d(TAG, "handleGetFolderItemsRsp called with " + items.length + " items.");
962         }
963         for (AvrcpPlayer item : items) {
964             if (VDBG) {
965                 Log.d(TAG, "bt player item: " + item);
966             }
967         }
968         List<AvrcpPlayer> itemsList = new ArrayList<>();
969         for (AvrcpPlayer p : items) {
970             itemsList.add(p);
971         }
972 
973         Message msg = mAvrcpCtSm.obtainMessage(
974                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, itemsList);
975         mAvrcpCtSm.sendMessage(msg);
976     }
977 
978     // JNI Helper functions to convert native objects to java.
createFromNativeMediaItem(byte[] uid, int type, String name, int[] attrIds, String[] attrVals)979     MediaItem createFromNativeMediaItem(byte[] uid, int type, String name, int[] attrIds,
980             String[] attrVals) {
981         if (VDBG) {
982             Log.d(TAG, "createFromNativeMediaItem uid: " + uid + " type " + type + " name " + name
983                     + " attrids " + attrIds + " attrVals " + attrVals);
984         }
985         MediaDescription.Builder mdb = new MediaDescription.Builder();
986 
987         Bundle mdExtra = new Bundle();
988         mdExtra.putString(MEDIA_ITEM_UID_KEY, byteUIDToHexString(uid));
989         mdb.setExtras(mdExtra);
990 
991         // Generate a random UUID. We do this since database unaware TGs can send multiple
992         // items with same MEDIA_ITEM_UID_KEY.
993         mdb.setMediaId(UUID.randomUUID().toString());
994 
995         // Concise readable name.
996         mdb.setTitle(name);
997 
998         // We skip the attributes since we can query them using UID for the item above
999         // Also MediaDescription does not give an easy way to provide this unless we pass
1000         // it as an MediaMetadata which is put inside the extras.
1001         return new MediaItem(mdb.build(), MediaItem.FLAG_PLAYABLE);
1002     }
1003 
createFromNativeFolderItem(byte[] uid, int type, String name, int playable)1004     MediaItem createFromNativeFolderItem(byte[] uid, int type, String name, int playable) {
1005         if (VDBG) {
1006             Log.d(TAG, "createFromNativeFolderItem uid: " + uid + " type " + type + " name " + name
1007                     + " playable " + playable);
1008         }
1009         MediaDescription.Builder mdb = new MediaDescription.Builder();
1010 
1011         // Covert the byte to a hex string. The coversion can be done back here to a
1012         // byte array when needed.
1013         Bundle mdExtra = new Bundle();
1014         mdExtra.putString(MEDIA_ITEM_UID_KEY, byteUIDToHexString(uid));
1015         mdb.setExtras(mdExtra);
1016 
1017         // Generate a random UUID. We do this since database unaware TGs can send multiple
1018         // items with same MEDIA_ITEM_UID_KEY.
1019         mdb.setMediaId(UUID.randomUUID().toString());
1020 
1021         // Concise readable name.
1022         mdb.setTitle(name);
1023 
1024         return new MediaItem(mdb.build(), MediaItem.FLAG_BROWSABLE);
1025     }
1026 
createFromNativePlayerItem(int id, String name, byte[] transportFlags, int playStatus, int playerType)1027     AvrcpPlayer createFromNativePlayerItem(int id, String name, byte[] transportFlags,
1028             int playStatus, int playerType) {
1029         if (VDBG) {
1030             Log.d(TAG,
1031                     "createFromNativePlayerItem name: " + name + " transportFlags " + transportFlags
1032                             + " play status " + playStatus + " player type " + playerType);
1033         }
1034         AvrcpPlayer player = new AvrcpPlayer(id, name, 0, playStatus, playerType);
1035         return player;
1036     }
1037 
handleChangeFolderRsp(int count)1038     private void handleChangeFolderRsp(int count) {
1039         if (DBG) {
1040             Log.d(TAG, "handleChangeFolderRsp count: " + count);
1041         }
1042         Message msg =
1043                 mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH,
1044                         count);
1045         mAvrcpCtSm.sendMessage(msg);
1046     }
1047 
handleSetBrowsedPlayerRsp(int items, int depth)1048     private void handleSetBrowsedPlayerRsp(int items, int depth) {
1049         if (DBG) {
1050             Log.d(TAG, "handleSetBrowsedPlayerRsp depth: " + depth);
1051         }
1052         Message msg = mAvrcpCtSm.obtainMessage(
1053                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER, items, depth);
1054         mAvrcpCtSm.sendMessage(msg);
1055     }
1056 
handleSetAddressedPlayerRsp(int status)1057     private void handleSetAddressedPlayerRsp(int status) {
1058         if (DBG) {
1059             Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status);
1060         }
1061         Message msg = mAvrcpCtSm.obtainMessage(
1062                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER);
1063         mAvrcpCtSm.sendMessage(msg);
1064     }
1065 
1066     @Override
dump(StringBuilder sb)1067     public void dump(StringBuilder sb) {
1068         super.dump(sb);
1069         mAvrcpCtSm.dump(sb);
1070     }
1071 
byteUIDToHexString(byte[] uid)1072     public static String byteUIDToHexString(byte[] uid) {
1073         StringBuilder sb = new StringBuilder();
1074         for (byte b : uid) {
1075             sb.append(String.format("%02X", b));
1076         }
1077         return sb.toString();
1078     }
1079 
hexStringToByteUID(String uidStr)1080     public static byte[] hexStringToByteUID(String uidStr) {
1081         if (uidStr == null) {
1082             Log.e(TAG, "Null hex string.");
1083             return EMPTY_UID;
1084         } else if (uidStr.length() % 2 == 1) {
1085             // Odd length strings should not be possible.
1086             Log.e(TAG, "Odd length hex string " + uidStr);
1087             return EMPTY_UID;
1088         }
1089         int len = uidStr.length();
1090         byte[] data = new byte[len / 2];
1091         for (int i = 0; i < len; i += 2) {
1092             data[i / 2] = (byte) ((Character.digit(uidStr.charAt(i), 16) << 4) + Character.digit(
1093                     uidStr.charAt(i + 1), 16));
1094         }
1095         return data;
1096     }
1097 
classInitNative()1098     private static native void classInitNative();
1099 
initNative()1100     private native void initNative();
1101 
cleanupNative()1102     private native void cleanupNative();
1103 
sendPassThroughCommandNative(byte[] address, int keyCode, int keyState)1104     static native boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState);
1105 
sendGroupNavigationCommandNative(byte[] address, int keyCode, int keyState)1106     static native boolean sendGroupNavigationCommandNative(byte[] address, int keyCode,
1107             int keyState);
1108 
setPlayerApplicationSettingValuesNative(byte[] address, byte numAttrib, byte[] atttibIds, byte[] attribVal)1109     static native void setPlayerApplicationSettingValuesNative(byte[] address, byte numAttrib,
1110             byte[] atttibIds, byte[] attribVal);
1111 
1112     /* This api is used to send response to SET_ABS_VOL_CMD */
sendAbsVolRspNative(byte[] address, int absVol, int label)1113     static native void sendAbsVolRspNative(byte[] address, int absVol, int label);
1114 
1115     /* This api is used to inform remote for any volume level changes */
sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol, int label)1116     static native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol,
1117             int label);
1118 
1119     /* API used to fetch the playback state */
getPlaybackStateNative(byte[] address)1120     static native void getPlaybackStateNative(byte[] address);
1121 
1122     /* API used to fetch the current now playing list */
getNowPlayingListNative(byte[] address, int start, int end)1123     static native void getNowPlayingListNative(byte[] address, int start, int end);
1124 
1125     /* API used to fetch the current folder's listing */
getFolderListNative(byte[] address, int start, int end)1126     static native void getFolderListNative(byte[] address, int start, int end);
1127 
1128     /* API used to fetch the listing of players */
getPlayerListNative(byte[] address, int start, int end)1129     static native void getPlayerListNative(byte[] address, int start, int end);
1130 
1131     /* API used to change the folder */
changeFolderPathNative(byte[] address, byte direction, byte[] uid)1132     static native void changeFolderPathNative(byte[] address, byte direction, byte[] uid);
1133 
playItemNative(byte[] address, byte scope, byte[] uid, int uidCounter)1134     static native void playItemNative(byte[] address, byte scope, byte[] uid, int uidCounter);
1135 
setBrowsedPlayerNative(byte[] address, int playerId)1136     static native void setBrowsedPlayerNative(byte[] address, int playerId);
1137 
setAddressedPlayerNative(byte[] address, int playerId)1138     static native void setAddressedPlayerNative(byte[] address, int playerId);
1139 }
1140