当前位置:   article > 正文

Android 系统蓝牙 控制手机端音乐暂停 (AVRCP)_android 蓝牙 avrcp

android 蓝牙 avrcp



需求:Android 平板做为一个音响的功能使用。已经移植 BT 的 a2dp sink 的功能。现在需要加上 平板控制手机的声音播放,暂停,音量大小等功能


移植:
    
平台: rk3368 系统 android6.0 


    按网上的资料,http://blog.csdn.net/shichaog/article/details/52182987 。在 Android stdio 里移植,发现
import android.bluetooth.BluetoothAvrcpController;
import android.bluetooth.BluetoothAvrcp;
这两个 import 是找不到。查看源码,其实里面有的。网上查出原因,是因为用了 @hide 来隐藏,不暴露给app

于是想到在系统app 里尝试能否使用。

测试 在系统的源码里  package/app/Bluetooth 里的 src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java 加上
+import android.bluetooth.BluetoothAvrcpController;
+import android.bluetooth.BluetoothAvrcp;
     编译,能够通过。证明系统app 是可以调用的。
 
于是在 A2dpSinkStateMachine.java 加了一个 
+           mAvrcpController.sendPassThroughCmd(device, BluetoothAvrcp.PASSTHROUGH_ID_STOP, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);
+           mAvrcpController.sendPassThroughCmd(device, BluetoothAvrcp.PASSTHROUGH_ID_STOP, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);
来暂停
 
那么问题来了,如何获取 mAvrcpController
 
看例子发现,需要一个 Listener
于是加了
 
 
+    private BluetoothProfile.ServiceListener mAvrcpServiceListener = new BluetoothProfile.ServiceListener(){
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+        Log.d(TAG, "BT profile Service connected");
+            if (profile == BluetoothProfile.AVRCP_CONTROLLER){
+                Log.d(TAG, "AvrcpControllerService connected");
+
+                mAvrcpController = (BluetoothAvrcpController) proxy;
+//                mAvrcpController.setCallback(new AvrcpControllerCallback());
+
+                Log.d(TAG, "Avrcp devices: ");
+                List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices();
+                for (BluetoothDevice device : devices)
+                    Log.d(TAG, " - " + device.getName() + " " + device.getAddress());
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
+                Log.d(TAG, "AvrcpControllerService disconnected");
+                //mAvrcpController.removeCallback();
+                mAvrcpController = null;
+            }
+        }
+    };
+


   然而程序进不了 onServiceConnected ,mAvrcpController 为空
   
   在framework 里加 log 发现 在 frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java 里的 BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener);出了错。怀疑是 服务没有启动。
   
12-12 21:02:16.207  1700  1700 D BluetoothAdapter: wade getProfileProxy12
12-12 21:02:16.207  1700  1700 D BluetoothAdapter: wade new BluetoothAvrcpController
12-12 21:02:16.210  1700  1700 E BluetoothAvrcpController: Could not bind to Bluetooth AVRCP Controller Service with Intent { act=android.bluetooth.IBluetoothAvrcpController }


  怎么启动 avrcp 的服务呢?没有想到很好的办法。比较以前有的一些补丁和源码。发现了问题。
  
--- a/packages/apps/Bluetooth/res/values/config.xml
+++ b/packages/apps/Bluetooth/res/values/config.xml
@@ -26,7 +26,7 @@
     <bool name="pbap_include_photos_in_vcard">false</bool>
     <bool name="pbap_use_profile_for_owner_vcard">true</bool>
     <bool name="profile_supported_map">true</bool>
-    <bool name="profile_supported_avrcp_controller">false</bool>
+    <bool name="profile_supported_avrcp_controller">true</bool>
     <bool name="profile_supported_sap">false</bool>
 
   在修改了这个之后,终于可以暂停手机端的音乐暂停了。
   
   
 总结:
    1. 编译要在源码里,编译系统app ,android stdio 用不了。
2. mAvrcpController 的获取需要加一个 BluetoothProfile.ServiceListener
3.系统本身要修改 config.xml 以打开 BluetoothAvrcpController 服务。
4. 只做到了一播放就暂停。关于场景的工作,(何时暂停,播放,或者按钮暂停播放没有做)

附所有的修改
app :
diff --git a/packages/apps/Bluetooth/res/values/config.xml b/packages/apps/Bluetooth/res/values/config.xml
index 0262064..57c8345 100644
--- a/packages/apps/Bluetooth/res/values/config.xml
+++ b/packages/apps/Bluetooth/res/values/config.xml
@@ -26,7 +26,7 @@
     <bool name="pbap_include_photos_in_vcard">false</bool>
     <bool name="pbap_use_profile_for_owner_vcard">true</bool>
     <bool name="profile_supported_map">true</bool>
-    <bool name="profile_supported_avrcp_controller">false</bool>
+    <bool name="profile_supported_avrcp_controller">true</bool>
     <bool name="profile_supported_sap">false</bool>
 
     <!-- If true, we will require location to be enabled on the device to
diff --git a/packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java b/packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.
index ffe8931..a8bedda 100755
--- a/packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java
+++ b/packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java
@@ -35,6 +35,8 @@ import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetooth;
+import android.bluetooth.BluetoothAvrcpController;
+import android.bluetooth.BluetoothAvrcp;
 import android.content.Context;
 import android.media.AudioFormat;
 import android.media.AudioManager;
@@ -106,6 +108,11 @@ final class A2dpSinkStateMachine extends StateMachine {
     private final WakeLock mWakeLock;
 
     private static final int MSG_CONNECTION_STATE_CHANGED = 0;
+    public static final int AVRC_ID_PAUSE = 0x46;
+    public static final int KEY_STATE_PRESSED = 0;
+    public static final int KEY_STATE_RELEASED = 1;
+    private BluetoothAvrcpController mAvrcpController;
+    private static final String TAG = "AVRCPa2dpsinktest";
 
     // mCurrentDevice is the device connected before the state changes
     // mTargetDevice is the device to be connected
@@ -145,6 +152,7 @@ final class A2dpSinkStateMachine extends StateMachine {
         mService = svc;
         mContext = context;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mAdapter.getProfileProxy(context, mAvrcpServiceListener, BluetoothProfile.AVRCP_CONTROLLER);
 
         initNative();
 
@@ -312,6 +320,9 @@ final class A2dpSinkStateMachine extends StateMachine {
             log("Exit Disconnected: " + getCurrentMessage().what);
         }
 
+
+
+
         // in Disconnected state
         private void processConnectionEvent(int state, BluetoothDevice device) {
             switch (state) {
@@ -670,6 +681,8 @@ final class A2dpSinkStateMachine extends StateMachine {
                     mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                     audioPlay();
+                                       Log.d(TAG, "audioPlay now");
+                    sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_STOP);
                     break;
                 case AUDIO_STATE_REMOTE_SUSPEND:
                     logd("in remote suspend here do nothing");
@@ -687,6 +700,23 @@ final class A2dpSinkStateMachine extends StateMachine {
         }
     }
 
+        private void sendCommand(int keyCode){
+        if (mAvrcpController == null)
+               {
+               Log.d(TAG, "sendCommand mAvrcpController null");
+                       return;
+               }
+            
+
+        List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices();
+        for (BluetoothDevice device : devices){
+            Log.d(TAG, "send command to device: "+ keyCode + device.getName() + " " + device.getAddress());
+            //<B8>÷<BD><B7><A8><CA><C7>Ҫ<U+05F7><D7>ٵ<C4><D6>ص<E3>֮<B6><FE>
+           mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);
+           mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);
+        }
+    }
+               
     private void processAudioConfigEvent(BluetoothAudioConfig audioConfig, BluetoothDevice device) {
         mAudioConfigs.put(device, audioConfig);
                int lastSamprate = currentSamprate;
@@ -926,6 +956,34 @@ final class A2dpSinkStateMachine extends StateMachine {
         }
     };
 
+
+    private BluetoothProfile.ServiceListener mAvrcpServiceListener = new BluetoothProfile.ServiceListener(){
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+        Log.d(TAG, "BT profile Service connected");
+            if (profile == BluetoothProfile.AVRCP_CONTROLLER){
+                Log.d(TAG, "AvrcpControllerService connected");
+
+                mAvrcpController = (BluetoothAvrcpController) proxy;
+//                mAvrcpController.setCallback(new AvrcpControllerCallback());
+
+                Log.d(TAG, "Avrcp devices: ");
+                List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices();
+                for (BluetoothDevice device : devices)
+                    Log.d(TAG, " - " + device.getName() + " " + device.getAddress());
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
+                Log.d(TAG, "AvrcpControllerService disconnected");
+                //mAvrcpController.removeCallback();
+                mAvrcpController = null;
+            }
+        }
+    };
+
            class RecordThread  extends Thread{
         @Override
         public void run() {
diff --git a/packages/apps/Bluetooth/src/com/android/bluetooth/avrcp/AvrcpControllerService.java b/packages/apps/Bluetooth/src/com/android/bluetooth/avrcp/AvrcpControllerSe
index ed426ec..2bf0e30 100644
--- a/packages/apps/Bluetooth/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
+++ b/packages/apps/Bluetooth/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
@@ -40,7 +40,7 @@ import java.util.HashMap;
  * @hide
  */
 public class AvrcpControllerService extends ProfileService {
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
     private static final String TAG = "AvrcpControllerService";
 
     private static final int MESSAGE_SEND_PASS_THROUGH_CMD = 1;




framework:

wade@SuperX:~/work/rk3368_618$ git diff frameworks
diff --git a/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java b/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
index 1f3ff51..ba59f29 100644
--- a/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1805,8 +1805,12 @@ public final class BluetoothAdapter {
      */
     public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
                                    int profile) {
-        if (context == null || listener == null) return false;
-
+        if (context == null || listener == null) 
+        {  
+            Log.d(TAG, "wade getprofileproxy context or listenser is null");
+            return false;
+        }
+            Log.d(TAG, "wade getProfileProxy" + profile);
         if (profile == BluetoothProfile.HEADSET) {
             BluetoothHeadset headset = new BluetoothHeadset(context, listener);
             return true;
@@ -1817,6 +1821,7 @@ public final class BluetoothAdapter {
             BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener);
             return true;
         } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
+            Log.d(TAG, "wade new BluetoothAvrcpController");
             BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener);
             return true;
         } else if (profile == BluetoothProfile.INPUT_DEVICE) {
diff --git a/frameworks/base/core/java/android/bluetooth/BluetoothAvrcpController.java b/frameworks/base/core/java/android/bluetooth/BluetoothAvrcpController.java
index b53a8fc..425fba7 100644
--- a/frameworks/base/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/frameworks/base/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -211,6 +211,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
 
     public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
         if (DBG) Log.d(TAG, "sendPassThroughCmd");
+        Log.d(TAG, "sendPassThroughCmd");
         if (mService != null && isEnabled()) {
             try {
                 mService.sendPassThroughCmd(device, keyCode, keyState);



   
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/134484
推荐阅读
相关标签
  

闽ICP备14008679号