1 /* 2 * Copyright (C) 2014 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.BluetoothAvrcpController; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothProfile; 23 import android.bluetooth.IBluetoothAvrcpController; 24 import android.content.Intent; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.os.Looper; 28 import android.os.Message; 29 import android.util.Log; 30 31 import com.android.bluetooth.btservice.ProfileService; 32 import com.android.bluetooth.Utils; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.HashMap; 37 38 /** 39 * Provides Bluetooth AVRCP Controller profile, as a service in the Bluetooth application. 40 * @hide 41 */ 42 public class AvrcpControllerService extends ProfileService { 43 private static final boolean DBG = false; 44 private static final String TAG = "AvrcpControllerService"; 45 46 private static final int MESSAGE_SEND_PASS_THROUGH_CMD = 1; 47 48 private AvrcpMessageHandler mHandler; 49 private static AvrcpControllerService sAvrcpControllerService; 50 51 private final ArrayList<BluetoothDevice> mConnectedDevices 52 = new ArrayList<BluetoothDevice>(); 53 54 static { 55 classInitNative(); 56 } 57 58 public AvrcpControllerService() { 59 initNative(); 60 } 61 62 protected String getName() { 63 return TAG; 64 } 65 66 protected IProfileServiceBinder initBinder() { 67 return new BluetoothAvrcpControllerBinder(this); 68 } 69 70 protected boolean start() { 71 HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler"); 72 thread.start(); 73 Looper looper = thread.getLooper(); 74 mHandler = new AvrcpMessageHandler(looper); 75 76 setAvrcpControllerService(this); 77 return true; 78 } 79 80 protected boolean stop() { 81 return true; 82 } 83 84 protected boolean cleanup() { 85 mHandler.removeCallbacksAndMessages(null); 86 Looper looper = mHandler.getLooper(); 87 if (looper != null) { 88 looper.quit(); 89 } 90 91 clearAvrcpControllerService(); 92 93 cleanupNative(); 94 95 return true; 96 } 97 98 //API Methods 99 100 public static synchronized AvrcpControllerService getAvrcpControllerService(){ 101 if (sAvrcpControllerService != null && sAvrcpControllerService.isAvailable()) { 102 if (DBG) Log.d(TAG, "getAvrcpControllerService(): returning " 103 + sAvrcpControllerService); 104 return sAvrcpControllerService; 105 } 106 if (DBG) { 107 if (sAvrcpControllerService == null) { 108 Log.d(TAG, "getAvrcpControllerService(): service is NULL"); 109 } else if (!(sAvrcpControllerService.isAvailable())) { 110 Log.d(TAG,"getAvrcpControllerService(): service is not available"); 111 } 112 } 113 return null; 114 } 115 116 private static synchronized void setAvrcpControllerService(AvrcpControllerService instance) { 117 if (instance != null && instance.isAvailable()) { 118 if (DBG) Log.d(TAG, "setAvrcpControllerService(): set to: " + sAvrcpControllerService); 119 sAvrcpControllerService = instance; 120 } else { 121 if (DBG) { 122 if (sAvrcpControllerService == null) { 123 Log.d(TAG, "setAvrcpControllerService(): service not available"); 124 } else if (!sAvrcpControllerService.isAvailable()) { 125 Log.d(TAG,"setAvrcpControllerService(): service is cleaning up"); 126 } 127 } 128 } 129 } 130 131 private static synchronized void clearAvrcpControllerService() { 132 sAvrcpControllerService = null; 133 } 134 135 public List<BluetoothDevice> getConnectedDevices() { 136 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 137 return mConnectedDevices; 138 } 139 140 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 141 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 142 for (int i = 0; i < states.length; i++) { 143 if (states[i] == BluetoothProfile.STATE_CONNECTED) { 144 return mConnectedDevices; 145 } 146 } 147 return new ArrayList<BluetoothDevice>(); 148 } 149 150 int getConnectionState(BluetoothDevice device) { 151 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 152 return (mConnectedDevices.contains(device) ? BluetoothProfile.STATE_CONNECTED 153 : BluetoothProfile.STATE_DISCONNECTED); 154 } 155 156 public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { 157 if (DBG) Log.d(TAG, "sendPassThroughCmd"); 158 Log.v(TAG, "keyCode: " + keyCode + " keyState: " + keyState); 159 if (device == null) { 160 throw new NullPointerException("device == null"); 161 } 162 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 163 Message msg = mHandler.obtainMessage(MESSAGE_SEND_PASS_THROUGH_CMD, 164 keyCode, keyState, device); 165 mHandler.sendMessage(msg); 166 } 167 168 //Binder object: Must be static class or memory leak may occur 169 private static class BluetoothAvrcpControllerBinder extends IBluetoothAvrcpController.Stub 170 implements IProfileServiceBinder { 171 private AvrcpControllerService mService; 172 173 private AvrcpControllerService getService() { 174 if (!Utils.checkCaller()) { 175 Log.w(TAG,"AVRCP call not allowed for non-active user"); 176 return null; 177 } 178 179 if (mService != null && mService.isAvailable()) { 180 return mService; 181 } 182 return null; 183 } 184 185 BluetoothAvrcpControllerBinder(AvrcpControllerService svc) { 186 mService = svc; 187 } 188 189 public boolean cleanup() { 190 mService = null; 191 return true; 192 } 193 194 public List<BluetoothDevice> getConnectedDevices() { 195 AvrcpControllerService service = getService(); 196 if (service == null) return new ArrayList<BluetoothDevice>(0); 197 return service.getConnectedDevices(); 198 } 199 200 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 201 AvrcpControllerService service = getService(); 202 if (service == null) return new ArrayList<BluetoothDevice>(0); 203 return service.getDevicesMatchingConnectionStates(states); 204 } 205 206 public int getConnectionState(BluetoothDevice device) { 207 AvrcpControllerService service = getService(); 208 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 209 return service.getConnectionState(device); 210 } 211 212 public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { 213 Log.v(TAG,"Binder Call: sendPassThroughCmd"); 214 AvrcpControllerService service = getService(); 215 if (service == null) return; 216 service.sendPassThroughCmd(device, keyCode, keyState); 217 } 218 }; 219 220 /** Handles Avrcp messages. */ 221 private final class AvrcpMessageHandler extends Handler { 222 private AvrcpMessageHandler(Looper looper) { 223 super(looper); 224 } 225 226 @Override 227 public void handleMessage(Message msg) { 228 switch (msg.what) { 229 case MESSAGE_SEND_PASS_THROUGH_CMD: 230 if (DBG) Log.v(TAG, "MESSAGE_SEND_PASS_THROUGH_CMD"); 231 BluetoothDevice device = (BluetoothDevice)msg.obj; 232 sendPassThroughCommandNative(getByteAddress(device), msg.arg1, msg.arg2); 233 break; 234 } 235 } 236 } 237 238 private void onConnectionStateChanged(boolean connected, byte[] address) { 239 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 240 (Utils.getAddressStringFromByte(address)); 241 Log.d(TAG, "onConnectionStateChanged " + connected + " " + device); 242 Intent intent = new Intent(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED); 243 int oldState = (mConnectedDevices.contains(device) ? BluetoothProfile.STATE_CONNECTED 244 : BluetoothProfile.STATE_DISCONNECTED); 245 int newState = (connected ? BluetoothProfile.STATE_CONNECTED 246 : BluetoothProfile.STATE_DISCONNECTED); 247 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, oldState); 248 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 249 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 250 // intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 251 sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 252 if (connected && oldState == BluetoothProfile.STATE_DISCONNECTED) { 253 mConnectedDevices.add(device); 254 } else if (!connected && oldState == BluetoothProfile.STATE_CONNECTED) { 255 mConnectedDevices.remove(device); 256 } 257 } 258 259 private void handlePassthroughRsp(int id, int keyState) { 260 Log.d(TAG, "passthrough response received as: key: " 261 + id + " state: " + keyState); 262 } 263 264 private byte[] getByteAddress(BluetoothDevice device) { 265 return Utils.getBytesFromAddress(device.getAddress()); 266 } 267 268 @Override 269 public void dump(StringBuilder sb) { 270 super.dump(sb); 271 } 272 273 private native static void classInitNative(); 274 private native void initNative(); 275 private native void cleanupNative(); 276 private native boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState); 277 } 278