1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.avrcp;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.util.Log;
22 
23 import java.util.List;
24 
25 /**
26  * Native Interface to communicate with the JNI layer. This class should never be passed null
27  * data.
28  */
29 public class AvrcpNativeInterface {
30     private static final String TAG = "AvrcpNativeInterface";
31     private static final boolean DEBUG = true;
32 
33     private static AvrcpNativeInterface sInstance;
34     private AvrcpTargetService mAvrcpService;
35 
36     static {
classInitNative()37         classInitNative();
38     }
39 
getInterface()40     static AvrcpNativeInterface getInterface() {
41         if (sInstance == null) {
42             sInstance = new AvrcpNativeInterface();
43         }
44 
45         return sInstance;
46     }
47 
init(AvrcpTargetService service)48     void init(AvrcpTargetService service) {
49         d("Init AvrcpNativeInterface");
50         mAvrcpService = service;
51         initNative();
52     }
53 
cleanup()54     void cleanup() {
55         d("Cleanup AvrcpNativeInterface");
56         mAvrcpService = null;
57         cleanupNative();
58     }
59 
getCurrentSongInfo()60     Metadata getCurrentSongInfo() {
61         d("getCurrentSongInfo");
62         if (mAvrcpService == null) {
63             Log.w(TAG, "getCurrentSongInfo(): AvrcpTargetService is null");
64             return null;
65         }
66 
67         return mAvrcpService.getCurrentSongInfo();
68     }
69 
getPlayStatus()70     PlayStatus getPlayStatus() {
71         d("getPlayStatus");
72         if (mAvrcpService == null) {
73             Log.w(TAG, "getPlayStatus(): AvrcpTargetService is null");
74             return null;
75         }
76 
77         return mAvrcpService.getPlayState();
78     }
79 
sendMediaKeyEvent(int keyEvent, boolean pushed)80     void sendMediaKeyEvent(int keyEvent, boolean pushed) {
81         d("sendMediaKeyEvent: keyEvent=" + keyEvent + " pushed=" + pushed);
82         if (mAvrcpService == null) {
83             Log.w(TAG, "sendMediaKeyEvent(): AvrcpTargetService is null");
84             return;
85         }
86 
87         mAvrcpService.sendMediaKeyEvent(keyEvent, pushed);
88     }
89 
getCurrentMediaId()90     String getCurrentMediaId() {
91         d("getCurrentMediaId");
92         if (mAvrcpService == null) {
93             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
94             return "";
95         }
96 
97         return mAvrcpService.getCurrentMediaId();
98     }
99 
getNowPlayingList()100     List<Metadata> getNowPlayingList() {
101         d("getNowPlayingList");
102         if (mAvrcpService == null) {
103             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
104             return null;
105         }
106 
107         return mAvrcpService.getNowPlayingList();
108     }
109 
getCurrentPlayerId()110     int getCurrentPlayerId() {
111         d("getCurrentPlayerId");
112         if (mAvrcpService == null) {
113             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
114             return -1;
115         }
116 
117         return mAvrcpService.getCurrentPlayerId();
118     }
119 
getMediaPlayerList()120     List<PlayerInfo> getMediaPlayerList() {
121         d("getMediaPlayerList");
122         if (mAvrcpService == null) {
123             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
124             return null;
125         }
126 
127         return mAvrcpService.getMediaPlayerList();
128     }
129 
130     // TODO(apanicke): This shouldn't be named setBrowsedPlayer as it doesn't actually connect
131     // anything internally. It just returns the number of items in the root folder.
setBrowsedPlayer(int playerId)132     void setBrowsedPlayer(int playerId) {
133         d("setBrowsedPlayer: playerId=" + playerId);
134         mAvrcpService.getPlayerRoot(playerId, (a, b, c, d) ->
135                 setBrowsedPlayerResponse(a, b, c, d));
136     }
137 
setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems)138     void setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems) {
139         d("setBrowsedPlayerResponse: playerId=" + playerId
140                 + " success=" + success
141                 + " rootId=" + rootId
142                 + " numItems=" + numItems);
143         setBrowsedPlayerResponseNative(playerId, success, rootId, numItems);
144     }
145 
getFolderItemsRequest(int playerId, String mediaId)146     void getFolderItemsRequest(int playerId, String mediaId) {
147         d("getFolderItemsRequest: playerId=" + playerId + " mediaId=" + mediaId);
148         mAvrcpService.getFolderItems(playerId, mediaId, (a, b) -> getFolderItemsResponse(a, b));
149     }
150 
getFolderItemsResponse(String parentId, List<ListItem> items)151     void getFolderItemsResponse(String parentId, List<ListItem> items) {
152         d("getFolderItemsResponse: parentId=" + parentId + " items.size=" + items.size());
153         getFolderItemsResponseNative(parentId, items);
154     }
155 
sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue)156     void sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue) {
157         d("sendMediaUpdate: metadata=" + metadata
158                 + " playStatus=" + playStatus
159                 + " queue=" + queue);
160         sendMediaUpdateNative(metadata, playStatus, queue);
161     }
162 
sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids)163     void sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids) {
164         d("sendFolderUpdate: availablePlayers=" + availablePlayers
165                 + " addressedPlayers=" + addressedPlayers
166                 + " uids=" + uids);
167         sendFolderUpdateNative(availablePlayers, addressedPlayers, uids);
168     }
169 
playItem(int playerId, boolean nowPlaying, String mediaId)170     void playItem(int playerId, boolean nowPlaying, String mediaId) {
171         d("playItem: playerId=" + playerId + " nowPlaying=" + nowPlaying + " mediaId" + mediaId);
172         if (mAvrcpService == null) {
173             Log.d(TAG, "playItem: AvrcpTargetService is null");
174             return;
175         }
176 
177         mAvrcpService.playItem(playerId, nowPlaying, mediaId);
178     }
179 
connectDevice(String bdaddr)180     boolean connectDevice(String bdaddr) {
181         d("connectDevice: bdaddr=" + bdaddr);
182         return connectDeviceNative(bdaddr);
183     }
184 
disconnectDevice(String bdaddr)185     boolean disconnectDevice(String bdaddr) {
186         d("disconnectDevice: bdaddr=" + bdaddr);
187         return disconnectDeviceNative(bdaddr);
188     }
189 
setActiveDevice(String bdaddr)190     void setActiveDevice(String bdaddr) {
191         BluetoothDevice device =
192                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr.toUpperCase());
193         d("setActiveDevice: device=" + device);
194         mAvrcpService.setActiveDevice(device);
195     }
196 
deviceConnected(String bdaddr, boolean absoluteVolume)197     void deviceConnected(String bdaddr, boolean absoluteVolume) {
198         BluetoothDevice device =
199                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr.toUpperCase());
200         d("deviceConnected: device=" + device + " absoluteVolume=" + absoluteVolume);
201         if (mAvrcpService == null) {
202             Log.w(TAG, "deviceConnected: AvrcpTargetService is null");
203             return;
204         }
205 
206         mAvrcpService.deviceConnected(device, absoluteVolume);
207     }
208 
deviceDisconnected(String bdaddr)209     void deviceDisconnected(String bdaddr) {
210         BluetoothDevice device =
211                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr.toUpperCase());
212         d("deviceDisconnected: device=" + device);
213         if (mAvrcpService == null) {
214             Log.w(TAG, "deviceDisconnected: AvrcpTargetService is null");
215             return;
216         }
217 
218         mAvrcpService.deviceDisconnected(device);
219     }
220 
sendVolumeChanged(String bdaddr, int volume)221     void sendVolumeChanged(String bdaddr, int volume) {
222         d("sendVolumeChanged: volume=" + volume);
223         sendVolumeChangedNative(bdaddr, volume);
224     }
225 
setVolume(int volume)226     void setVolume(int volume) {
227         d("setVolume: volume=" + volume);
228         if (mAvrcpService == null) {
229             Log.w(TAG, "setVolume: AvrcpTargetService is null");
230             return;
231         }
232 
233         mAvrcpService.setVolume(volume);
234     }
235 
classInitNative()236     private static native void classInitNative();
initNative()237     private native void initNative();
sendMediaUpdateNative( boolean trackChanged, boolean playState, boolean playPos)238     private native void sendMediaUpdateNative(
239             boolean trackChanged, boolean playState, boolean playPos);
sendFolderUpdateNative( boolean availablePlayers, boolean addressedPlayers, boolean uids)240     private native void sendFolderUpdateNative(
241             boolean availablePlayers, boolean addressedPlayers, boolean uids);
setBrowsedPlayerResponseNative( int playerId, boolean success, String rootId, int numItems)242     private native void setBrowsedPlayerResponseNative(
243             int playerId, boolean success, String rootId, int numItems);
getFolderItemsResponseNative(String parentId, List<ListItem> list)244     private native void getFolderItemsResponseNative(String parentId, List<ListItem> list);
cleanupNative()245     private native void cleanupNative();
connectDeviceNative(String bdaddr)246     private native boolean connectDeviceNative(String bdaddr);
disconnectDeviceNative(String bdaddr)247     private native boolean disconnectDeviceNative(String bdaddr);
sendVolumeChangedNative(String bdaddr, int volume)248     private native void sendVolumeChangedNative(String bdaddr, int volume);
249 
d(String msg)250     private static void d(String msg) {
251         if (DEBUG) {
252             Log.d(TAG, msg);
253         }
254     }
255 }
256