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.annotation.RequiresPermission;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.util.Log;
23 
24 import com.android.bluetooth.audio_util.ListItem;
25 import com.android.bluetooth.audio_util.Metadata;
26 import com.android.bluetooth.audio_util.PlayStatus;
27 import com.android.bluetooth.audio_util.PlayerInfo;
28 
29 import java.util.List;
30 
31 /**
32  * Native Interface to communicate with the JNI layer. This class should never be passed null
33  * data.
34  */
35 public class AvrcpNativeInterface {
36     private static final String TAG = "AvrcpNativeInterface";
37     private static final boolean DEBUG = true;
38 
39     private static AvrcpNativeInterface sInstance;
40     private AvrcpTargetService mAvrcpService;
41 
42     static {
classInitNative()43         classInitNative();
44     }
45 
getInterface()46     static AvrcpNativeInterface getInterface() {
47         if (sInstance == null) {
48             sInstance = new AvrcpNativeInterface();
49         }
50 
51         return sInstance;
52     }
53 
init(AvrcpTargetService service)54     void init(AvrcpTargetService service) {
55         d("Init AvrcpNativeInterface");
56         mAvrcpService = service;
57         initNative();
58     }
59 
cleanup()60     void cleanup() {
61         d("Cleanup AvrcpNativeInterface");
62         mAvrcpService = null;
63         cleanupNative();
64     }
65 
registerBipServer(int l2capPsm)66     void registerBipServer(int l2capPsm) {
67         d("Register our BIP server at psm=" + l2capPsm);
68         registerBipServerNative(l2capPsm);
69     }
70 
unregisterBipServer()71     void unregisterBipServer() {
72         d("Unregister any BIP server");
73         unregisterBipServerNative();
74     }
75 
setBipClientStatus(String bdaddr, boolean connected)76     void setBipClientStatus(String bdaddr, boolean connected) {
77         setBipClientStatusNative(bdaddr, connected);
78     }
79 
getCurrentSongInfo()80     Metadata getCurrentSongInfo() {
81         d("getCurrentSongInfo");
82         if (mAvrcpService == null) {
83             Log.w(TAG, "getCurrentSongInfo(): AvrcpTargetService is null");
84             return null;
85         }
86 
87         return mAvrcpService.getCurrentSongInfo();
88     }
89 
getPlayStatus()90     PlayStatus getPlayStatus() {
91         d("getPlayStatus");
92         if (mAvrcpService == null) {
93             Log.w(TAG, "getPlayStatus(): AvrcpTargetService is null");
94             return null;
95         }
96 
97         return mAvrcpService.getPlayState();
98     }
99 
sendMediaKeyEvent(int keyEvent, boolean pushed)100     void sendMediaKeyEvent(int keyEvent, boolean pushed) {
101         d("sendMediaKeyEvent: keyEvent=" + keyEvent + " pushed=" + pushed);
102         if (mAvrcpService == null) {
103             Log.w(TAG, "sendMediaKeyEvent(): AvrcpTargetService is null");
104             return;
105         }
106 
107         mAvrcpService.sendMediaKeyEvent(keyEvent, pushed);
108     }
109 
getCurrentMediaId()110     String getCurrentMediaId() {
111         d("getCurrentMediaId");
112         if (mAvrcpService == null) {
113             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
114             return "";
115         }
116 
117         return mAvrcpService.getCurrentMediaId();
118     }
119 
getNowPlayingList()120     List<Metadata> getNowPlayingList() {
121         d("getNowPlayingList");
122         if (mAvrcpService == null) {
123             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
124             return null;
125         }
126 
127         return mAvrcpService.getNowPlayingList();
128     }
129 
getCurrentPlayerId()130     int getCurrentPlayerId() {
131         d("getCurrentPlayerId");
132         if (mAvrcpService == null) {
133             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
134             return -1;
135         }
136 
137         return mAvrcpService.getCurrentPlayerId();
138     }
139 
getMediaPlayerList()140     List<PlayerInfo> getMediaPlayerList() {
141         d("getMediaPlayerList");
142         if (mAvrcpService == null) {
143             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
144             return null;
145         }
146 
147         return mAvrcpService.getMediaPlayerList();
148     }
149 
150     // TODO(apanicke): This shouldn't be named setBrowsedPlayer as it doesn't actually connect
151     // anything internally. It just returns the number of items in the root folder.
setBrowsedPlayer(int playerId)152     void setBrowsedPlayer(int playerId) {
153         d("setBrowsedPlayer: playerId=" + playerId);
154         mAvrcpService.getPlayerRoot(playerId, (a, b, c, d) ->
155                 setBrowsedPlayerResponse(a, b, c, d));
156     }
157 
setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems)158     void setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems) {
159         d("setBrowsedPlayerResponse: playerId=" + playerId
160                 + " success=" + success
161                 + " rootId=" + rootId
162                 + " numItems=" + numItems);
163         setBrowsedPlayerResponseNative(playerId, success, rootId, numItems);
164     }
165 
getFolderItemsRequest(int playerId, String mediaId)166     void getFolderItemsRequest(int playerId, String mediaId) {
167         d("getFolderItemsRequest: playerId=" + playerId + " mediaId=" + mediaId);
168         mAvrcpService.getFolderItems(playerId, mediaId, (a, b) -> getFolderItemsResponse(a, b));
169     }
170 
getFolderItemsResponse(String parentId, List<ListItem> items)171     void getFolderItemsResponse(String parentId, List<ListItem> items) {
172         d("getFolderItemsResponse: parentId=" + parentId + " items.size=" + items.size());
173         getFolderItemsResponseNative(parentId, items);
174     }
175 
sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue)176     void sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue) {
177         d("sendMediaUpdate: metadata=" + metadata
178                 + " playStatus=" + playStatus
179                 + " queue=" + queue);
180         sendMediaUpdateNative(metadata, playStatus, queue);
181     }
182 
sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids)183     void sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids) {
184         d("sendFolderUpdate: availablePlayers=" + availablePlayers
185                 + " addressedPlayers=" + addressedPlayers
186                 + " uids=" + uids);
187         sendFolderUpdateNative(availablePlayers, addressedPlayers, uids);
188     }
189 
playItem(int playerId, boolean nowPlaying, String mediaId)190     void playItem(int playerId, boolean nowPlaying, String mediaId) {
191         d("playItem: playerId=" + playerId + " nowPlaying=" + nowPlaying + " mediaId" + mediaId);
192         if (mAvrcpService == null) {
193             Log.d(TAG, "playItem: AvrcpTargetService is null");
194             return;
195         }
196 
197         mAvrcpService.playItem(playerId, nowPlaying, mediaId);
198     }
199 
connectDevice(String bdaddr)200     boolean connectDevice(String bdaddr) {
201         d("connectDevice: bdaddr=" + bdaddr);
202         return connectDeviceNative(bdaddr);
203     }
204 
disconnectDevice(String bdaddr)205     boolean disconnectDevice(String bdaddr) {
206         d("disconnectDevice: bdaddr=" + bdaddr);
207         return disconnectDeviceNative(bdaddr);
208     }
209 
setActiveDevice(String bdaddr)210     void setActiveDevice(String bdaddr) {
211         BluetoothDevice device =
212                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr.toUpperCase());
213         d("setActiveDevice: device=" + device);
214         mAvrcpService.setActiveDevice(device);
215     }
216 
deviceConnected(String bdaddr, boolean absoluteVolume)217     void deviceConnected(String bdaddr, boolean absoluteVolume) {
218         BluetoothDevice device =
219                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr.toUpperCase());
220         d("deviceConnected: device=" + device + " absoluteVolume=" + absoluteVolume);
221         if (mAvrcpService == null) {
222             Log.w(TAG, "deviceConnected: AvrcpTargetService is null");
223             return;
224         }
225 
226         mAvrcpService.deviceConnected(device, absoluteVolume);
227     }
228 
deviceDisconnected(String bdaddr)229     void deviceDisconnected(String bdaddr) {
230         BluetoothDevice device =
231                 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr.toUpperCase());
232         d("deviceDisconnected: device=" + device);
233         if (mAvrcpService == null) {
234             Log.w(TAG, "deviceDisconnected: AvrcpTargetService is null");
235             return;
236         }
237 
238         mAvrcpService.deviceDisconnected(device);
239     }
240 
sendVolumeChanged(String bdaddr, int volume)241     void sendVolumeChanged(String bdaddr, int volume) {
242         d("sendVolumeChanged: volume=" + volume);
243         sendVolumeChangedNative(bdaddr, volume);
244     }
245 
setVolume(int volume)246     void setVolume(int volume) {
247         d("setVolume: volume=" + volume);
248         if (mAvrcpService == null) {
249             Log.w(TAG, "setVolume: AvrcpTargetService is null");
250             return;
251         }
252 
253         mAvrcpService.setVolume(volume);
254     }
255 
classInitNative()256     private static native void classInitNative();
initNative()257     private native void initNative();
registerBipServerNative(int l2capPsm)258     private native void registerBipServerNative(int l2capPsm);
unregisterBipServerNative()259     private native void unregisterBipServerNative();
sendMediaUpdateNative( boolean trackChanged, boolean playState, boolean playPos)260     private native void sendMediaUpdateNative(
261             boolean trackChanged, boolean playState, boolean playPos);
sendFolderUpdateNative( boolean availablePlayers, boolean addressedPlayers, boolean uids)262     private native void sendFolderUpdateNative(
263             boolean availablePlayers, boolean addressedPlayers, boolean uids);
setBrowsedPlayerResponseNative( int playerId, boolean success, String rootId, int numItems)264     private native void setBrowsedPlayerResponseNative(
265             int playerId, boolean success, String rootId, int numItems);
getFolderItemsResponseNative(String parentId, List<ListItem> list)266     private native void getFolderItemsResponseNative(String parentId, List<ListItem> list);
cleanupNative()267     private native void cleanupNative();
connectDeviceNative(String bdaddr)268     private native boolean connectDeviceNative(String bdaddr);
disconnectDeviceNative(String bdaddr)269     private native boolean disconnectDeviceNative(String bdaddr);
sendVolumeChangedNative(String bdaddr, int volume)270     private native void sendVolumeChangedNative(String bdaddr, int volume);
setBipClientStatusNative(String bdaddr, boolean connected)271     private native void setBipClientStatusNative(String bdaddr, boolean connected);
272 
d(String msg)273     private static void d(String msg) {
274         if (DEBUG) {
275             Log.d(TAG, msg);
276         }
277     }
278 }
279