关键词:蓝牙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
一、蓝牙扫描常用的方法:
蓝牙扫描的可以通过两种途径实现:命令行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
@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部分
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); 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中
(1)、看到这份log我们也许会更明白其他功能的由来: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]); } }
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 中:
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); } } } }