您的位置:首页 > 产品设计 > UI/UE

Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

2014-11-24 11:57 453 查看
关键词:蓝牙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(欢迎转载,请注明作者,请尊重版权谢谢)

欢迎指正错误,共同学习、共同进步!!

参考网站:

/article/1384717.html

/article/6997839.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界面点击搜索设备



应用扫描触发逻辑流程:自上而下三种颜色,分别代表应用部分、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

[html] view
plaincopy

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


[html] view
plaincopy





@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

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





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


[html] view
plaincopy





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


[html] view
plaincopy





#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部分




1、DBUS对应方法的实现,这里跟JNI部分比较类似,也是用了函数结构体对应关系。

idh.code\external\bluetooth\bluez\src\adapter.c


[html] view
plaincopy





#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


[html] view
plaincopy





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


[html] view
plaincopy





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

[html] view
plaincopy





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


[html] view
plaincopy





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


[html] view
plaincopy





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

[html] view
plaincopy





/* 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特性,包括包类型,包括事件类型



当上层调用setsockopt(sock, SOL_HCI, HCI_FILTER,&flt, sizeof(flt))时,触发相应的内核路径。

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

[html] view
plaincopy





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

[html] view
plaincopy





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);

}

………………

}<span style="font-weight: bold;">

</span>

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

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




(五)、EVENT返回状态




Controller收到查询命令后,返回一个命令状态

1、cmd_status

idh.code\external\bluetooth\bluez\plugins\hciops.c

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





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内容

[html] view
plaincopy





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

[html] view
plaincopy





// 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

[html] view
plaincopy





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


[html] view
plaincopy





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

[html] view
plaincopy





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中

[html] view
plaincopy





<pre code_snippet_id="452489" snippet_file_name="blog_20140817_26_3867453" name="code" class="html"> 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]);

}

} </pre>

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

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





<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


[html] view
plaincopy





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 中:



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

[html] view
plaincopy





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动态注册的,如下:

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





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

[html] view
plaincopy





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);

}

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐