編輯:關於Android編程
在上一篇文章中,我們詳細分析了android是如何解析藍牙反饋上來的搜索到的設備信息,本文將會繼續分析這些信息到了上層之後是如何處理。
8、inquiry result引起的上層變化
我們知道inquiry result引起的上層變化是通過向上層回報device found的signal來實現的。在jni層收到這個signal之後,會調用java層的onDeviceFound接口,這個地方為什麼會調用我就不詳細解釋了,看了我之前的文章,這個地方應該是輕車熟路了。直接分析onDeviceFound函數:
private void onDeviceFound(String address, String[] properties) { if (properties == null) { Log.e(TAG, "ERROR: Remote device properties are null"); return; } //把address和對應的properties保存 addDevice(address, properties); } private void addDevice(String address, String[] properties) { BluetoothDeviceProperties deviceProperties = mBluetoothService.getDeviceProperties(); //保存address和對應的properties deviceProperties.addProperties(address, properties); //得到rssi,class,name String rssi = deviceProperties.getProperty(address, "RSSI"); String classValue = deviceProperties.getProperty(address, "Class"); String name = deviceProperties.getProperty(address, "Name"); short rssiValue; // For incoming connections, we don't get the RSSI value. Use a default of MIN_VALUE. // If we accept the pairing, we will automatically show it at the top of the list. if (rssi != null) { rssiValue = (short)Integer.valueOf(rssi).intValue(); } else { //得到short的最小值 rssiValue = Short.MIN_VALUE; } if (classValue != null) { //有clasevalue我們會產生action_found的broadcast Intent intent = new Intent(BluetoothDevice.ACTION_FOUND); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); intent.putExtra(BluetoothDevice.EXTRA_CLASS, new BluetoothClass(Integer.valueOf(classValue))); intent.putExtra(BluetoothDevice.EXTRA_RSSI, rssiValue); intent.putExtra(BluetoothDevice.EXTRA_NAME, name); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } else { //對於不知道class的設備,我們並不會上報 log ("ClassValue: " + classValue + " for remote device: " + address + " is null"); } }
很明顯,一般而言,會把這些信息通過action_found的broadcast回報到上層的app,供對應的app使用。我們去settings中看看吧,這個broadcast該如何處理啊,呵呵~~
在settings中對這個broadcast的處理就只有一個:
addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
所以,我們直接去看DeviceFoundHandler是怎麼實現的:
//device found的處理 private class DeviceFoundHandler implements Handler { public void onReceive(Context context, Intent intent, BluetoothDevice device) { //得到rssi,class,name short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS); String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); // TODO Pick up UUID. They should be available for 2.1 devices. // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1. //cachedDevice中是否有對應的設備 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); if (cachedDevice == null) { //若是沒有,則把device加進去 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device); Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: " + cachedDevice); // callback to UI to create Preference for new device //創建對應的preference dispatchDeviceAdded(cachedDevice); } //設置cached device的屬性,並顯示出來 cachedDevice.setRssi(rssi); cachedDevice.setBtClass(btClass); cachedDevice.setName(name); cachedDevice.setVisible(true); } }
所以,這裡就是在ui上顯示對應的搜索到的內容了。
到這裡,我們就完成從點擊搜索到開始顯示第一個搜索到的設備之間的流程的分析。下面就是接著搜索到的設備的顯示,這個操作是雷同的,直到搜索結束。底層表示搜索結束的event是inquiry complete,收到這個event表示我們的搜索就結束了,那麼上層對這個event是如何處理的呢?我們繼續分析。
9、inquiry complete event的處理
在分析inquirycomplete之前,我們同樣來看一下spec中是如何定義這個event的:
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHAgYWxpZ249"left">看起來很簡單啊,就返回了一個status的參數,這個參數若是為0就表示success,若是不為0則根據返回值有各種返回錯誤碼。在core
spec中類似只返回status的event還是蠻多的,後面遇到的話我就只提一下,不會再截圖了哦,大家可以自己去看具體的spec。
具體的inquiry complete event的處理如下:
static inline void inquiry_complete_evt(int index, uint8_t status) { int adapter_type; struct btd_adapter *adapter; //inquiry失敗 if (status) { error("Inquiry Failed with status 0x%02x", status); return; } //找到對應的adapter adapter = manager_find_adapter_by_id(index); if (!adapter) { error("No matching adapter found"); return; } //得到對應的type,le or bredr adapter_type = get_adapter_type(index); if (adapter_type == BR_EDR_LE && adapter_has_discov_sessions(adapter)) { //LE我們忽略 int err = hciops_start_scanning(index, TIMEOUT_BR_LE_SCAN); if (err < 0) set_state(index, DISCOV_HALTED); } else { //bredr設置state位halted set_state(index, DISCOV_HALTED); } }
9.1 設置state為DISCOV_HALTED
這個state的設置代碼如下:
switch (dev->discov_state) { case DISCOV_HALTED: //得到adapter的state if (adapter_get_state(adapter) == STATE_SUSPENDED) return; //resolv name是否enable,並且有discov的session if (is_resolvname_enabled() && adapter_has_discov_sessions(adapter)) //resolve name adapter_set_state(adapter, STATE_RESOLVNAME); else //否則就直接設為idle了 adapter_set_state(adapter, STATE_IDLE); break; 這裡其實就是去進行resolve name: case STATE_RESOLVNAME: resolve_names(adapter); break;
9.2 resolve name的分析
static inline void resolve_names(struct btd_adapter *adapter) { int err; //這個必須先設置adapter的state才行 if (adapter->state != STATE_RESOLVNAME) return; err = adapter_resolve_names(adapter); //err < 0是表示有錯誤,這裡設為idle,沒有錯誤的情況下,這裡是不會走到idle的哦 if (err < 0) adapter_set_state(adapter, STATE_IDLE); } int adapter_resolve_names(struct btd_adapter *adapter) { struct remote_dev_info *dev, match; int err; /* Do not attempt to resolve more names if on suspended state */ if (adapter->state == STATE_SUSPENDED) return 0; memset(&match, 0, sizeof(struct remote_dev_info)); bacpy(&match.bdaddr, BDADDR_ANY); match.name_status = NAME_REQUIRED; //遍歷found device中name_required的設備 dev = adapter_search_found_devices(adapter, &match); if (!dev) return -ENODATA; /* send at least one request or return failed if the list is empty */ do { /* flag to indicate the current remote name requested */ dev->name_status = NAME_REQUESTED; //這個應該就是發送remote name request了 err = adapter_ops->resolve_name(adapter->dev_id, &dev->bdaddr); //成功直接返回 if (!err) break; error("Unable to send HCI remote name req: %s (%d)", strerror(errno), errno); /* if failed, request the next element */ /* remove the element from the list */ //faile,就直接把這個設備從found device中remove掉 adapter_remove_found_device(adapter, &dev->bdaddr); /* get the next element */ //找下一個name request的設備 dev = adapter_search_found_devices(adapter, &match); } while (dev); return err; }
Hciops中resolve name的實現:
static int hciops_resolve_name(int index, bdaddr_t *bdaddr) { struct dev_info *dev = &devs[index]; remote_name_req_cp cp; char addr[18]; ba2str(bdaddr, addr); DBG("hci%d dba %s", index, addr); memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, bdaddr); cp.pscan_rep_mode = 0x02; //remote name request command的發送 if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ, REMOTE_NAME_REQ_CP_SIZE, &cp) < 0) return -errno; return 0; }
所以,在收到inquiry comple的event之後,我們並沒有立即通知上層結束掃描,而是在繼續地發送remote name request的command去繼續得到設備的名字。而此時,上層根本不知道有這些東西在,它還是老老實實地轉著圈圈等待搜索的結束。
若您覺得該文章對您有幫助,請在下面用鼠標輕輕按一下“頂”,哈哈~~·
一、概述 在上一篇博文中,我們給大家介紹了Android自定義控件系列的基礎篇。鏈接:http://www.cnblogs.com/jerehedu/p/4360
=============================== 准備 1,導入銀聯支付libs:UPPayAssistEx.jar;UPPayPluginEx.jar;
模仿小米安全中心檢測效果廢話少說,咱們先上效果圖:github地址: https://github.com/niniloveyou/GradeProgressView這個
一個Android項目包含了Android app代碼在內的所有文件。Android SDK工具提供默認的項目目錄和文件讓創建一個項目變得很簡單。 這篇課程會向大家展