• 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.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 {
classInitNative()205         classInitNative();
206     }
207 
AvrcpControllerService()208     public AvrcpControllerService() {
209         initNative();
210     }
211 
getName()212     protected String getName() {
213         return TAG;
214     }
215 
initBinder()216     protected IProfileServiceBinder initBinder() {
217         return new BluetoothAvrcpControllerBinder(this);
218     }
219 
start()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 
stop()230     protected boolean stop() {
231         if (mAvrcpCtSm != null) {
232             mAvrcpCtSm.doQuit();
233         }
234         return true;
235     }
236 
237     //API Methods
238 
getAvrcpControllerService()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 
setAvrcpControllerService(AvrcpControllerService instance)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 
clearAvrcpControllerService()274     private static synchronized void clearAvrcpControllerService() {
275         sAvrcpControllerService = null;
276     }
277 
getConnectedDevices()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      */
getDevicesMatchingConnectionStates(int[] states)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 
getConnectionState(BluetoothDevice device)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 
sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)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 
sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState)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 
startAvrcpUpdates()342     public void startAvrcpUpdates() {
343         mAvrcpCtSm.obtainMessage(
344             AvrcpControllerStateMachine.MESSAGE_START_METADATA_BROADCASTS).sendToTarget();
345     }
346 
stopAvrcpUpdates()347     public void stopAvrcpUpdates() {
348         mAvrcpCtSm.obtainMessage(
349             AvrcpControllerStateMachine.MESSAGE_STOP_METADATA_BROADCASTS).sendToTarget();
350     }
351 
getMetaData(BluetoothDevice device)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 
getPlaybackState(BluetoothDevice device)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.
getPlaybackState(BluetoothDevice device, boolean cached)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 
getPlayerSettings(BluetoothDevice device)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 
setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)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      */
getChildren( BluetoothDevice device, String parentMediaId, int start, int items)435     public synchronized boolean getChildren(
436             BluetoothDevice device, String parentMediaId, int start, int items) {
437         if (DBG) {
438             Log.d(TAG, "getChildren 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 
getNowPlayingList( BluetoothDevice device, String id, int start, int items)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 
getFolderList( BluetoothDevice device, String id, int start, int items)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 
getPlayerList(BluetoothDevice device, int start, int items)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 
changeFolderPath( BluetoothDevice device, int direction, String uid, String fid)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 
setBrowsedPlayer(BluetoothDevice device, int id, String fid)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 
fetchAttrAndPlayItem(BluetoothDevice device, String uid)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 
getService()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 
BluetoothAvrcpControllerBinder(AvrcpControllerService svc)658         BluetoothAvrcpControllerBinder(AvrcpControllerService svc) {
659             mService = svc;
660         }
661 
cleanup()662         public boolean cleanup() {
663             mService = null;
664             return true;
665         }
666 
667         @Override
getConnectedDevices()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
getDevicesMatchingConnectionStates(int[] states)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
getConnectionState(BluetoothDevice device)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
sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState)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
getPlayerSettings(BluetoothDevice device)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
setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting)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.
handlePassthroughRsp(int id, int keyState, byte[] address)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 
handleGroupNavigationRsp(int id, int keyState)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.
onConnectionStateChanged( boolean rc_connected, boolean br_connected, byte[] address)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.
getRcFeatures(byte[] address, int features)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
setPlayerAppSettingRsp(byte[] address, byte accepted)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.
handleRegisterNotificationAbsVol(byte[] address, byte label)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.
handleSetAbsVolume(byte[] address, byte absVol, byte label)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.
onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals)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
onPlayPositionChanged(byte[] address, int songLen, int currSongPosition)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
onPlayStatusChanged(byte[] address, byte playStatus)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
handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen)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 
onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen)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.
handleGetFolderItemsRsp(int status, MediaItem[] items)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 
handleGetPlayerItemsRsp(AvrcpPlayer[] items)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.
createFromNativeMediaItem( byte[] uid, int type, String name, int[] attrIds, String[] attrVals)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 
createFromNativeFolderItem( byte[] uid, int type, String name, int playable)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 
createFromNativePlayerItem( int id, String name, byte[] transportFlags, int playStatus, int playerType)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 
handleChangeFolderRsp(int count)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 
handleSetBrowsedPlayerRsp(int items, int depth)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 
handleSetAddressedPlayerRsp(int status)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
dump(StringBuilder sb)1081     public void dump(StringBuilder sb) {
1082         super.dump(sb);
1083         mAvrcpCtSm.dump(sb);
1084     }
1085 
byteUIDToHexString(byte[] uid)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 
hexStringToByteUID(String uidStr)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 
classInitNative()1112     private native static void classInitNative();
1113 
initNative()1114     private native void initNative();
1115 
cleanupNative()1116     private native void cleanupNative();
1117 
sendPassThroughCommandNative(byte[] address, int keyCode, int keyState)1118     native static boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState);
1119 
sendGroupNavigationCommandNative(byte[] address, int keyCode, int keyState)1120     native static boolean sendGroupNavigationCommandNative(byte[] address, int keyCode,
1121         int keyState);
1122 
setPlayerApplicationSettingValuesNative(byte[] address, byte numAttrib, byte[] atttibIds, byte[] attribVal)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 */
sendAbsVolRspNative(byte[] address, int absVol, int label)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 */
sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol, int label)1130     native static void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol,
1131         int label);
1132 
1133     /* API used to fetch the playback state */
getPlaybackStateNative(byte[] address)1134     native static void getPlaybackStateNative(byte[] address);
1135     /* API used to fetch the current now playing list */
getNowPlayingListNative(byte[] address, byte start, byte end)1136     native static void getNowPlayingListNative(byte[] address, byte start, byte end);
1137     /* API used to fetch the current folder's listing */
getFolderListNative(byte[] address, byte start, byte end)1138     native static void getFolderListNative(byte[] address, byte start, byte end);
1139     /* API used to fetch the listing of players */
getPlayerListNative(byte[] address, byte start, byte end)1140     native static void getPlayerListNative(byte[] address, byte start, byte end);
1141     /* API used to change the folder */
changeFolderPathNative(byte[] address, byte direction, byte[] uid)1142     native static void changeFolderPathNative(byte[] address, byte direction, byte[] uid);
playItemNative( byte[] address, byte scope, byte[] uid, int uidCounter)1143     native static void playItemNative(
1144         byte[] address, byte scope, byte[] uid, int uidCounter);
setBrowsedPlayerNative(byte[] address, int playerId)1145     native static void setBrowsedPlayerNative(byte[] address, int playerId);
setAddressedPlayerNative(byte[] address, int playerId)1146     native static void setAddressedPlayerNative(byte[] address, int playerId);
1147 }
1148