Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
关键词:蓝牙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界面点击搜索设备
应用扫描触发逻辑流程:自上而下三种颜色,分别代表应用部分、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
@Overridepublic 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 scanningif (!mAdapter.isDiscovering()) {if (!force) {// Don't scan more than frequently than SCAN_EXPIRATION_MS,// unless forcedif (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部分
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特性,包含包类型,包含事件类型;
当上层调用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返回状态
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);elseadapter_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 changeemit_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_BLUETOOTHmethod_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
<receiverandroid: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 中:
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();@Overridepublic 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 broadcastsaddHandler(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.javapublic 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);}}}}
转载于:https://www.cnblogs.com/yxysuanfa/p/7348841.html
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析相关推荐
- 【转】Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
原文网址:http://blog.csdn.net/xubin341719/article/details/38584469 关键词:蓝牙blueZ A2DP.SINK.sink_connect.s ...
- Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP RFCOMM 版本:基于android4.2之前版本 bluez内核:linux/linux3.08 系统:an ...
- Android bluetooth介绍(两): android 蓝牙源架构和uart 至rfcomm过程
关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP RFCOMM 版本号:基于android4.2先前版本 bluez内核:linux/linux3.08 系统:a ...
- Android bluetooth介绍(一):基本概念及硬件接口
关键词:蓝牙硬件接口 UART PCM blueZ 版本:基于android4.2之前版本 bluez 内核:linux/linux3.08 系统:android/android4.1.3.4 ...
- android 蓝牙不停扫描,android – BluetoothAdapter不会停止扫描BLE设备
在我的应用程序中我有启动和停止按钮,当用户按下启动时我调用startScan方法 bluetoothAdapter.getBluetoothLeScanner().startScan(getLeSca ...
- Android连接、断开蓝牙扫描枪时屏幕刷新
场景:使用蓝牙扫描枪连接安卓设备,用于扫描条码. 问题:连接和断开扫描枪时,屏幕都会刷新,造成临时加载的数据丢失. 原因: 扫描枪连接时是将其作为物理输入设备,即物理键盘,而安卓在改变屏幕方向.弹出隐 ...
- php扫描蓝牙设备接口,微信硬件蓝牙扫描某个设备接口onScanWXDeviceResult
好久没更新微信硬件蓝牙开发的技术文章了,最近闲来无事,翻了翻系列文章以及微信硬件jsapi接口,发现onScanWXDeviceResult扫描某个设备接口并没有做案例demo给大家分享出来,所以接着 ...
- 【CSRMesh2.1蓝牙开发】-Android demo介绍【二】之代码分析
怎么介绍呢,一直考虑,还是从业务流程开始,1.Meshservice初始化-->2.扫描设备--->3.连接设备-->4.扫描设备-->5.设备组网 这样分析多初学者,或者刚接 ...
- Android init第三、四部分详细分析
本文一定要在详细阅读了,系列的第二篇文章时候,再来阅读. /init程序第三部分 action_for_each_trigger("early-init", action_add_ ...
- Android 11:bluetooth@1.0蓝牙架构分析
介绍结 参考:蓝牙 | Android 开源项目 | Android Open Source Projecthttps://source.android.com/docs/core/conne ...
最新文章
- zabbix服务器性能监控工具的安装一
- ClearTextBox.Text
- 完成MSP430的IAP升级程序(总结)
- [Linux]守护进程(精灵进程)
- 电气技术应用和计算机应用,电气技术应用专业介绍-1.ppt
- php可以支持代码重用技术的命令,Linux下的编程 PHP高级技巧全放送(一)
- CheckStyle检查规则模板说明
- 这个情人节,工程师用阿里云来试着表达不一样的爱意 1
- exchange server 2013 owa界面语言修改为中文
- c语言课程设计酒店管理系统实验报告 免费下载,C语言酒店管理系统设计
- html5 游戏营销,五大H5游戏营销成功案例,你都玩过了吗?
- 水倒七分、茶倒八分、酒倒满
- 三星S7edge从8.0降到6.0.1,只为流畅的飞一般的感觉
- Java学习-集合类
- 技术部负责人的爱恨情仇
- 简述python的书写规则_python3 基本书写规范
- 2022疫情缩水,啃透这份399页Java架构知识点笔记,已从13K涨到25K
- 《论语》原文及其全文翻译 学而篇11
- 力扣121、122、309、714(C语言版)动态规划股票问题
- 全息生成的体光栅的严格模拟