Home | History | Annotate | Download | only in avrcp
      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