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.BluetoothDevice;
20 import android.util.Log;
21 
22 import com.android.bluetooth.Utils;
23 import com.android.bluetooth.audio_util.ListItem;
24 import com.android.bluetooth.audio_util.Metadata;
25 import com.android.bluetooth.audio_util.PlayStatus;
26 import com.android.bluetooth.audio_util.PlayerInfo;
27 import com.android.bluetooth.audio_util.PlayerSettingsManager.PlayerSettingsValues;
28 import com.android.bluetooth.btservice.AdapterService;
29 import com.android.bluetooth.flags.Flags;
30 import com.android.internal.annotations.GuardedBy;
31 import com.android.internal.annotations.VisibleForTesting;
32 
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * Native Interface to communicate with the JNI layer. This class should never be passed null data.
38  */
39 public class AvrcpNativeInterface {
40     private static final String TAG = AvrcpNativeInterface.class.getSimpleName();
41 
42     @GuardedBy("INSTANCE_LOCK")
43     private static AvrcpNativeInterface sInstance;
44 
45     private static final Object INSTANCE_LOCK = new Object();
46 
47     private AvrcpTargetService mAvrcpService;
48     private AdapterService mAdapterService;
49 
AvrcpNativeInterface()50     private AvrcpNativeInterface() {
51         mAdapterService =
52                 Objects.requireNonNull(
53                         AdapterService.getAdapterService(),
54                         "AdapterService cannot be null when AvrcpNativeInterface init");
55     }
56 
getInstance()57     static AvrcpNativeInterface getInstance() {
58         synchronized (INSTANCE_LOCK) {
59             if (sInstance == null) {
60                 sInstance = new AvrcpNativeInterface();
61             }
62         }
63 
64         return sInstance;
65     }
66 
67     /** Set singleton instance. */
68     @VisibleForTesting
setInstance(AvrcpNativeInterface instance)69     public static void setInstance(AvrcpNativeInterface instance) {
70         synchronized (INSTANCE_LOCK) {
71             sInstance = instance;
72         }
73     }
74 
init(AvrcpTargetService service)75     void init(AvrcpTargetService service) {
76         d("Init AvrcpNativeInterface");
77         mAvrcpService = service;
78         initNative();
79     }
80 
cleanup()81     void cleanup() {
82         d("Cleanup AvrcpNativeInterface");
83         mAvrcpService = null;
84         cleanupNative();
85     }
86 
registerBipServer(int l2capPsm)87     void registerBipServer(int l2capPsm) {
88         d("Register our BIP server at psm=" + l2capPsm);
89         registerBipServerNative(l2capPsm);
90     }
91 
unregisterBipServer()92     void unregisterBipServer() {
93         d("Unregister any BIP server");
94         unregisterBipServerNative();
95     }
96 
setBipClientStatus(BluetoothDevice device, boolean connected)97     void setBipClientStatus(BluetoothDevice device, boolean connected) {
98         String identityAddress =
99                 Flags.identityAddressNullIfUnknown()
100                         ? Utils.getBrEdrAddress(device)
101                         : mAdapterService.getIdentityAddress(device.getAddress());
102         setBipClientStatusNative(identityAddress, connected);
103     }
104 
getCurrentSongInfo()105     Metadata getCurrentSongInfo() {
106         d("getCurrentSongInfo");
107         if (mAvrcpService == null) {
108             Log.w(TAG, "getCurrentSongInfo(): AvrcpTargetService is null");
109             return null;
110         }
111 
112         return mAvrcpService.getCurrentSongInfo();
113     }
114 
getPlayStatus()115     PlayStatus getPlayStatus() {
116         d("getPlayStatus");
117         if (mAvrcpService == null) {
118             Log.w(TAG, "getPlayStatus(): AvrcpTargetService is null");
119             return null;
120         }
121 
122         return mAvrcpService.getPlayState();
123     }
124 
sendMediaKeyEvent(int keyEvent, boolean pushed)125     void sendMediaKeyEvent(int keyEvent, boolean pushed) {
126         d("sendMediaKeyEvent: keyEvent=" + keyEvent + " pushed=" + pushed);
127         if (mAvrcpService == null) {
128             Log.w(TAG, "sendMediaKeyEvent(): AvrcpTargetService is null");
129             return;
130         }
131 
132         mAvrcpService.sendMediaKeyEvent(keyEvent, pushed);
133     }
134 
getCurrentMediaId()135     String getCurrentMediaId() {
136         d("getCurrentMediaId");
137         if (mAvrcpService == null) {
138             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
139             return "";
140         }
141 
142         return mAvrcpService.getCurrentMediaId();
143     }
144 
getNowPlayingList()145     List<Metadata> getNowPlayingList() {
146         d("getNowPlayingList");
147         if (mAvrcpService == null) {
148             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
149             return null;
150         }
151 
152         return mAvrcpService.getNowPlayingList();
153     }
154 
getCurrentPlayerId()155     int getCurrentPlayerId() {
156         d("getCurrentPlayerId");
157         if (mAvrcpService == null) {
158             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
159             return -1;
160         }
161 
162         return mAvrcpService.getCurrentPlayerId();
163     }
164 
getMediaPlayerList()165     List<PlayerInfo> getMediaPlayerList() {
166         d("getMediaPlayerList");
167         if (mAvrcpService == null) {
168             Log.w(TAG, "getMediaPlayerList(): AvrcpTargetService is null");
169             return null;
170         }
171 
172         return mAvrcpService.getMediaPlayerList();
173     }
174 
175     // TODO(apanicke): This shouldn't be named setBrowsedPlayer as it doesn't actually connect
176     // anything internally. It just returns the number of items in the root folder.
setBrowsedPlayer(int playerId)177     void setBrowsedPlayer(int playerId) {
178         d("setBrowsedPlayer: playerId=" + playerId);
179         mAvrcpService.getPlayerRoot(playerId, (a, b, c, d) -> setBrowsedPlayerResponse(a, b, c, d));
180     }
181 
setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems)182     void setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems) {
183         d(
184                 "setBrowsedPlayerResponse: playerId="
185                         + playerId
186                         + " success="
187                         + success
188                         + " rootId="
189                         + rootId
190                         + " numItems="
191                         + numItems);
192         setBrowsedPlayerResponseNative(playerId, success, rootId, numItems);
193     }
194 
getFolderItemsRequest(int playerId, String mediaId)195     void getFolderItemsRequest(int playerId, String mediaId) {
196         d("getFolderItemsRequest: playerId=" + playerId + " mediaId=" + mediaId);
197         mAvrcpService.getFolderItems(playerId, mediaId, (a, b) -> getFolderItemsResponse(a, b));
198     }
199 
getFolderItemsResponse(String parentId, List<ListItem> items)200     void getFolderItemsResponse(String parentId, List<ListItem> items) {
201         d("getFolderItemsResponse: parentId=" + parentId + " items.size=" + items.size());
202         getFolderItemsResponseNative(parentId, items);
203     }
204 
sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue)205     void sendMediaUpdate(boolean metadata, boolean playStatus, boolean queue) {
206         d(
207                 "sendMediaUpdate: metadata="
208                         + metadata
209                         + " playStatus="
210                         + playStatus
211                         + " queue="
212                         + queue);
213         sendMediaUpdateNative(metadata, playStatus, queue);
214     }
215 
sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids)216     void sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers, boolean uids) {
217         d(
218                 "sendFolderUpdate: availablePlayers="
219                         + availablePlayers
220                         + " addressedPlayers="
221                         + addressedPlayers
222                         + " uids="
223                         + uids);
224         sendFolderUpdateNative(availablePlayers, addressedPlayers, uids);
225     }
226 
playItem(int playerId, boolean nowPlaying, String mediaId)227     void playItem(int playerId, boolean nowPlaying, String mediaId) {
228         d("playItem: playerId=" + playerId + " nowPlaying=" + nowPlaying + " mediaId=" + mediaId);
229         if (mAvrcpService == null) {
230             Log.d(TAG, "playItem: AvrcpTargetService is null");
231             return;
232         }
233 
234         mAvrcpService.playItem(playerId, nowPlaying, mediaId);
235     }
236 
disconnectDevice(BluetoothDevice device)237     boolean disconnectDevice(BluetoothDevice device) {
238         String identityAddress =
239                 Flags.identityAddressNullIfUnknown()
240                         ? Utils.getBrEdrAddress(device)
241                         : mAdapterService.getIdentityAddress(device.getAddress());
242         d("disconnectDevice: identityAddress=" + identityAddress);
243         return disconnectDeviceNative(identityAddress);
244     }
245 
setActiveDevice(String bdaddr)246     void setActiveDevice(String bdaddr) {
247         BluetoothDevice device =
248                 mAdapterService.getDeviceFromByte(Utils.getBytesFromAddress(bdaddr));
249         d("setActiveDevice: device=" + device);
250         mAvrcpService.setActiveDevice(device);
251     }
252 
deviceConnected(String bdaddr, boolean absoluteVolume)253     void deviceConnected(String bdaddr, boolean absoluteVolume) {
254         BluetoothDevice device =
255                 mAdapterService.getDeviceFromByte(Utils.getBytesFromAddress(bdaddr));
256         d("deviceConnected: device=" + device + " absoluteVolume=" + absoluteVolume);
257         if (mAvrcpService == null) {
258             Log.w(TAG, "deviceConnected: AvrcpTargetService is null");
259             return;
260         }
261 
262         mAvrcpService.deviceConnected(device, absoluteVolume);
263     }
264 
deviceDisconnected(String bdaddr)265     void deviceDisconnected(String bdaddr) {
266         BluetoothDevice device =
267                 mAdapterService.getDeviceFromByte(Utils.getBytesFromAddress(bdaddr));
268         d("deviceDisconnected: device=" + device);
269         if (mAvrcpService == null) {
270             Log.w(TAG, "deviceDisconnected: AvrcpTargetService is null");
271             return;
272         }
273 
274         mAvrcpService.deviceDisconnected(device);
275     }
276 
sendVolumeChanged(BluetoothDevice device, int volume)277     void sendVolumeChanged(BluetoothDevice device, int volume) {
278         d("sendVolumeChanged: volume=" + volume);
279         String identityAddress =
280                 Flags.identityAddressNullIfUnknown()
281                         ? Utils.getBrEdrAddress(device)
282                         : mAdapterService.getIdentityAddress(device.getAddress());
283         sendVolumeChangedNative(identityAddress, volume);
284     }
285 
setVolume(int volume)286     void setVolume(int volume) {
287         d("setVolume: volume=" + volume);
288         if (mAvrcpService == null) {
289             Log.w(TAG, "setVolume: AvrcpTargetService is null");
290             return;
291         }
292 
293         mAvrcpService.setVolume(volume);
294     }
295 
296     /** Request from remote to list supported player settings. */
listPlayerSettingsRequest()297     void listPlayerSettingsRequest() {
298         byte[] settingsArray = new byte[2];
299         settingsArray[0] = (byte) PlayerSettingsValues.SETTING_REPEAT;
300         settingsArray[1] = (byte) PlayerSettingsValues.SETTING_SHUFFLE;
301         listPlayerSettingsResponseNative(settingsArray);
302     }
303 
304     /** Request from remote to list supported values for player setting. */
listPlayerSettingValuesRequest(byte settingRequest)305     void listPlayerSettingValuesRequest(byte settingRequest) {
306         byte[] valuesArray;
307         switch (settingRequest) {
308             case (byte) PlayerSettingsValues.SETTING_REPEAT:
309                 valuesArray = new byte[4];
310                 valuesArray[0] = PlayerSettingsValues.STATE_REPEAT_OFF;
311                 valuesArray[1] = PlayerSettingsValues.STATE_REPEAT_SINGLE_TRACK;
312                 valuesArray[2] = PlayerSettingsValues.STATE_REPEAT_ALL_TRACK;
313                 valuesArray[3] = PlayerSettingsValues.STATE_REPEAT_GROUP;
314                 break;
315             case (byte) PlayerSettingsValues.SETTING_SHUFFLE:
316                 valuesArray = new byte[3];
317                 valuesArray[0] = PlayerSettingsValues.STATE_SHUFFLE_OFF;
318                 valuesArray[1] = PlayerSettingsValues.STATE_SHUFFLE_ALL_TRACK;
319                 valuesArray[2] = PlayerSettingsValues.STATE_SHUFFLE_GROUP;
320                 break;
321             default:
322                 // For settings we don't support yet, return only state off.
323                 valuesArray = new byte[1];
324                 valuesArray[0] = PlayerSettingsValues.STATE_DEFAULT_OFF;
325         }
326         listPlayerSettingValuesResponseNative(settingRequest, valuesArray);
327     }
328 
329     /** Request from remote current values for player settings. */
getCurrentPlayerSettingValuesRequest(byte[] settingsRequest)330     void getCurrentPlayerSettingValuesRequest(byte[] settingsRequest) {
331         byte[] valuesArray = new byte[settingsRequest.length];
332         for (int i = 0; i < settingsRequest.length; i++) {
333             switch (settingsRequest[i]) {
334                 case (byte) PlayerSettingsValues.SETTING_REPEAT:
335                     valuesArray[i] = (byte) mAvrcpService.getRepeatMode();
336                     break;
337                 case (byte) PlayerSettingsValues.SETTING_SHUFFLE:
338                     valuesArray[i] = (byte) mAvrcpService.getShuffleMode();
339                     break;
340                 default:
341                     valuesArray[i] = (byte) PlayerSettingsValues.STATE_DEFAULT_OFF;
342                     break;
343             }
344         }
345         getPlayerSettingsResponseNative(settingsRequest, valuesArray);
346     }
347 
348     /** Request from remote to set current values for player settings. */
setPlayerSettingsRequest(byte[] settingsRequest, byte[] valuesRequest)349     void setPlayerSettingsRequest(byte[] settingsRequest, byte[] valuesRequest) {
350         boolean success = true;
351         if (settingsRequest.length != valuesRequest.length) {
352             success = false;
353         } else {
354             for (int i = 0; i < settingsRequest.length; i++) {
355                 if (settingsRequest[i] == (byte) PlayerSettingsValues.SETTING_REPEAT
356                         && !mAvrcpService.setRepeatMode(valuesRequest[i])) {
357                     success = false;
358                 } else if (settingsRequest[i] == (byte) PlayerSettingsValues.SETTING_SHUFFLE
359                         && !mAvrcpService.setShuffleMode(valuesRequest[i])) {
360                     success = false;
361                 }
362             }
363         }
364 
365         setPlayerSettingsResponseNative(success);
366     }
367 
sendPlayerSettings(int repeatMode, int shuffleMode)368     void sendPlayerSettings(int repeatMode, int shuffleMode) {
369         byte[] settingsArray = new byte[2];
370         byte[] valuesArray = new byte[2];
371         settingsArray[0] = (byte) PlayerSettingsValues.SETTING_REPEAT;
372         settingsArray[1] = (byte) PlayerSettingsValues.SETTING_SHUFFLE;
373         valuesArray[0] = (byte) repeatMode;
374         valuesArray[1] = (byte) shuffleMode;
375         sendPlayerSettingsNative(settingsArray, valuesArray);
376     }
377 
initNative()378     private native void initNative();
379 
registerBipServerNative(int l2capPsm)380     private native void registerBipServerNative(int l2capPsm);
381 
unregisterBipServerNative()382     private native void unregisterBipServerNative();
383 
sendMediaUpdateNative( boolean trackChanged, boolean playState, boolean playPos)384     private native void sendMediaUpdateNative(
385             boolean trackChanged, boolean playState, boolean playPos);
386 
sendFolderUpdateNative( boolean availablePlayers, boolean addressedPlayers, boolean uids)387     private native void sendFolderUpdateNative(
388             boolean availablePlayers, boolean addressedPlayers, boolean uids);
389 
setBrowsedPlayerResponseNative( int playerId, boolean success, String rootId, int numItems)390     private native void setBrowsedPlayerResponseNative(
391             int playerId, boolean success, String rootId, int numItems);
392 
getFolderItemsResponseNative(String parentId, List<ListItem> list)393     private native void getFolderItemsResponseNative(String parentId, List<ListItem> list);
394 
cleanupNative()395     private native void cleanupNative();
396 
connectDeviceNative(String bdaddr)397     private native boolean connectDeviceNative(String bdaddr);
398 
disconnectDeviceNative(String bdaddr)399     private native boolean disconnectDeviceNative(String bdaddr);
400 
sendVolumeChangedNative(String bdaddr, int volume)401     private native void sendVolumeChangedNative(String bdaddr, int volume);
402 
setBipClientStatusNative(String bdaddr, boolean connected)403     private native void setBipClientStatusNative(String bdaddr, boolean connected);
404 
listPlayerSettingsResponseNative(byte[] attributes)405     private native void listPlayerSettingsResponseNative(byte[] attributes);
406 
listPlayerSettingValuesResponseNative(byte attribute, byte[] values)407     private native void listPlayerSettingValuesResponseNative(byte attribute, byte[] values);
408 
getPlayerSettingsResponseNative(byte[] attributes, byte[] values)409     private native void getPlayerSettingsResponseNative(byte[] attributes, byte[] values);
410 
setPlayerSettingsResponseNative(boolean success)411     private native void setPlayerSettingsResponseNative(boolean success);
412 
sendPlayerSettingsNative(byte[] attributes, byte[] values)413     private native void sendPlayerSettingsNative(byte[] attributes, byte[] values);
414 
d(String msg)415     private static void d(String msg) {
416         Log.d(TAG, msg);
417     }
418 }
419