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