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