Android蓝牙系统

安卓系统中的蓝牙打印机操作指南 安卓蓝牙API从安卓系统发布之初就一直得到持续的发展和优化。随着安卓版本的更新,蓝牙API也经历了多次升级和改进。随着新的API级别的发布,新的蓝牙功能和改进被引入,以便开发者能够开发出更先进的蓝牙应用。例如,从安卓3.0 Honeycomb版本开始,安卓引入了对蓝牙A2DP(高级音频分发配置文件)的支持,使得开发者能够开发出高质量音频传输的应用。而在安卓4.3 Jelly Bean及之后的版本中,引入了蓝牙低功耗(BLE)支持,使得低功耗蓝牙设备的通信成为可能。 阅读详情

第1章 Android蓝牙系统

1.1 蓝牙技术简介

蓝牙(Bleuetooth)原是十世纪统一了丹麦的一个国王的名字,现取其“统一”的含义,用来意在统一无线局域网通讯的标准的蓝牙技术。蓝牙技术是爱立信,IBM,Intel等世界5家著名大公司在1998年联合推出的一项无线通讯规范。随后成立的蓝牙技术特殊兴趣组织(SIG)来负责该技术的开发和技术协议的制定,如今全世界已有1800多家公司加盟该组织,最近微软公司也正式加盟并成为SIG组织的领导成员之一。它以低成本的近距离无线连接为基础,为移动通信设备建立一个短程无线连接。其实质内容是建立通用的无线电空中接口,使计算机和通信设备进一步结合,让不同的厂家生产便携式设备在没有电缆或电线相互连接的情况下,能在近距离范围内具有相互通信的一种技术。

1.2 蓝牙技术的特点

  • 全球范围适用

蓝牙技术使用无需经过许可的工业、科研和医疗(ISM)波段(2.4至2.485 GHz),使用展频、调频、全双工信号,标称速率为1600跳/秒。在大多数国家,无需经过许可便可使用2.4 GHz ISM波段。

  • 抗干扰

蓝牙技术的适配跳频(AFH)能力的设计目的是为了减少共用2.4 GHz频谱的无线技术之间出现的干扰。该功能会在频谱中寻找并无被占用的频带以供蓝牙技术使用。AFH的工作原理是识别该频谱中的其他设备并避开这些设备所用的频带。跳频功能以1 MHz的频率在79个频段中进行切换,从而获得了较高的抗干扰能力,同时使该频谱中能够实现更加高效的传输。有了跳频功能,尽管其他技术与蓝牙技术同时使用,但蓝牙技术的用户仍能享有优质的性能表现。

  • 射程

射程根据不同的具体应用而定,尽管核心规格规定了最低射程,但这并非限制,制造商仍可根据其具体用例调整射程应用。

根据具体应用中使用的射频种类,射程将有所不同:

第三类射频 – 射程最高1米或3英尺

第二类射频 – 最常见于移动设备,射程为10米或33英尺

第一类射频 – 主要用于工业用例,射程为100米或300英尺

  • 低功耗

最常用的射频为第二类,其能耗为2.5 mW。蓝牙技术的设计能耗非常之低。此外,规格允许射频处于非活跃状态时可以断电则进一步降低了能耗。3.0版HS中的通用替代MAC/PHY能够发现高速设备的AMP,并仅在需要进行数据传输时开启射频,实现了节能优势,同时增强了射频的安全性。对于无需高速数据传输率但需要最大限度延长电池寿命的设备而言,蓝牙低耗能技术为其实现了优化效果,其耗电量仅为传统蓝牙技术的1/2至1/100。

 

1.3 蓝牙技术在android中的应用

1.3.1 蓝牙服务的启动

在前面章节android启动过程中介绍到android服务的启动,init进程中,启动Zygote后,然后由SystemServer启动一系列服务,蓝牙服务就是在这个时候启动的。详细见代码:

/framework/base/services/java/com/android/server/SystemServer.java

if (SystemProperties.get("ro.kernel.qemu").equals("1")) {

} else {

……

bluetooth = new BluetoothService(context);

……

bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);

……

if (airplaneModeOn == 0 && bluetoothOn != 0) {

                    bluetooth.enable();

 }  

Bluetooth服务的代码首先通过SystemProperties的get方法来判断系统是不是使用模拟器内核,如果是使用模拟器内核来启动android的系统,那么就会跳过蓝牙服务的启动,也就是说Android 4.0模拟器是不支持蓝牙系统的。否则就是一个实在的设备产品(ro.kernel.qemu=0)

就是构造一个bluetooth的服务(BluetoothService)和一个蓝牙耳机服务(BluetoothA2dpService)。

代码段最后一部分是判断开机是否要启用蓝牙,通过函数我们可以看到如果设备的飞行模式是关闭的并且bluetooth的那个开关是在on。就是调用bluetoothService的enable方法使得我们设备开机的时候就将蓝牙开启。飞行模式就是那些使用无线频谱的模块都必须关掉,譬如:wifi,Bluetooth,GPS等。接下来就是BluetoothService的enable方法了。

/framework/base/core/java/android/server/BluetoothService.java

public synchronized boolean enable(boolean saveSetting) {

mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,"Need BLUETOOTH_ADMIN permission");

if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {

            return false;

 }   

mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);

        return true;

 }   

蓝牙服务的enable的方法会先判断进程有没有操作权限,需要蓝牙管理的权限才能去enable蓝牙模块,然后还会再次判断系统的飞行模式有没有打开,如果此时飞行模式是on的话,那么会返回,还是不能打开服务。在确保拥有权限并且不是出于飞行模式的情况下,就会往蓝牙状态机发送一个USER_TURN_ON的命令。下面介绍一下android中的蓝牙状态机。

  • Poweroff

这就是蓝牙模块没有初始化的状态,这时候硬件模块是出于没有上电的状态。

  • Warmup

这个状态就是给设备上电,使设备能够从没电到待机状态。

  • Hotoff

Hotoff我个人理解就是在模块上电了,出于一种待命的状态,如果收到了turn_on_coninue的命令时候就会去将蓝牙模块切换到工作状态。如果接收到了turn_cold的命令时候,设备就会断电进入poweroff状态。

  • Switching

这也是一个中间状态,需要继续接收命令。

  • Bluetoothon

这时蓝牙模块出于正常工作的状态。

根据android中蓝牙状态的源码中,具体的各个状态机相互转换图如下:

 

 

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class PowerOff extends State {

public void enter() {

   if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);

   } 

……

case USER_TURN_ON:

broadcastState(BluetoothAdapter.STATE_TURNING_ON);

 transitionTo(mWarmUp);

……

if (prepareBluetooth()) {

if ((Boolean) message.obj) {

   persistSwitchSetting(true);

}

deferMessage(obtainMessage(TURN_ON_CONTINUE));

蓝牙状态机初始化时PowerOff的,从上面的BluetoothService的enable函数中USER_TURN_ON命令。从上面代码中可以看出蓝牙状态机在接收到USER_TURN_ON后,首先就像蓝牙适配器广播蓝牙正处于STATE_TRUNING_ON的状态,蓝牙的适配器的蓝牙状态有四个:

分别是,state_off(10),state_turning_on(11),state_on(12),state_turning_off(14)。由于我们刚开机所以蓝牙适配器的状态必然是从10->11。然后将蓝牙状态机的状态切换到mWaremUp状态。

接下来调用了prepareBluetooth()方法。接下来看看prepareBluetooth方法。代码如下:

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private boolean prepareBluetooth() {

  if (mBluetoothService.enableNative() != 0) {

          return false;

   }

……

int retryCount = 2;

boolean eventLoopStarted = false;

while ((retryCount-- > 0) && !eventLoopStarted) {

mEventLoop.start();

while ((pollCount-- > 0) && !eventLoopStarted) {

         if (mEventLoop.isEventLoopRunning()) {

                 eventLoopStarted = true;

                     break;

      }

在preprareBluetooth方法中,首先就是调用了BluetoothService的enableNative()的方法,只要一看到这种带Native的方法,JNI的代码是少不了的。由于enableNative方法走的路有点多,所以先直接到BluetoothService的代码中寻找enableNative()看个究竟。

framework/base/core/java/android/server/BluetoothService.java

/*package*/ native int enableNative();

Framework/base/core/jni/ android_server_BluetoothService.cpp

 

static JNINativeMethod sMethods[] = {

……

{"enableNative", "()I", (void *)enableNative},

……

}

static jint enableNative(JNIEnv *env, jobject object) {

return bt_enable();

}

从上面的代码可以看出,BluetoothService的enableNative就是直接调用了JNI的代码,JNI是java native interface的 缩写,中文叫java本地接口。Android上层跑的java代码,而底层代码都是c语言。以android的一贯作风是通过JNI代码调用HAL层,然后就可以直接调用驱动代码或者经由内核达到操作驱动代码。enableNative的代码很简单,就是调用了bt_enable。我们可以继续找到这个函数的实现。

System/bluetooth/bluedroid/bluetooth.c

int bt_enable() {

……

if (set_bluetooth_power(1) < 0) goto out

……

if (property_set("ctl.start", "hciattach") < 0)

……

for (attempt = 1000; attempt > 0;  attempt--) {

hci_sock = create_hci_sock();

……

ret = ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID);

……

}

Set_bluetooth_power()函数会根据蓝牙的硬件开关,也就是hci设备注册的时候会同时在linux内核中注册一个rfkill类,比如我们在电脑键盘上面可能会看见一个按键来开关蓝牙或者wifi之类的。这里会去读这个键值,如果是1代表可以开启蓝牙的,否则是没法使用蓝牙的,在开发过程中如果没这样的按键,可将这行代码拿掉。Propery_set(“ctl.start”,hciattach)。这个函数会去启动hciattach服务,具体这个服务是以二进制文件存储在系统system/bin目录下面的。

我们可以从andriod启动脚本文件找到名字叫hciattach服务。当然这个是针对接串口的蓝牙来说需要启动服务,如果我们的设备是通过USB总线接入系统的话,其实这个服务也是可以不启动的。剩下的代码是一个for循环,先建立一个bluetooth的套接字,然后通过ioctl来和bluez的代码来打开蓝牙设备,可以重试1000次。接下来的代码就要跑到内核的BlueZ了。

Kernel/net/bluetooth/hci_sock.c

static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){

……

case HCIDEVUP:

                if (!capable(CAP_NET_ADMIN))

                        return -EACCES;

                return hci_dev_open(arg);

……

}

从上面的ioctl下来的代码可以看出,当函数第二个参数cmd为HCIDEVUP时,就会直接调用hci_dev_open(arg)方法。这个函数就好比我们在终端下面使用蓝牙调试工具hciconfig,执行了

#hciconfig hci0 up。

Kernel/net/bluetooth/hci_core.c

int hci_dev_open(__u16 dev)

{

……

hdev = hci_dev_get(dev);

……

if (hdev->open(hdev)) {

                ret = -EIO;

                goto done;

  }

}

在kernel的bluez调用hci_dev_open,而在这个函数中又hdev->open(hdev),这个就是我们驱动注册时候的回调函数open。由于我们平台使用的是usb的蓝牙接入方式,我就以usb的蓝牙驱动为例,看看驱动的open函数。

Kernel/driver/bluetooth/btusb.c

static int btusb_open(struct hci_dev *hdev){

……

err = btusb_submit_intr_urb(hdev, GFP_KERNEL);

……

err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);

……

}

USB hci设备打开后,首先将设备的interface配置为HCI_RUNNING状态,然后为数据传输初始化设备的端点和管道,初始化和填充urb。代码到这,蓝牙设备就算是真正打开了。

回到之前的蓝牙状态机的代码:

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private boolean prepareBluetooth() {

  if (mBluetoothService.enableNative() != 0) {

          return false;

   }

……

int retryCount = 2;

boolean eventLoopStarted = false;

while ((retryCount-- > 0) && !eventLoopStarted) {

mEventLoop.start();

while ((pollCount-- > 0) && !eventLoopStarted) {

         if (mEventLoop.isEventLoopRunning()) {

                 eventLoopStarted = true;

                     break;

      }

mBluetoothService在enableNative()函数主要功能就是通过一系列代码来打开蓝牙设备。如果设备驱动代码没有问题的话,我们enableNative()返回的将会是true。在实际调试蓝牙设备时候,我们可以通过在linux或者android的终端下面使用自带的工具命令(hciconfig),执行:

# hciconfig –a如果驱动能够和设备绑定的话,我们就会看到蓝牙设备的一些比较重要信息,如:蓝牙的物理地址,总线类型,协议类型等。

上面的代码接下来会是一个while循环,执行2次。mEventLoop.start()。也就是说调用了EventLoop的start方法。

/framework/base/core/java/android/server/BluetoothEventLoop.java

/* package */ void start() {

   if (!isEventLoopRunningNative()) {

            if (DBG) log("Starting Event Loop thread");

            startEventLoopNative();

        }   

}

第一次进入这个函数isEventLoopRunningNative肯定是返回false的,所以直接进入了startEventLoopNative(),前面说过了一般带native的函数结尾的函数都是JNI。看到这里又要进JNI了。

Framework/base/core/jni/android_server_BluetoothEventLoop.cpp

static JNINativeMethod sMethods[] = {

……

{"startEventLoopNative", "()V", (void *)startEventLoopNative},

……

}

static jboolean startEventLoopNative(JNIEnv *env, jobject object) {

……

nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *

            DEFAULT_INITIAL_POLLFD_COUNT);

……

nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *

            DEFAULT_INITIAL_POLLFD_COUNT);

……

if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {

        LOGE("Error getting BT control socket");

        goto done;

}

……

if (setUpEventLoop(nat) != JNI_TRUE) {

        LOGE("failure setting up Event Loop!");

        goto done;

 }

pthread_create(&(nat->thread), NULL, eventLoopMain, nat);

……

}

 

为socket文件描述符分配内存数据,同时为DBusWatch结构体分配内存,socketpair创建了一对套接字(AF_LOCAL域中使用),这个描述符可以是单双工也可以是全双工的,这里是单双工的,也就是只能从这个描述符中读取数据,而不能写数据。如果socketpair的第四个参数是个数组,也可以实现一个描述符读,另外一个描述符写。从而实现全双工。然后就是setUpEventLoop函数,最后就是创建了eventLoopMain的线程。

Framework/base/core/jni/android_server_BluetoothEventLoop.cpp

static jboolean setUpEventLoop(native_data_t *nat) {

……

dbus_threads_init_default();

……

dbus_error_init(&err);

……

if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){

            return JNI_FALSE;

        }

……

dbus_bus_add_match(nat->conn,

                "type='signal',interface='org.freedesktop.DBus'",   &err);

dbus_bus_add_match(nat->conn,

   "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",&err);

……

}

 

 

这里是初始化dbus,是bluez能够挂接上dbus。建立一个dbus连接之后,为这个dbus连接起名,为我们将要进行的消息循环添加匹配条件(就是通过信号名和信号接口名来进行匹配控制的) -- dbus_bus_add_match()。我们进入等待循环后,只需要对信号名,信号接口名进行判断就可以分别处理各种信号了。在各个处理分支上。我们可以分离出消息中的参数。对参数类型进行判断和其他的处理。具体对dbus感兴趣的话可以参照:http://dbus.freedesktop.org。

Framework/base/core/jni/android_server_BluetoothEventLoop.cpp

static void *eventLoopMain(void *ptr) {

……

while (1) {

……

if (nat->pollData[i].fd == nat->controlFdR) {

while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT) != -1) {

……

switch (data) {

case EVENT_LOOP_EXIT:

   dbus_connection_set_watch_functions(nat->conn,NULL, NULL, NULL, NULL, NULL);

   tearDownEventLoop(nat);

   nat->vm->DetachCurrentThread();

……

case EVENT_LOOP_ADD:

    {

     handleWatchAdd(nat);

         break;

     }

case EVENT_LOOP_REMOVE:

     {

      handleWatchRemove(nat);

          break;

      }

 

以轮训的方式从socket的描述符中不断的接收数据,如果有数据到来,就根据数据的类型做相应的处理。到这里dbus就和bluez建立连接。还是回到之前我们prepareBluetooth的函数。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class PowerOff extends State {

public void enter() {

   if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);

   } 

……

case USER_TURN_ON:

broadcastState(BluetoothAdapter.STATE_TURNING_ON);

 transitionTo(mWarmUp);

……

if (prepareBluetooth()) {

if ((Boolean) message.obj) {

   persistSwitchSetting(true);

}

deferMessage(obtainMessage(TURN_ON_CONTINUE));

前面的代码我们分析完了prepareBluetooth(),如果没有问题就进入了persistSwitchSetting()。

然后就是讲蓝牙状态机切换到mWarnUp状态。并向蓝牙状态机发送了一个TURN_ON_CONTINUE的命令。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class WarmUp extends State {

……

public boolean processMessage(Message message) {

   log("WarmUp process message: " + message.what);

……

switch(message.what) {

case TURN_ON_CONTINUE:

 

这个命令在WarmUp状态里面什么也没做。直接通过deferMessage()到HotOff状态里面重新发送了TURN_ON_CONTINUE的命令。那我们HotOff状态机。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class HotOff extends State {

……

public boolean processMessage(Message message) {

    log("HotOff process message: " + message.what);

……

switch(message.what) {

case TURN_ON_CONTINUE:

        int retryCount = 5

……

mBluetoothService.switchConnectable(true);

transitionTo(mSwitching);

……

}

 

在HotOff状态机中,接收到TURN_ON_CONTINUE命令后,先调用了BluetoothService的switchConnectable(true);然后将蓝牙的状态机切换到Switching状态。

/framework/base/core/java/android/server/BluetoothService.java

/*package*/ synchronized void switchConnectable(boolean on) {

        setAdapterPropertyBooleanNative("Powered", on ? 1 : 0);

    }

又到了以Native结尾的函数,还是到JNI里面找到它的实现吧。

Framework/base/core/jni/android_server_BluetoothService.cpp

static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key,

                                         void *value, jint type) {

……

msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,get_adapter_path(env, object),

DBUS_ADAPTER_IFACE, "SetProperty");

……

dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);

dbus_message_iter_init_append(msg, &iter);

……

reply = dbus_connection_send_with_reply(nat->conn, msg, NULL, -1);

……

}

 

通过Dbus向bluez发送SetPropery的信息(message),当成功的时候,我们在另外一端就会收到powerChanged的信号。具体处理如下的代码:

Framework/base/core/java/android/server/BluetoothEventLoop.java

/*package*/ void onPropertyChanged(String[] propValues) {

……

BluetoothAdapterProperties adapterProperties = mBluetoothService.getAdapterProperties();

……

else if (name.equals("Pairable") || name.equals("Discoverable")) {

 adapterProperties.setProperty(name, propValues[1]);

if (name.equals("Discoverable")) {

          mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SCAN_MODE_CHANGED);

            } 

……

else if (name.equals("Powered")) {

          mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,

                propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));

 

当有蓝牙AdapterProperies发生变化时,在BluetoothEventLoop.java中就会有个onProperyCha

nged方法来处理。首先通过BluetoothService的getAdapterProperties来获取蓝牙适配器的所有属性,都有哪些属性,在实际开发过程中我们通过调试可以看到按顺序依次是:power,

Pairable,class,device,UUID,Discoverable。通过将power的value设置为true,就会向蓝牙状态发送一个POWER_STATE_CHAGED,通过Discoverable的属性来向蓝牙状态机发送一个

SCAN_MODE_CHANGED的命令。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

case POWER_STATE_CHANGED:

               removeMessages(POWER_DOWN_TIMEOUT);

                    if (!((Boolean) message.obj)) {

                        if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) {

                            transitionTo(mHotOff);

                            finishSwitchingOff();

                            if (!mContext.getResources().getBoolean

                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {

                                deferMessage(obtainMessage(TURN_COLD));

                            }  

                        }  

                    } else {

                        if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) {

                            if (mContext.getResources().getBoolean

                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {

                                recoverStateMachine(TURN_HOT, null);

                            } else {

                                recoverStateMachine(TURN_COLD, null);

                            }  

                        }  

                    }  

在上一个HotOff的时候,已经将蓝牙状态机切换到了Switching了。所以直接在Switching这个状态里面来处理命令。第一个power_state_changed的命令很简单。在蓝牙状态机里面有个叫mPublicState的全局变量来记录蓝牙适配器的状态。如果是power的值为true,那么就将这个变量的值变为STATE_TURNING_ON,否则就是STATE_TURNING_OFF。在前面介绍过了蓝牙适配器总共有四个状态:State_off(10),state_turning_on(11),state_on(12),state_turning_off(

13)。那么继续来看第二个命令,scan_mode_changed。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

case SCAN_MODE_CHANGED:

     if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) {

        mBluetoothService.setPairable();

        mBluetoothService.initBluetoothAfterTurningOn();

        transitionTo(mBluetoothOn);

        broadcastState(BluetoothAdapter.STATE_ON);

mBluetoothService.runBluetooth();

}

根据第一个命令,mPublicState的值是STATE_TURNING_ON,这里又要和BluetoothService来交互了,先调用了setPairable和initBluetoothAfterTurningOn,runBluetooth并将蓝牙状态机切换到BluetoothOn的状态。接下来到bluetoothService看这个setPairable方法。

/framework/base/core/java/android/server/BluetoothService.java

 

/*package*/ synchronized void setPairable() {

        String pairableString = getProperty("Pairable", false);

        if (pairableString == null) {

            Log.e(TAG, "null pairableString");

            return;

        }   

        if (pairableString.equals("false")) {

            setAdapterPropertyBooleanNative("Pairable", 1);

        }   

    }   

这个过程和上面的设置POWER的过程是类似的,先通过getPropery获取Pairable的状态,如果是false的话,就需要调用JNI的方法setAdapterPropertyBooleanNative来通过dbus来向bluez来设置蓝牙适配器的Pairable的值。如果设置成功的话,同样还会调用BluetoothEventLoop中的onProperyChanged方法。继续跟进代码initBluetoothAfterTurningOn:

framework/base/core/java/android/server/BluetoothService.java

/*package*/ void initBluetoothAfterTurningOn() {

String discoverable = getProperty("Discoverable", false);

        String timeout = getProperty("DiscoverableTimeout", false);

        if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) {

            setAdapterPropertyBooleanNative("Discoverable", 0);

        }

mBondState.initBondState();

        initProfileState();

        getProfileProxy();

}

这个函数首先还是像设置power,Parirable的属性差不多,设置Discoverable的属性。当蓝牙模块打开和蓝牙适配器配对(Pairable)之后。剩下的initProfileState可以获取蓝牙的物理地址。

getProfileProxy直接调用了Adapter的getProfileProxy。得到俄ProfileProxy可以是HEADSET,

A2DP,INPUT_DEVICE,PAN,HEALTH。

framework/base/core/java/android/server/BluetoothService.java

/*package*/ void runBluetooth() {

……

autoConnect();

}

private void autoConnect() {

String[] bonds = getKnownDevices();

        if (bonds == null) {

            return;

        }

……

for (String path : bonds) {

            String address = getAddressFromObjectPath(path);

            BluetoothDeviceProfileState state = mDeviceProfileState.get(address);

            if (state != null) {

                Message msg = new Message();

                msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;

                state.sendMessage(msg);

            }

        }

    }

 

在autoConnect中,就会扫描附近的设备,并获取设备的地址和名字。这是我们看到就是能看到了一系列扫描出来的附近的设备。此时蓝牙的状态出于正常运行。到这里蓝牙模块就在

Android中工作起来了。

1.4 蓝牙开发在android中的调试

1.4.1 内核和驱动的支持

作为是linux内核的Android系统,必须在编译内核过程中将bluez编译的config选上。

CONFIG_BT =y

CONFIG_BT_RFCOMM =y

CONFIG_BT_BNEP = y

CONFIG_BT_CMTP =y

CONFIG_BT_L2CAP=y

CONFIG_BT_SCO=y

然后根据我们的驱动使用的接入方式,常见的有串口(uart),USB,SDIO总线等。如果我们的驱动能够正常工作工作的话,我们在linux的终端通过下面命令就可以看见hci设备了。

root@android:/ # hciconfig -a                                                 

hci0:   Type: BR/EDR  Bus: USB

        BD Address: 74:2F:68:CE:13:57  ACL MTU: 1022:8  SCO MTU: 183:5

        DOWN

        RX bytes:505 acl:0 sco:0 events:22 errors:0

        TX bytes:99 acl:0 sco:0 commands:22 errors:0

        Features: 0xff 0xfe 0x0d 0xfe 0xd8 0x7f 0x7b 0x87

        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3

        Link policy: RSWITCH HOLD SNIFF

        Link mode: SLAVE ACCEPT

如果能够像上面可以看见蓝牙的类型,总线类型,物理地址等信息。说明蓝牙设备已经在内核中注册成功了,但能不能使用还要继续使用下面命令,我们注意到蓝牙模块状态时DOWN的。

root@android:/ # hciconfig hci0 up

root@android:/ # hciconfig -a                                                 

hci0:   Type: BR/EDR  Bus: USB

        BD Address: 74:2F:68:CE:13:57  ACL MTU: 1022:8  SCO MTU: 183:5

        UP RUNNING

        RX bytes:994 acl:0 sco:0 events:42 errors:0

        TX bytes:185 acl:0 sco:0 commands:42 errors:0                          

        Features: 0xff 0xfe 0x0d 0xfe 0xd8 0x7f 0x7b 0x87                      

        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3                       

        Link policy: RSWITCH HOLD SNIFF                                        

        Link mode: SLAVE ACCEPT                                                

        Name: 'Bluetooth USB Host Controller'                                  

        Class: 0x000000                                                         

        Service Classes: Unspecified                                           

        Device Class: Miscellaneous,                                           

        HCI Version: 4.0 (0x6)  Revision: 0x102                                

        LMP Version: 4.0 (0x6)  Subversion: 0x1                                

        Manufacturer: Atheros Communications, Inc. (69)

通过hciconfig hci0 up命令让蓝牙模块的状态从DOWN变成UP状态。这个时候还不能就确定蓝牙驱动是能正常工作的。需要继续看看我们的蓝牙能不能扫描其他的蓝牙设备,如果能够扫描到其他的设备,就可以说明我们的蓝牙设备在内核态是可以正常工作的。

root@android:/ # hcitool scan                                                 

Scanning ...

        00:1C:26:D5:3E:D6       DAWEIYAN-MOBL

        00:1E:4C:F3:BC:FA       ZHILONGX-MOBL

        00:1F:3A:F1:94:CD       JUELIUX-MOBL

        00:27:13:D6:66:D9       QWANG29-MOBL2

        50:63:13:C7:83:6D       YANCHAOY-MOBL

        00:1C:26:FD:11:3A       ZWANG16X-MOBL

好了如果能够扫描出设备的物理地址和名字的话,那我们的设备在linux 内核态就ok了。

1.4.2 Android Boradconfig和服务的支持

在BoardConfig.mk中添加:BOARD_HAVE_BLUETOOTH := true。因为在framework中的代码很多函数是需要这个宏的,如果这个宏没有打开的话,很多代码是走不过的。在android的添加蓝牙工作必要的服务,Dbus-daemon,bluetoothd,

/init.rc

service dbus /system/bin/dbus-daemon --system --nofork

    class main                       

    socket dbus stream 660 bluetooth bluetooth         

    user bluetooth                              

group bluetooth net_bt_admin  

service bluetoothd /system/bin/bluetoothd -n

    class main                                   

    socket bluetooth stream 660 bluetooth bluetooth                  

socket dbus_bluetooth stream 660 bluetooth Bluetooth

service  hciattach /system/bin/hciattach  当然这个服务只针对你的蓝牙接入方式是串口的,向我们这里是USB的话,这个服务还是可以省掉的。

service hfag /system/bin/sdptool add --channel=10 HFAG

    user bluetooth

    group bluetooth net_bt_admin

    disabled

oneshot

service hsag /system/bin/sdptool add --channel=11 HSAG

    user bluetooth

    group bluetooth net_bt_admin

    disabled

oneshot

service opush /system/bin/sdptool add --channel=12 OPUSH

    user bluetooth

    group bluetooth net_bt_admin

    disabled

oneshot

service pbap /system/bin/sdptool add --channel=19 PBAP

    user bluetooth

    group bluetooth net_bt_admin

    disabled

    oneshot

好了,到这里蓝牙应该就能正常工作了。上面的代码都是基于android 4.0。

SAP BTP开发入门:从PaaS到CAP/UI5的30天学习路线 在云计算与数字化转型的大背景下,企业系统集成与云端扩展需求激增,传统的单点开发模式正面临深刻变革。PaaS(平台即服务)作为云原生的核心概念,将基础设施、开发环境与应用服务统一封装,为企业提供了灵活的扩展能力。SAP BTP正是SAP生态中的PaaS平台,它承载着系统集成、低代码开发、AI服务与数据管理,成为连接S/4HANA等核心业务系统与创新应用的桥梁。对于ABAP开发者而言,掌握BTP不仅是技能升级,更是应对从传统部署向云端订阅模式转型的必由之路。本文从平台基础出发,梳理BTP的核心服务与开发模型(如 阅读详情

相关推荐

ElasticSearch 集群安全加固实战:从零配置TLS证书与密码认证

本文提供了一份详尽的ElasticSearch集群安全加固实战指南,重点介绍了如何从零开始配置TLS证书与用户安全认证。通过分步讲解证书生成、节点配置、集群重启及密码设置,帮助管理员为ES集群构建通信加密与访问控制的双重防线,有效提升数据安全性与合规性。

weixin_29323493的博客 444

实战Linux Bluetooth编程(三) HCI层编程

1. HCI层协议概述:HCI提供一套统一的方法来访问Bluetooth底层。如图所示:  从图上可以看出,Host Controller Interface(HCI)  就是用来沟通Host和Module。Host通常就是PC, Module则是以各种物理连接形式(USB,serial,pc-card等)连接到PC上的bluetooth Dongle。在Host这一端:a

baozhongchao的专栏 1万+

理想汽车智驾方案介绍 4 World model + 强化学习重建自动驾驶交互环境

本帖将以 DrivingSphere 为例来解析这个过程。

地平线开发者 1921

【Settings开发】蓝牙模块(一)

概要 蓝牙-bluetooth此名取自公元十世纪一位颇有作为的丹麦国王Harald bluetooth,是由爱立信公司于上世纪90年代牵头发展而来的一种短距离无线通信技术标准,其具有体积小、抗干扰、低功耗等特点,该协议目前由蓝牙技术联盟(SIG)负责维护管理。 历史协议版本 协议版本 发布时间 主要特点 1.0/1.0B 1999 产品互操作性差 ...

乌啼夜的酒痕 2166

计算机视觉三大顶会(ICCV/CVPR/ECCV)投稿指南与趋势解析

本文详细解析计算机视觉三大顶会(ICCV/CVPR/ECCV)的投稿指南与最新趋势。从基础认知到实战经验,涵盖时间线管理、选题策略、论文写作技巧及实验设计要点,帮助研究者提升投稿成功率。特别关注2024年技术融合、评估维度和伦理要求等前沿趋势,为计算机视觉领域的研究者提供实用建议。

weixin_29165725的博客 1845

Vitis HLS 学习指南与实践教程

Vitis HLS 学习指南与实践教程摘要:本文系统介绍了AMD Vitis HLS工具及其应用。HLS技术将C/C++代码自动转换为硬件描述语言,显著提升FPGA开发效率(5-10倍)。文章对比了HLS与传统RTL设计的优势,包括抽象级别高、开发周期短、代码重用性强等特点。详细讲解了Vitis HLS工具链的核心功能,包括支持的C/C++特性(如任意精度数据类型)、开发流程(项目创建-代码编写-仿真-综合)以及关键优化技术(流水线、循环展开、数组分割等编译指示)。通过具体代码示例展示了HLS设计方法,为F

weixin_44707118的博客 1401

android 蓝牙连接广播,Android广播接收器蓝牙事件捕捉

为了捕获蓝牙状态更改( STATE_OFF , STATE_TURNING_ON , STATE_ON , STATE_TURNING_OFF ),请在您的活动中执行此操作:首先,为您的AndroidManifest文件添加蓝牙权限:在您的活动或服务中创建BroadcastReceiver:private final BroadcastReceiver mBroadcastReceiver1 = ...

weixin_35968240的博客 947

【BigWorld 游戏服务器引擎】Cell 架构设计介绍,及Java实现方案的探索

横向扩展(Scalability):通过动态增减CellApp和BaseApp的数量,可以轻松应对玩家人数的变化。世界可以做得非常大,只需增加更多的 Cell。负载分离(Load Separation):将不同性质的负载分离,避免了单一进程瓶颈。CPU 密集型的实时计算(CellApp)和 I/O 密集型的数据操作(BaseApp)互不影响。高效的视野管理(AOI):每个 Cell 只关心自己区域内和边界上的实体,大大降低了计算复杂度。容错性:某个CellApp。

代码时光 1205

Android蓝牙相关广播

以下广播来自BluetoothAdapter ACTION_STATE_CHANGED 本地蓝牙适配器变化,如蓝牙打开、关闭。 EXTRA_STATE int,表示当前状态,值域为STATE_OFF、STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF EXTRA_PREVIOUS_STATE int,表示之前的状态 ...

一朵翔云的博客 3173

基于泰勒展开改进的物理信息神经网络

基于泰勒展开改进的物理信息神经网络

天天酷科研的博客 504

STM32G4 电机外设篇(三) TIM1 发波 和 ADC COMP DAC级联

STM32G4电机控制外设应用笔记摘要:本文详细介绍了TIM1高级定时器生成三对互补PWM波的配置方法(预分频器PSC=30000,重装载值ARR=10000),重点说明需断开电机连线避免干扰。通过STM32CubeMX配置TIM1通道4触发ADC采样,设置RCR分频参数,并给出Keil代码修改要点。第二部分探讨了TIM1、ADC、比较器和DAC的级联应用,包括电流采样触发时序(在PWM波峰值前采样)和过流保护机制。配置步骤包含DAC3作为比较器负输入端、设置Break触发封波保护,并附关键代码段实现外设联

qq_43091070的博客 1102

Android 系统启动流程与 Zygote、SystemServer

文章目录init 进程Zygote 进程SystemServer 进程ServiceManager 进程问题 init 进程 init 进程的 id 为 1,0 是 linux 内核,在 linux 中 id 的大小和启动顺序有关,意思是 linux 内核启动后,启动的就是 init 进程。 init 进程主要有两个作用: 启动系统关键的服务 守护关键服务,重启手机 怎样的服务属于关键服务?关键服务是对于手机而言必不可少的服务,比如网络服务、蓝牙服务、铃声服务,而 AMS、PMS 等这些父进程不

Vincent的博客 1527

一台NAS两个系统:飞牛nas上完美运行群晖DSM?(飞牛nas+群晖nas套娃安装实操)

想在飞牛NAS上体验群晖DSM系统?💡 本文详细记录了飞牛nas上部署群晖的完整过程,从镜像转换到系统安装,每一步都有详细说明 🖥️,让你在不增加硬件成本的情况下,轻松实现一套硬件、多种系统的灵活部署 ✅

XiaoqiangClub的博客 2374

android tv 蓝牙服务_安卓系统蓝牙服务com.android.bluetooth的使能

蓝牙系统服务层的使能流程分析蓝牙服务层的使能基础是其初始化完成,也就是AdapterService通过onBind()将AdapterServiceBinder上报给bind该服务的调用者。我们现在应该都知道在安卓系统中bind该服务的为BluetoothManagerService。本篇我们就从蓝牙服务管理收到bind的回调开启蓝牙使能流程的分析。熟悉安卓系统中bind服务机制的小伙伴应该都知道...

weixin_42347763的博客 799

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

安卓10蓝牙问题_安卓系统蓝牙服务com.android.bluetooth的使能

蓝牙系统服务层的使能流程分析蓝牙服务层的使能基础是其初始化完成,也就是AdapterService通过onBind()将AdapterServiceBinder上报给bind该服务的调用者。我们现在应该都知道在安卓系统中bind该服务的为BluetoothManagerService。本篇我们就从蓝牙服务管理收到bind的回调开启蓝牙使能流程的分析。熟悉安卓系统中bind服务机制的小伙伴应该都知道...

weixin_39585675的博客 1019

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

android 获取蓝牙设备id_安卓蓝牙系统中如何获取蓝牙音乐的音频跟踪会话ID

原标题:安卓蓝牙系统中如何获取蓝牙音乐的音频跟踪会话ID蓝牙音乐AudioTrack Session ID的获取当今这个音视频无处不在的时代,音频跟踪会话ID(AudioTrack Session ID)是个很重要的参数,可以用来实现音频相关的一些特效。接下来的内容我们就探究如何在安卓蓝牙系统中获取该id。熟悉蓝牙音乐播放流程的同学都了解蓝牙音乐AVDTP的控制流与音频流以及AVRCP是如何作用的...

weixin_32463071的博客 1006

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

android蓝牙键盘编辑,使用很麻烦 安卓系统蓝牙键盘使用技巧

【IT168 技术】安卓系统在蓝牙键盘支持方面就真是惨不忍睹了,因为很多原生安卓rom并没有集成HID蓝牙设备,对蓝牙键盘这类产品支持上存在不少缺陷。也导致目前市面上大多数的平板或者手机键盘只能服务苹果的产品。蓝牙无线技术是一个通用型的技术,没有理由区分Android和苹果。笔者在网上搜罗了一下安卓系统使用蓝牙键的方法,得到的结论是,改变底层的文件让安卓系统能识别并使用蓝牙键盘。之前笔者评测过的雷...

weixin_34902728的博客 2639

android蓝牙通信_安卓系统蓝牙协议栈 bluedroid 的使能

安卓系统蓝牙协议栈 bluedroid 使能流程分析本文承接上篇文章《安卓中蓝牙系统服务层的使能流程分析》,接续分析协议栈层相关的使能流程,所以蓝牙协议栈bluedroid的使能始于JNI层enableNative()中调用协议栈接口enable()函数。话不多说,还是按照老规矩我们先从整体上对协议栈的使能有个印象,参考如下时序图。安卓原生的蓝牙协议栈bluedroid在分层上被分为btif、bt...

weixin_39832965的博客 493

android 蓝牙 这个createRfcommSocketToServiceRecord 是安卓2.3系统及以下用的,新的安卓要用createInsecureRfcommSocketToSer

这个createRfcommSocketToServiceRecord 是安卓2.3系统及以下用的,新的安卓要用createInsecureRfcommSocketToServiceRecord

chenhao0568的专栏 7359

android蓝牙协议栈bluedroid分析,安卓系统蓝牙协议栈 bluedroid 的使能

安卓系统蓝牙协议栈 bluedroid 使能流程分析本文承接上篇文章《安卓中蓝牙系统服务层的使能流程分析》,接续分析协议栈层相关的使能流程,所以蓝牙协议栈bluedroid的使能始于JNI层enableNative()中调用协议栈接口enable()函数。话不多说,还是按照老规矩我们先从整体上对协议栈的使能有个印象,参考如下时序图。安卓原生的蓝牙协议栈bluedroid在分层上被分为btif、bt...

weixin_34194499的博客 2665

安卓系统中蓝牙服务层的初始化流程分析

安卓系统中蓝牙服务层的初始化流程分析 蓝牙系统依赖于安卓,从上到下,层次分明。本篇文章我们就来简单分析下蓝牙服务层初始化的相关流程。 首先来了解下安卓系统中的蓝牙模块大致分为以下几部分: 对安卓系统启动时序有大概了解的小伙伴想必都知道蓝牙服务的管理对象BluetoothManagerService会在系统SystemServer启动过程中被初始化,处于系统进程中运行,然后在系统启动的不同阶段执...

坏小子-无线通信技术 2460

uniapp安卓打开系统蓝牙

【代码】uniapp安卓打开系统蓝牙。

shingXiao的博客 462

安卓系统蓝牙配对流程分析

安卓系统蓝牙配对流程分析 配对流程基本上始于首次连接一个蓝牙设备的过程中,本端的搜索流程结束获取到该设备的BluetoothDevice信息,就可以开启配对流程。 配对,顾名思义就是将两个设备通过相关技术进行绑定,达到能够互相识别彼此的目的。因此通过蓝牙技术进行的配对就是大家理解中的蓝牙配对。随着蓝牙技术在消费市场上越来越普及,生活中很容易就能遇到蓝牙配对这一使用蓝牙功能事必不可缺失的步骤。本期我们就简单聊聊安卓系统蓝牙配对的流程是如何运行的。 Application通过framework层Bluetoo

坏小子-无线通信技术 4040

android6.0系统去掉所有蓝牙功能

最近遇到一个安卓6.0的项目,客户要求额外出一个固件专门用于清海关,这个固件其中有一个要求就是去掉所有蓝牙功能,意义在于海关检查设备机器,如果机器没有蓝牙功能可以少交点税,如下就是去掉系统中几个关于蓝牙的功能的记录: 1.去掉下拉状态栏中的蓝牙快捷设置:到frameworks/base/packages/SystemUI/res/values/config.xml文件中的quick_settings_tiles_default属性中去掉bt即可 2.去掉系统设置中的蓝牙菜单条目:如下补丁所示...

wzh048503的博客 1847

android OS系统如何适配蓝牙遥控器

蓝牙遥控器(简称:遥控器)功能介绍 一. 遥控器组合按键功能 遥控器功能除了熟知的丝印按键功能,还有以下两个组合按键: 1. 触发配对信息组合按键,其作用向板端蓝牙发送配对请求,请求与板端蓝牙配对; 2. 触发解除配对组合按键,其作用向板端蓝牙发送解除配对请求,解除与板端蓝牙的配对; 二. 遥控器在Android 原生系统中应用的技术问题 1. 出厂android设备,

leo.wang 技术专栏 4446

Android 蓝牙获取不到服[特性信息][安卓系统蓝牙缓存的作用][清理蓝牙缓存的原因][清理蓝牙缓存的方法]

最近做手机蓝牙与BLE设备的项目。遇到一个问题,记录下来。方便以后。出现的情况是:手机蓝牙与蓝牙设备连接几次,交互信息后,无法再次连接与搜索到服务,需重启手机蓝牙或者关闭APP后,再次启动APP,才能再次连接设备。上网查找了不少资料。才找资料。/*多次创建gatt连接对象的直接结果是创建过6个以上gatt后就会再也连接不上任何设备,原因应该是中对BLE限制了同时连接的数量为6个* 解决办法是在每一次重新连接时都执行一次gatt.close();关闭上一个连接。

挖到尽头 8717
上一篇: 电话按键音分析号码
下一篇: MP4文件格式的解析,以及MP4文件的分割算法
badboyplane
博客等级 码龄22年 19粉丝 0原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值