关键词:蓝牙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

  @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.Adapterstatic 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_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 : trueD BluetoothEventLoop: Property Changed: Pairable : trueD BluetoothEventLoop: Property Changed: Class : 5898764D BluetoothEventLoop: Property Changed: Pairable : trueD BluetoothEventLoop: Property Changed: Discoverable : falseD BluetoothEventLoop: Property Changed: Discovering : trueD BluetoothEventLoop: Property Changed: Discovering : falseD BluetoothEventLoop: Property Changed: Devices : 1D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Connected value: trueD BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Paired value: trueD 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

                                     

1)、ACTION_DISCOVERY_STARTEDACTION_DISCOVERY_FINISHEDAndroidManifest.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 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);                }            }        }    }































更多相关文章

  1. C语言函数的递归(上)
  2. Activity之SharedPreferences探究
  3. android 控件放大缩小效果实现
  4. android Audio设置音量流程及其binder通讯原理
  5. bluethooth BLE Android
  6. LocalBroadcastManager原理解析
  7. Android手机监控应用(一)
  8. android CallCard.java/updateState(Phone phone)分析
  9. Android(安卓)studio 点击按钮跳转到新的Activity

随机推荐

  1. android RxJava(RxAndroid)的简单使用
  2. Spring Tool Suits 配置android 开发环境
  3. 【Android 界面效果42】如何自定义字体
  4. Android(安卓)Gingerbread基于32 bit Ubu
  5. 往Android Studio中import工程出错时的解
  6. Android上的ART虚拟机
  7. 阅读《Android(安卓)从入门到精通》(?)——V
  8. Android adb shell刷机命令实战
  9. Android Volley框架使用详解
  10. android 单元测试, 以sqlite测试为例