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(int volume)221 void sendVolumeChanged(int volume) { 222 d("sendVolumeChanged: volume=" + volume); 223 sendVolumeChangedNative(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(int volume)248 private native void sendVolumeChangedNative(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