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

本文介绍如何让Android平板作为音响设备,并实现控制手机播放、暂停及调节音量等功能。通过修改系统配置文件和源码,成功实现了基本的音频控制。



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


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


    按网上的资料,http://blog.youkuaiyun.com/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);



   
<think>嗯,用户想设计一个基于51单片机通过蓝牙控制手机播放音乐系统。首先,我需要理清楚系统的基本组成部分。根据用户提到的51单片机、蓝牙模块、音乐播放控制以及手机端,可能需要分成硬件和软件两部分来考虑。 硬件部分,51单片机是核心控制器,常用的型号有STC89C52或AT89S52。蓝牙模块的话,HC-05或者HC-06比较常见,但HC-05支持主从模式,可能更适合双向通信。用户之前的问题里提到了ADC0832,不过这里可能需要的是蓝牙通信而不是模数转换,所以得确认是否需要音频处理。不过用户的需求是控制手机播放音乐,而不是处理音频数据本身,所以可能不需要ADC模块。但用户之前的引用里提到过用ADC转换音乐信号,这里可能存在混淆。需要明确系统是发送控制指令还是传输音频数据。如果是控制播放,比如播放、暂停、切歌,那只需要发送指令,不需要处理音频数据。这样的话,单片机通过蓝牙发送指令给手机,手机上的APP解析指令并执行相应操作。 软件部分,单片机需要编写程序,通过串口与蓝牙模块通信,发送特定的控制命令。手机端则需要一个能够接收蓝牙指令并控制音乐播放的APP。可能需要参考现有的蓝牙协议,比如SPP(串口协议)或者BLE(低功耗蓝牙),但51单片机可能更适合SPP,因为BLE可能需要更复杂的栈支持。 用户之前的引用中提到了使用ADC0832将音乐信号转换为数字量,但在这个需求里,如果只是控制播放,可能不需要这个步骤。所以需要确认用户是否误解了系统功能。如果用户是想让单片机通过蓝牙发送控制指令,而不是处理音乐内容,那么硬件可以简化,不需要ADC模块,只需要单片机、蓝牙模块、电源和一些输入设备(比如按键)来触发控制命令。 接下来,系统的工作流程可能是:用户通过按键或传感器输入控制信号,单片机处理这些信号并生成相应的控制指令,通过蓝牙模块发送到手机APP,APP解析指令并控制音乐播放。比如,按键按下发送播放命令,再次按下暂停,或者调节音量等。 通信协议方面,需要定义简单的指令格式,比如用单个字符表示不同操作,例如'P'表示播放,'S'表示停止,'+'增加音量等。这样单片机发送这些字符,手机APP接收到后执行相应动作。 电源部分,需要考虑单片机和蓝牙模块的供电,可能需要5V和3.3V的转换,因为蓝牙模块通常是3.3V工作电压,而51单片机是5V,所以需要电平转换电路,或者选择兼容5V的蓝牙模块。 手机APP的开发可能需要使用Android或iOS平台,利用蓝牙API进行通信。比如,Android可以使用BluetoothSocket和BluetoothAdapter类来建立连接和数据传输。用户可能需要开发一个简单的APP,或者使用现有的蓝牙串口APP,但自定义指令可能需要自己编写APP。 测试方面,需要确保蓝牙连接的稳定性,指令的正确解析,以及手机端能够正确响应。可能需要调试蓝牙模块的AT命令,设置正确的通信参数,比如波特率、配对码等。 可能遇到的问题包括蓝牙连接不稳定、指令丢失或延迟,手机APP的兼容性问题,以及单片机处理能力有限导致的多任务处理困难。需要优化代码,确保实时性,或者采用中断方式处理按键输入和蓝牙通信。 总结一下,系统设计应包括硬件选型(单片机、蓝牙模块、输入设备)、软件设计(单片机程序、手机APP)、通信协议定义、电源管理以及测试调试。需要参考用户之前的引用中提到的蓝牙模块和单片机型号,结合控制指令的需求,构建一个稳定可靠的系统。</think>### 基于51单片机的蓝牙音乐控制系统设计方案 #### 一、系统架构设计 1. **硬件组成** - **主控芯片**:STC89C52/AT89S52单片机(支持串口通信) - **蓝牙模块**:HC-05(主从一体,支持SPP协议) - **输入设备**:矩阵按键(控制播放/暂停/切歌) - **供电模块**:5V电源适配器(需包含3.3V电平转换电路) - **指示模块**:LED状态指示灯 2. **软件框架** - 单片机程序(Keil C51开发) - 手机端APP(Android Studio开发) - 自定义蓝牙通信协议 #### 二、核心实现原理 $$ \begin{cases} \text{按键输入} \xrightarrow{中断检测} \text{单片机} \xrightarrow{串口通信} \text{HC-05} \\ \downarrow \\ \text{手机APP} \xrightarrow{解析指令} \text{音乐播放控制} \end{cases} $$ #### 三、关键实现步骤 1. **蓝牙模块配置** - 使用AT指令设置蓝牙参数: ```c AT+NAME=MusicCtrl // 设置设备名称 AT+UART=9600,0,0 // 设置波特率 ``` 2. **单片机程序设计** ```c void UART_SendCommand(char cmd) { SBUF = cmd; // 发送指令到蓝牙模块 while(!TI); TI = 0; } void main() { while(1) { if(KEY_PLAY) UART_SendCommand('P'); // 发送播放指令 if(KEY_NEXT) UART_SendCommand('N'); // 发送切歌指令 } } ``` 3. **手机APP开发要点** - 建立蓝牙Socket连接 - 监听接收数据流 ```java BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID); InputStream mmInStream = socket.getInputStream(); ``` #### 四、系统优化方向 1. **低功耗设计**:通过$P_{avg}=V_{CC} \times I_{sleep}$公式优化电源管理[^2] 2. **协议增强**:添加CRC校验位,格式:`[起始符][指令][校验和][结束符]` 3. **多设备兼容**:支持A2DP协议与AVRCP协议的双模控制
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值