关键词:蓝牙blueZ  A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_connected、sink_get_properties、AUDIO、DBUS
版本号:基于android4.2之前版本号 bluez
内核:linux/linux3.08
系统:android/android4.1.3.4
作者:xubin341719(欢迎转载。请注明作者。请尊重版权谢谢)
欢迎指正错误。共同学习、共同进步!!

參考站点:
http://blog.csdn.net/u011960402/article/details/17216563
http://www.cnblogs.com/fityme/archive/2013/04/13/3019471.html socket相关
http://hi.baidu.com/wwwkljoel/item/a35e5745d14e02e6bcf45170 setsockopt

Android bluetooth介绍(一):基本概念及硬件接口
Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
Android bluetooth介绍(四): a2dp connect流程分析

一、蓝牙扫描经常使用的方法:
         蓝牙扫描的能够通过两种途径实现:命令行hciitool扫描;Android界面触发,通过JNI、DUBS下发命令。

1、  命令行hciitool扫描(这部分通过Linux命令操作,跟android没有关系)
通过bluez的tool发送扫描命令,如:hcitoool scan
adb shell 下#hcitool  scan扫描结果

Hcitool扫描逻辑例如以下所看到的:

2、Android界面触发,通过JNI、DUBS下发命令:通过android界面点击搜索设备

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHViaW4zNDE3MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" width="270" height="480" />
应用扫描触发逻辑流程:自上而下三种颜色,分别代表应用部分、JNI部分、linux blueZ部分。

二、Hcitool触发逻辑分析
1、hcitool这部分代码比較简单,实现函数
idh.code\external\bluetooth\bluez\tools\hcitool.c代码大致流程例如以下:

通过所带的參数,找到cmd_scan,进入hci_inquriy。这个函数中创建一个BTPROTO_HCI的socket,通过ioctlHCINQUIRY向内核读取数据,保存返回信息。

2、内核层逻辑:
当然IOCTL仅仅是当中一项。
idh.code\kernel\net\bluetooth\ hci_sock.c

static const struct proto_ops hci_sock_ops = {
…………
.ioctl = hci_sock_ioctl,
.poll = datagram_poll,
.listen = sock_no_listen,
…………
};

它的流程就是构造查询命令,放入命令队列,调度队列来发送命令,当中hci_send_frame后面会解说,这里关键是命令的发送和数据的收集是分开的。所以它在里面会放弃2s的调度。以此来等待数据的收集。收集的数据放在hdev->inq_cache里面。我们来看看这个数据是怎样取得的。例如以下图所看到的:

入口点hci_rx_work前面已经具体分析过了,这里就不说了。它里面会依据不同的事件类型做不同的处理。通常情况下,扫描都是带信号强度的扫描。所以走的hci_inquiry_result_with_rssi_evt路线,还有其他几种扫描方式,比方:HCI_EV_INQUIRY_RESULT,HCI_EV_EXTENDED_INQUIRY_RESULT等。处理逻辑都几乎相同的,里面会hci_inquiry_cache_update来把结果放到hdev->discovery链表里面去。供后面的查询;比方前面调用的inquiry_cache_dump函数就能够从这个链表里面把数据取出来。然后copy到用户层;
三、Android界面触发,通过JNI、DUBS下发命令
总体流程例如以下所看到的:

(一)、应用部分:
1、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

  @Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
…………
mLocalAdapter.startScanning(true);
return true;
}

2、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\LocalBluetoothAdapter.java

 private final BluetoothAdapter mAdapter;
void startScanning(boolean force) {
// Only start if we're not already scanning
if (!mAdapter.isDiscovering()) {
if (!force) {
// Don't scan more than frequently than SCAN_EXPIRATION_MS,
// unless forced
if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
return;
}
// If we are playing music, don't scan unless forced.
A2dpProfile a2dp = mProfileManager.getA2dpProfile();
if (a2dp != null && a2dp.isA2dpPlaying()) {
return;
}
}
if (mAdapter.startDiscovery()) {
mLastScan = System.currentTimeMillis();
}
}
}

3、idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java

    public boolean startDiscovery() {
if (getState() != STATE_ON) return false;
try {
return mService.startDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}

4、JNI函数的调用idh.code\frameworks\base\core\java\android\server\BluetoothService.java

private native boolean startDiscoveryNative();//Native函数声明
public class BluetoothService extends IBluetooth.Stub {
private static final String TAG = "BluetoothService";
private static final boolean DBG = true;
…………
public synchronized boolean startDiscovery() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (!isEnabledInternal()) return false;
return startDiscoveryNative();
}
………………
}

(二)、JNI部分:
1、android_server_BluetoothService.cpp中JNI函数的对比表
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp

static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
………………
{"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
{"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
…………
}

2、相应Native函数的实现
这里面有个知识点DBUS,这个后面我们单独去解说
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp

#define BLUEZ_DBUS_BASE_IFC       "org.bluez"
#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"//事实上DBUS_ADAPTER_IFACE 也就是 org.bluez.Adapter
static jboolean startDiscoveryNative(JNIEnv *env, jobject object) { ………………
/* Compose the command */
msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
get_adapter_path(env, object),
DBUS_ADAPTER_IFACE, "StartDiscovery");
…………
}
Native函数startDiscoveryNative和字符串StartDiscovery相应。

(三)、DBUS部分

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHViaW4zNDE3MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

1、DBUS相应方法的实现,这里跟JNI部分比較相似,也是用了函数结构体相应关系。
idh.code\external\bluetooth\bluez\src\adapter.c

#define ADAPTER_INTERFACE	"org.bluez.Adapter"
static GDBusMethodTable adapter_methods[] = {
………………
{ "ReleaseSession", "", "", release_session },
{ "StartDiscovery", "", "", adapter_start_discovery },
{ "StopDiscovery", "", "", adapter_stop_discovery,
G_DBUS_METHOD_FLAG_ASYNC},
………………
}

字符StartDiscovery又相应C中的实现函数adapter_start_discovery。
2、adapter_start_discovery的实现
idh.code\external\bluetooth\bluez\src\adapter.c

static DBusMessage *adapter_start_discovery(DBusConnection *conn,
DBusMessage *msg, void *data)
{
…………
err = start_discovery(adapter);
if (err < 0 && err != -EINPROGRESS)
return btd_error_failed(msg, strerror(-err)); done:
req = create_session(adapter, conn, msg, 0,
session_owner_exit); adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req); return dbus_message_new_method_return(msg);
}

3、 start_discovery调用
idh.code\external\bluetooth\bluez\src\adapter.c

const struct btd_adapter_ops *adapter_ops = NULL;
static int start_discovery(struct btd_adapter *adapter)
{
…………
pending_remote_name_cancel(adapter);
return adapter_ops->start_discovery(adapter->dev_id);
}

adapter_ops相应结构体btd_adapter_ops中相应函数,例如以下:上面部分就相应到btd_adapter_ops中的hci_ops结构体。

4、btd_adapter_ops中的hci_ops结构体
idh.code\external\bluetooth\bluez\plugins\hciops.c

static struct btd_adapter_ops hci_ops = {
…………
.set_powered = hciops_set_powered,
.set_discoverable = hciops_set_discoverable,
.set_pairable = hciops_set_pairable,
.set_limited_discoverable = hciops_set_limited_discoverable,
.start_discovery = hciops_start_discovery,
.stop_discovery = hciops_stop_discovery,
………………
.create_bonding = hciops_create_bonding,
.cancel_bonding = hciops_cancel_bonding,
.read_local_oob_data = hciops_read_local_oob_data,
.add_remote_oob_data = hciops_add_remote_oob_data,
.remove_remote_oob_data = hciops_remove_remote_oob_data,
.set_link_timeout = hciops_set_link_timeout,
.retry_authentication = hciops_retry_authentication,
};

5、hciops_start_discovery函数的实现
idh.code\external\bluetooth\bluez\plugins\hciops.c

static int hciops_start_discovery(int index)
{
int adapter_type = get_adapter_type(index); switch (adapter_type) {
case BR_EDR_LE:
return hciops_start_inquiry(index, LENGTH_BR_LE_INQ);
case BR_EDR: //蓝牙芯片为2.1+EDR的
return hciops_start_inquiry(index, LENGTH_BR_INQ);
case LE_ONLY:
return hciops_start_scanning(index, TIMEOUT_LE_SCAN);
default:
return -EINVAL;
}
}

6、hciops_start_inquiry
idh.code\external\bluetooth\bluez\plugins\hciops.c

static int hciops_start_inquiry(int index, uint8_t length)
{
struct dev_info *dev = &devs[index];
uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
inquiry_cp inq_cp; DBG("hci%d length %u", index, length); memset(&inq_cp, 0, sizeof(inq_cp));
memcpy(&inq_cp.lap, lap, 3);
inq_cp.length = length;
inq_cp.num_rsp = 0x00; if (hci_send_cmd(dev->sk, OGF_LINK_CTL,
OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)
return -errno; return 0;
}

7、idh.code\external\bluetooth\bluez\lib\hci.c

/* HCI functions that require open device
* dd - Device descriptor returned by hci_open_dev. */
dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
{
………………
if (plen) {
iv[2].iov_base = param;
iv[2].iov_len = plen;
ivn = 3;
} while (writev(dd, iv, ivn) < 0) {//writev这里把数据写到socket里面。
if (errno == EAGAIN || errno == EINTR)
continue;
return -1;
}
return 0;
}

(四)、内核部分:
1、HCI FILTER的设置
HCIsocket的类型为BTPROTO_HCI。上层调用setsockopt的时候。触发了内核的hci_sock_setsockopt函数的运行。在这里面设置了socket的filter特性,包含包类型,包含事件类型

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHViaW4zNDE3MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

当上层调用setsockopt(sock, SOL_HCI, HCI_FILTER,&flt, sizeof(flt))时。触发相应的内核路径。
idh.code\kernel\net\bluetooth\hci_sock.c

static const struct proto_ops hci_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
…………
.shutdown = sock_no_shutdown,
.setsockopt = hci_sock_setsockopt,
.getsockopt = hci_sock_getsockopt,
.connect = sock_no_connect,
…………
};

idh.code\kernel\net\bluetooth\hci_sock.c

static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len)
{
………………
case HCI_FILTER:
{
struct hci_filter *f = &hci_pi(sk)->filter; uf.type_mask = f->type_mask;
uf.opcode = f->opcode;
uf.event_mask[0] = *((u32 *) f->event_mask + 0);
uf.event_mask[1] = *((u32 *) f->event_mask + 1);
}
………………
}

内核这部分就比較统一的数据,通过hci_send_cmd把命令发出去,HCI_FILTER这个地方的处理还没理解。后面补充

Writev函数通过socket把数据写下去,经过VFS层,调用到内核空间的sendmsg函数。

(五)、EVENT返回状态

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHViaW4zNDE3MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

Controller收到查询命令后。返回一个命令状态
1、cmd_status
idh.code\external\bluetooth\bluez\plugins\hciops.c

	switch (eh->evt) {
case EVT_CMD_STATUS:
cmd_status(index, ptr);
break;
static inline void cmd_status(int index, void *ptr)
{
evt_cmd_status *evt = ptr;
uint16_t opcode = btohs(evt->opcode); if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))//假设是inquriy做特殊处理;
cs_inquiry_evt(index, evt->status);
}

2、cs_inquiry_evt的实现 idh.code\external\bluetooth\bluez\plugins\hciops.c

static inline void cs_inquiry_evt(int index, uint8_t status)
{
if (status) {//错误信息
error("Inquiry Failed with status 0x%02x", status);
return;
} set_state(index, DISCOV_INQ);//设置状态为INQ,向上层回复discoverying的property change
}

3、设置不同的DISCOV 状态 idh.code\external\bluetooth\bluez\plugins\hciops.c

static void set_state(int index, int state)
{
………………
switch (dev->discov_state) {
case DISCOV_HALTED://停止发现;
if (adapter_get_state(adapter) == STATE_SUSPENDED)
return; if (is_resolvname_enabled() &&
adapter_has_discov_sessions(adapter))
adapter_set_state(adapter, STATE_RESOLVNAME);
else
adapter_set_state(adapter, STATE_IDLE);
break;
case DISCOV_INQ:
case DISCOV_SCAN://扫描发现;
adapter_set_state(adapter, STATE_DISCOV);
break;
}
}

4、设置adapter的状态 idh.code\external\bluetooth\bluez\src\adapter.c

idh.code\external\bluetooth\bluez\src\adapter.c
#define ADAPTER_INTERFACE "org.bluez.Adapter"
void adapter_set_state(struct btd_adapter *adapter, int state)
{
…………
case STATE_DISCOV:
discov_active = TRUE;
//向上层回复discovering的property change
emit_property_changed(connection, path,
ADAPTER_INTERFACE, "Discovering",
DBUS_TYPE_BOOLEAN, &discov_active);
break;
…………
}

emit_property_changed发送PropertyChanged的消息,消息内容为Discovering。通知上层BluetoothEventLoop进行Discovering。

5、emit_property_changed发送Discovering消息的实现 idh.code\external\bluetooth\bluez\src\dbus-common.c
这部分涉及到DBUS内容

dbus_bool_t emit_property_changed(DBusConnection *conn,
const char *path,
const char *interface,
const char *name,
int type, void *value)
{
DBusMessage *signal;
DBusMessageIter iter;
signal = dbus_message_new_signal(path, interface, "PropertyChanged"); // 创建消息对象并标识路径
if (!signal) {
error("Unable to allocate new %s.PropertyChanged signal",
interface);
return FALSE;
}
dbus_message_iter_init_append(signal, &iter);//把信号相相应的參数压进去
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);//申请一个首地址。把前面压入的參数传入这个首地址
append_variant(&iter, type, value);//
return g_dbus_send_message(conn, signal);//启动发送调用,并释放发送相关消息信息
}

6、DBUS消息接收的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp

// Called by dbus during WaitForAndDispatchEventNative()
static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
void *data) {
…………
else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) {
jobjectArray str_array = parse_adapter_property_change(env, msg);//(1)、对收到消息的解析
if (str_array != NULL) {
/* Check if bluetoothd has (re)started, if so update the path. */
jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
const char *c_property = env->GetStringUTFChars(property, NULL);
if (!strncmp(c_property, "Powered", strlen("Powered"))) {
jstring value =
(jstring) env->GetObjectArrayElement(str_array, 1);
const char *c_value = env->GetStringUTFChars(value, NULL);
if (!strncmp(c_value, "true", strlen("true")))
nat->adapter = get_adapter_path(nat->conn);
env->ReleaseStringUTFChars(value, c_value);
}
env->ReleaseStringUTFChars(property, c_property); env->CallVoidMethod(nat->me,
method_onPropertyChanged,//(2)、
method_onPropertyChanged NATVIE函数的实现
str_array);
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
goto success;
}

(1)、对收到消息的解析 idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp

jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {
return parse_property_change(env, msg, (Properties *) &adapter_properties,
sizeof(adapter_properties) / sizeof(Properties));
}

针对org.bluez.Adapter不同的消息类型
idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp

static Properties adapter_properties[] = {
{"Address", DBUS_TYPE_STRING},
{"Name", DBUS_TYPE_STRING},
{"Class", DBUS_TYPE_UINT32},
{"Powered", DBUS_TYPE_BOOLEAN},
{"Discoverable", DBUS_TYPE_BOOLEAN},
{"DiscoverableTimeout", DBUS_TYPE_UINT32},
{"Pairable", DBUS_TYPE_BOOLEAN},
{"PairableTimeout", DBUS_TYPE_UINT32},
{"Discovering", DBUS_TYPE_BOOLEAN},
{"Devices", DBUS_TYPE_ARRAY},
{"UUIDs", DBUS_TYPE_ARRAY},
};

(2)、method_onPropertyChanged NATVIE函数的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp

static void classInitNative(JNIEnv* env, jclass clazz) {
ALOGV("%s", __FUNCTION__);
#ifdef HAVE_BLUETOOTH
method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
"([Ljava/lang/String;)V");
method_onDevicePropertyChanged = env->GetMethodID(clazz,
"onDevicePropertyChanged","(Ljava/lang/String;[Ljava/lang/String;)V");
…………
}

7、JNI调用onPropertyChanged相应JAVA的实现,在BluetoothEventLoop.java
idh.code\frameworks\base\core\java\android\server\BluetoothEventLoop.java中

   private static native void classInitNative();
/*package*/ void onPropertyChanged(String[] propValues) {
………………
log("Property Changed: " + propValues[0] + " : " + propValues[1]);
String name = propValues[0];
if (name.equals("Name")) {//获取蓝牙名字;
…………
} else if (name.equals("Pairable") || name.equals("Discoverable")) {//配对。
………………
} else if (name.equals("Discovering")) {//扫描查询;
Intent intent;
adapterProperties.setProperty(name, propValues[1]);
if (propValues[1].equals("true")) {
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
} else {
// Stop the discovery.
mBluetoothService.cancelDiscovery();
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
}
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
} else if (name.equals("Devices") || name.equals("UUIDs")) {//Devices、UUID的获取;
………………
} else if (name.equals("Powered")) {//蓝牙打开、关闭;
mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,
propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));
} else if (name.equals("DiscoverableTimeout")) {
adapterProperties.setProperty(name, propValues[1]);
}
}

(1)、看到这份log我们或许会更明确其他功能的由来:

D BluetoothEventLoop: Property Changed: Powered : true
D BluetoothEventLoop: Property Changed: Pairable : true
D BluetoothEventLoop: Property Changed: Class : 5898764
D BluetoothEventLoop: Property Changed: Pairable : true
D BluetoothEventLoop: Property Changed: Discoverable : false
D BluetoothEventLoop: Property Changed: Discovering : true
D BluetoothEventLoop: Property Changed: Discovering : false
D BluetoothEventLoop: Property Changed: Devices : 1
D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Connected value: true
D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Paired value: true
D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: UUIDs value: 4

(2)、以下我们重点分析Discovering这部分
idh.code\frameworks\base\core\java\android\server\BluetoothEventLoop.java

else if (name.equals("Discovering")) {
Intent intent;
adapterProperties.setProperty(name, propValues[1]);
if (propValues[1].equals("true")) {//開始扫描
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//
} else {
// Stop the discovery. //停止扫描
mBluetoothService.cancelDiscovery();
intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
}
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
这样就能够通过broadcast发送ACTION_DISCOVERY_STARTED广播,注冊的receiver来响应了。

8、ACTION_DISCOVERY_STARTED\ACTION_DISCOVERY_FINISHED的receiver分析
从代码中我们能够看到这个action一共同拥有两个receiver,一个是静态注冊的BluetoothDiscoveryReceiver,一个是动态注冊是ScanningStateChangedHandler。
(1)、BluetoothDiscoveryReceiver:
这个receiver是在settings中的Androidmanifest中静态注冊的。用途:主要用于获取扫描開始和终止的时间。
idh.code\packages\apps\Settings\AndroidManifest.xml

<receiver
android:name=".bluetooth.BluetoothDiscoveryReceiver">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
<action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>

1)、ACTION_DISCOVERY_STARTED、ACTION_DISCOVERY_FINISHED和AndroidManifest.xml文件的联系
idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java

public final class BluetoothAdapter {
private static final String TAG = "BluetoothAdapter";
private static final boolean DBG = false;
…………
public static final String ACTION_DISCOVERY_STARTED =
"android.bluetooth.adapter.action.DISCOVERY_STARTED";
public static final String ACTION_DISCOVERY_FINISHED =
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
…………
}

2)、BluetoothAdapter,蓝牙适配器。直到我们建立bluetoothSocket连接之前。都要不断操作它。

BluetoothAdapter中的动作常量

ACTION_DISCOVERY_FINISHED

已完毕蓝牙搜索

ACTION_DISCOVERY_STARTED

已经開始搜索蓝牙设备

ACTION_LOCAL_NAME_CHANGED

更改蓝牙的名字

ACTION_REQUEST_DISCOVERABLE

请求能够被搜索

ACTION_REQUEST_ENABLE

请求启动蓝牙

ACTION_SCAN_MODE_CHANGED

扫描模式已经改变

ACTION_STATE_CHANGED

状态已改变

ACTION_CONNECTION_STATE_CHANGED

3)、收到广播后函数实现,開始扫描
Main log中显示的log为DISCOVERY_STARTED
D BluetoothDiscoveryReceiver: Received:android.bluetooth.adapter.action.DISCOVERY_STARTED
HCI log 中:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHViaW4zNDE3MTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothDiscoveryReceiver.java这个文件里就一个函数。还是比简单

public final class BluetoothDiscoveryReceiver extends BroadcastReceiver {
private static final String TAG = "BluetoothDiscoveryReceiver";
private static final boolean DEBUG = Debug.isDebug(); @Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DEBUG) Log.d(TAG, "Received: " + action); if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED) ||
action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
//共享时间戳。扫描開始和结束的时间。
LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
}
}
}

ScanningStateChangedHandler的注冊及用途,要用于開始扫描。和扫描显示界面的控制。

这个receiver是在idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java动态注冊的,例如以下:

BluetoothEventManager(LocalBluetoothAdapter adapter,
CachedBluetoothDeviceManager deviceManager, Context context) {
mLocalAdapter = adapter;
…………
// Bluetooth on/off broadcasts
addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler()); // Discovery broadcastsaddHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
…………
}

(1)、ScanningStateChangedHandler函数实现例如以下:idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java

 private class ScanningStateChangedHandler implements Handler {
private final boolean mStarted; ScanningStateChangedHandler(boolean started) {
mStarted = started;
}
public void onReceive(Context context, Intent intent,
BluetoothDevice device) {
synchronized (mCallbacks) {//1)、调用注冊的callback
中的onScanningStateChanged函数。 for (BluetoothCallback callback : mCallbacks) {
callback.onScanningStateChanged(mStarted);
}
}
//2)、这个函数就是把上次扫描到设备、和之前的设备做相应处理;
mDeviceManager.onScanningStateChanged(mStarted);
LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
}
}

1)、调用注冊的callback中的callback.onScanningStateChanged(mStarted)函数。
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

    public void onScanningStateChanged(boolean started) {
if (started == false) {//《1》、假设扫描结束;
removeOutOfRangeDevices();
}
updateProgressUi(started);// 《2》、UI显示小圆圈扫描;
}

《1》、假设扫描结束;removeOutOfRangeDevices();
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

private void removeOutOfRangeDevices() {
Collection<CachedBluetoothDevice> cachedDevices =
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
for (CachedBluetoothDevice cachedDevice : cachedDevices) {
if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
cachedDevice.isVisible() == false) {
BluetoothDevicePreference preference = mDevicePreferenceMap.get(cachedDevice);
if (preference != null) {
mDeviceListGroup.removePreference(preference);
}
mDevicePreferenceMap.remove(cachedDevice);
}
}
}

《2》、UI显示小圆圈扫描,updateProgressUi(started);例如以下图所看到的:

idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

private void updateProgressUi(boolean start) {
if (mDeviceListGroup instanceof ProgressCategory) {
((ProgressCategory) mDeviceListGroup).setProgress(start);
}
}

2)、这部分的作用,開始扫描,不显示列表中内容,或把之前列表中没扫描到的设备清除
mDeviceManager.onScanningStateChanged(mStarted);
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\CachedBluetoothDevice.java

private void updateProgressUi(boolean start) {
if (mDeviceListGroup instanceof ProgressCategory) {
((ProgressCategory) mDeviceListGroup).setProgress(start);
}
}
2)、这部分的作用,開始扫描。不显示列表中内容,或把之前列表中没扫描到的设备清除
mDeviceManager.onScanningStateChanged(mStarted);
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\ CachedBluetoothDevice.java
public synchronized void onScanningStateChanged(boolean started) {
// If starting a new scan, clear old visibility
// Iterate in reverse order since devices may be removed.
//假设開始新的扫描,清除旧的能见设备。迭代反序由于有的设备可能被删除
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
if (started) {//假设扫描開始就不显示;
cachedDevice.setVisible(false);
} else {//对扫描的结果作出推断。假设之前扫描过,这次没有扫描到,就移除列表。
if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
cachedDevice.isVisible() == false) {
mCachedDevices.remove(cachedDevice);
}
}
}
}

Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析的更多相关文章

  1. 【转】Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

    原文网址:http://blog.csdn.net/xubin341719/article/details/38584469 关键词:蓝牙blueZ  A2DP.SINK.sink_connect.s ...

  2. Android bluetooth介绍(两): android 蓝牙源架构和uart 至rfcomm过程

    关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本号:基于android4.2先前版本 bluez内核:linux/linux3.08系统:an ...

  3. Android bluetooth介绍(四): a2dp connect流程分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  4. Android bluetooth介绍

    Android bluetooth介绍(一):基本概念及硬件接口Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程Android blueto ...

  5. Android bluetooth介绍(一):基本概念及硬件接口

    关键词:蓝牙硬件接口 UART  PCM  blueZ 版本号:基于android4.2之前版本号 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者 ...

  6. 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程

    原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP ...

  7. Android 6.0 Kotlin 蓝牙扫描

    package com.arci.myapplication import android.app.Activityimport android.os.Bundleimport android.sup ...

  8. android Bluetooth(官方翻译)

    Bluetooth Using the Bluetooth APIs, an Android application can perform the following: 使用蓝牙APIs,一个And ...

  9. android bluetooth

    Android bluetooth介绍(一):基本概念及硬件接口 BlueZ为调试和与蓝牙子系统通信提供很多设置命令行工具,包含下面这些: hciconfig hcitool hcidump sdpt ...

随机推荐

  1. java的(PO,VO,TO,BO,DAO,POJO)解释1

    java的(PO,VO,TO,BO,DAO,POJO)解释  O/R Mapping 是 Object Relational Mapping(对象关系映射)的缩写.通俗点讲,就是将对象与关系数据库绑定 ...

  2. 多线程编程中条件变量和的spurious wakeup 虚假唤醒

    1. 概述 条件变量(condition variable)是利用共享的变量进行线程之间同步的一种机制.典型的场景包括生产者-消费者模型,线程池实现等. 对条件变量的使用包括两个动作: 1) 线程等待 ...

  3. C#托管堆对象实例包含什么

    每个托管堆上的对象实例除了包含本身的值外,还包括:○ Type Object Ponter: 指向Type对象实例.如果是同类型的对象实例,就指向同一个Type对象实例.○ Sync Block In ...

  4. Cocos2d-x

    http://blog.csdn.net/ccf19881030/article/details/9204801 Cocos2d-x相关的博客以及官网信息 1.CSDN博主无幻的博客:http://b ...

  5. 内存及字符串操作篇strlen strchar strcmp strcoll strcpy strdup strstr strtok strspn strrchr bcmp bcopy bzero index memccpy memset

    bcmp(比较内存内容) 相关函数 bcmp,strcasecmp,strcmp,strcoll,strncmp,strncasecmp 表头文件 #include<string.h> 定 ...

  6. 启明星会议室预定系统Outlook版开始支持Exchange2013与Office365版

    版本启明星会议室预定系统支持Exchange2013与微软云服务Office365版.(注意:Exchange2007与Exchange2010也适合此版本) 1.安装 首页,安装类似启明星普通的会议 ...

  7. 已有数据库(单机)部署Database Vault

    1.停止em和数据库 emctl stop dbconsole shutdown immediate 2.又一次安装DB软件 ./runInstaller 选组件的时候,选中"Oracle ...

  8. 第六章 JVM垃圾收集器(2)

    上一章记录了几种常见的垃圾收集器,见<第五章 JVM垃圾收集器(1)> 1.G1 说明: 从上图来看,G1与CMS相比,仅在最后的"筛选回收"部分不同(CMS是并发清除 ...

  9. SQL锁(转)

    说 明    Chaos 无法改写隔离级别更高的事务中的挂起的更改.   ReadCommitted 在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻 ...

  10. UNdelete

    --90兼容模式以上,2005+ -- http://raresql.com/2012/10/24/sql-server-how-to-find-who-deleted-what-records-at ...