android4.0藍牙使能的詳細解析
內容簡介:本文詳細分析了android4.0 中藍牙使能的過程,相比較android2.3,4.0中的藍牙最大的差別在于UI上on/off的偽開關。在android4.0中加入了 adapter的狀態機。所謂的狀態機就類似于狀態轉換圖,在一個狀態收到某個特定的命令會變成另外一個狀態,不同的命令可以跳轉到不同的狀態(當然也有 可能到同一狀態)。adapter的初始狀態為poweroff,在android系統啟動的時候會進入warmup狀態,同時會進行UUID的add, 該操作會引起propertychanged的UUID signal,該signal會使得狀態從warmup變換到hotoff狀態。因此在UI端off時其實adapter已經處于hotoff狀態而不是 poweroff狀態。這一點是很關鍵的。在正文中,我會從假如我不知道這些開始來描繪整個使能的過程。
正文:
毫無疑問,bluetooth的打開是在Settings中進行的操作。因此,冤有頭,債有主,我們來到了Settings.java中,果然發現了相關的代碼如下:
mBluetoothEnabler =new BluetoothEnabler(context, new Switch(context));
于是,我們得以進入真正的藍牙操作的殿堂,好好進去看看吧。
1、BluetoothEnabler的構造函數 public BluetoothEnabler(Context context,Switch switch_) { mContext = context; mSwitch = switch_; //很簡單了,去調用一個LocalBluetoothManager類的getInstance,其實會構造該類的 LocalBluetoothManager manager =LocalBluetoothManager.getInstance(context); if (manager == null) { // Bluetooth is not supported mLocalAdapter = null; mSwitch.setEnabled(false); } else { //構造成功后,通過manager得到bluetooth的adapter mLocalAdapter =manager.getBluetoothAdapter(); } //同時新建一個intent,用于接收ACTION_STATE_CHANGED mIntentFilter = newIntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); } 2、LocalBluetoothManager類的getInstance public static synchronizedLocalBluetoothManager getInstance(Context context) { if (sInstance == null) { //2.1同樣的,這個會去調用LocalBluetoothAdapter的getInstance,也會構造該類 LocalBluetoothAdapter adapter =LocalBluetoothAdapter.getInstance(); if (adapter == null) { return null; } // This will be around as long asthis process is Context appContext =context.getApplicationContext(); //2.2構造LocalBluetoothManager類 sInstance = newLocalBluetoothManager(adapter, appContext); } return sInstance; } 2.1LocalBluetoothAdapter的getInstance static synchronized LocalBluetoothAdaptergetInstance() { if (sInstance == null) { //2.1.1通過BluetoothAdapter得到DefaultAdapter BluetoothAdapter adapter =BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { //2.1.2若有該DefaultAdapter,則構造LocalBluetoothAdapter sInstance = newLocalBluetoothAdapter(adapter); } } return sInstance; } 2.1.1BluetoothAdapter得到DefaultAdapter public static synchronized BluetoothAdaptergetDefaultAdapter() { if (sAdapter == null) { IBinder b =ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); if (b != null) { IBluetooth service =IBluetooth.Stub.asInterface(b); sAdapter = newBluetoothAdapter(service); } } return sAdapter; } 2.1.2構造LocalBluetoothAdapter //其實就是 mAdapter的初始化而已 privateLocalBluetoothAdapter(BluetoothAdapter adapter) { mAdapter = adapter; } 2.2構造LocalBluetoothManager類 //管理本地藍牙類,用來在藍牙API子類上面再封裝一個接口 privateLocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) { mContext = context; //mLocalAdapter初始化為DefaultAdapter中得到的值 mLocalAdapter= adapter; //構造CachedBluetoothDeviceManager,用來管理遠程藍牙設備 mCachedDeviceManager = newCachedBluetoothDeviceManager(context); //2.2.1構建BluetoothEventManager,該類是用來管理廣播消息和回調函數的,即分發不同的消息去對UI進行處理 mEventManager = newBluetoothEventManager(mLocalAdapter, mCachedDeviceManager, context); //2.2.2該類提供對不同LocalBluetoothProfile object的訪問 mProfileManager = newLocalBluetoothProfileManager(context, mLocalAdapter,mCachedDeviceManager, mEventManager); } 2.2.1構建BluetoothEventManager BluetoothEventManager(LocalBluetoothAdapteradapter, CachedBluetoothDeviceManagerdeviceManager, Context context) { mLocalAdapter = adapter; mDeviceManager = deviceManager; //創建兩個IntentFilter mAdapterIntentFilter = newIntentFilter(); //這里沒有對mProfileIntentFilter進行初始化,這個在LocalBluetoothProfileManager的addProfile中實現 mProfileIntentFilter = newIntentFilter(); //創建一個Handler的Hash表 mHandlerMap = new HashMap<String,Handler>(); mContext = context; //注冊對adapter和Device的幾個廣播消息的處理回調函數 //add action到mAdapterIntentFilter // Bluetooth on/off broadcasts addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, newAdapterStateChangedHandler()); // Discovery broadcasts addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, newScanningStateChangedHandler(true)); addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, newScanningStateChangedHandler(false)); addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler()); addHandler(BluetoothDevice.ACTION_DISAPPEARED, newDeviceDisappearedHandler()); addHandler(BluetoothDevice.ACTION_NAME_CHANGED, newNameChangedHandler()); // Pairing broadcasts addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, newBondStateChangedHandler()); addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, newPairingCancelHandler()); // Fine-grained state broadcasts addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, newClassChangedHandler()); addHandler(BluetoothDevice.ACTION_UUID,new UuidChangedHandler()); // Dock event broadcasts addHandler(Intent.ACTION_DOCK_EVENT,new DockEventHandler()); //mAdapterIntentFilter的接收處理函數 mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter); } 2.2.2構造LocalBluetoothProfileManager類 LocalBluetoothProfileManager(Contextcontext, LocalBluetoothAdapter adapter, CachedBluetoothDeviceManagerdeviceManager, BluetoothEventManager eventManager){ mContext = context; //各個類之間進行關聯 mLocalAdapter = adapter; mDeviceManager = deviceManager; mEventManager = eventManager; // pass this reference to adapter andevent manager (circular dependency) mLocalAdapter.setProfileManager(this); mEventManager.setProfileManager(this); ParcelUuid[] uuids =adapter.getUuids(); // uuids may be null if Bluetooth isturned off if (uuids != null) { //假如已經有了uuid,根據uuid來add并new對應的profile,只針對A2DP,HFP,HSP,OPP四個profile,HID和PAN在下面,每次都add updateLocalProfiles(uuids); } // Always add HID and PAN profiles //加入HID和PAN兩個profile mHidProfile = new HidProfile(context,mLocalAdapter); addProfile(mHidProfile,HidProfile.NAME, BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); mPanProfile = new PanProfile(context); addPanProfile(mPanProfile,PanProfile.NAME, BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); Log.d(TAG,"LocalBluetoothProfileManager construction complete"); } 好吧,其實我們被騙了,剛剛只是一個路引,不是真正的操作,真正的操作向來都是從你滑動界面那個on/off鍵開始的,因此我們決定把這個鍵的處理給揪出來。在Settings界面上一共就只有兩個on/off鍵,一個是wifi,另一個就是藍牙了,我們從這個代碼入手: case HEADER_TYPE_SWITCH: //其實寫這個代碼的人也比較心虛,假如switch多一點,下面就要重寫了 // Would need a differenttreatment if the main menu had more switches if (header.id ==R.id.wifi_settings) { mWifiEnabler.setSwitch(holder.switch_); } else { //這個就是處理了,上面的路引沒有白做啊 mBluetoothEnabler.setSwitch(holder.switch_); } 3、mBluetoothEnabler.setSwitch分析 public void setSwitch(Switch switch_) { //若是和上次相同,則不做任何事情,可以理解,代碼也懶嘛 if (mSwitch == switch_) return; //把上次的switch的changelistener清空 mSwitch.setOnCheckedChangeListener(null); mSwitch = switch_; //重設這次的switch的changelistener mSwitch.setOnCheckedChangeListener(this); int bluetoothState =BluetoothAdapter.STATE_OFF; //獲取getBluetoothState,這個過程也會同步一下state,防止改變 if (mLocalAdapter != null)bluetoothState = mLocalAdapter.getBluetoothState(); //根據狀態設置一下兩個標志位 boolean isOn = bluetoothState ==BluetoothAdapter.STATE_ON; boolean isOff = bluetoothState ==BluetoothAdapter.STATE_OFF; //設置checked的狀態位。注意,假如這里狀態發生了改變,則會調用this.onCheckedChanged來進行處理 mSwitch.setChecked(isOn); if(WirelessSettings.isRadioAllowed(mContext, Settings.System.RADIO_BLUETOOTH)) { //有bluetooth或者不是airplane,則該switch不變灰,否則,灰的。 mSwitch.setEnabled(isOn || isOff); } else { mSwitch.setEnabled(false); } } 4、onCheckedChanged 在switch狀態發生改變后,會調用這個地方的回調函數進行處理。 public void onCheckedChanged(CompoundButtonbuttonView, boolean isChecked) { // Show toast message if Bluetooth isnot allowed in airplane mode //若是打開的話,就需要檢查一下是否allow Bluetooth(radio,airplane的check) if (isChecked && !WirelessSettings.isRadioAllowed(mContext,Settings.System.RADIO_BLUETOOTH)) { Toast.makeText(mContext,R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show(); // Reset switch to off //若是不對的話,reset為off buttonView.setChecked(false); } if (mLocalAdapter != null) { //4.1設置scanmode,放心,它會判斷state的,不是STATE_ON,會直接返回false的 mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); //4.2使能或不使能Bluetooth了 mLocalAdapter.setBluetoothEnabled(isChecked); } //過程中還是會反灰,直到setBluetoothEnabled的結果返回會改變switch的狀態 mSwitch.setEnabled(false); } 4.1設置scanmod 會調用adapter中的setScanMode,直接去看就可以了,事實上就是設置了兩個property標志,沒什么 public boolean setScanMode(int mode) { //這里把這個代碼寫出來就是證明一下,STATE_ON才會真正做下去,否則免談 if (getState() != STATE_ON) returnfalse; //這里會調用對應server中的setScanMode return setScanMode(mode, 120); } public synchronized boolean setScanMode(intmode, int duration) { //這里有個permission,好像和2.3中不一樣,注意一下 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS, "NeedWRITE_SECURE_SETTINGS permission"); boolean pairable; boolean discoverable; switch (mode) { case BluetoothAdapter.SCAN_MODE_NONE: pairable = false; discoverable = false; break; caseBluetoothAdapter.SCAN_MODE_CONNECTABLE: //開始就是這里了,可pairable,但是不可discoverable pairable = true; discoverable = false; break; caseBluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: pairable = true; discoverable = true; if (DBG) Log.d(TAG, "BTDiscoverable for " + duration + " seconds"); break; default: Log.w(TAG, "Requested invalidscan mode " + mode); return false; } //設置這兩個property標志 setPropertyBoolean("Discoverable", discoverable); setPropertyBoolean("Pairable", pairable); return true; } 4.2setBluetoothEnabled分析 public void setBluetoothEnabled(booleanenabled) { //根據enabled的標志設置是enable還是disable,在2.3中,這個地方就是bt_enable哦,這里還不知道,我們在第5步進行詳細的分析 boolean success = enabled ? mAdapter.enable() : mAdapter.disable(); //成功了,設置對應的狀態位 if (success) { setBluetoothStateInt(enabled ?BluetoothAdapter.STATE_TURNING_ON :BluetoothAdapter.STATE_TURNING_OFF); } else { if (Utils.V) { Log.v(TAG,"setBluetoothEnabled call, manager didn't return " + "success forenabled: " + enabled); } //同步一下設置的狀態 syncBluetoothState(); } } } 5、mAdapter.enable或者mAdapter.disable 就先分析enable吧,它會調用對應server端的enable(ture),我們來看看源碼 public synchronized boolean enable(booleansaveSetting) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); // Airplane mode can prevent Bluetoothradio from being turned on. //檢查是否是飛行模式 if (mIsAirplaneSensitive &&isAirplaneModeOn() && !mIsAirplaneToggleable) { return false; } //5.1注意與2.3的不同,在2.3中,這里會調用enablethread去調用native的bt_enable,而4.0沒有這么做。沒事,我們來分析4.0怎么做的。 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON,saveSetting); return true; } 5.1mBluetoothState.sendMessage 簡單理解一下,mBluetoothState是BluetoothAdapterStateMachine類。因此,在分析的之前,簡單說一下,它其實就是類似一個狀態轉換圖,根據你所處于的狀態,然后再判斷收到的操作,進行不同的處理。根據構造函數中的setInitialState(mPowerOff);可以知道初始狀態是PowerOff。但是從它給出的狀態機可以看出,在PowerOff的狀態時,它是通過TURN_HOT/TURN_ON來改變到HotOff狀態的,然后才會收到USER_TURN_ON,去該變到BluetootOn的狀態。因此,可以肯定的是我們這里的USER_TURN_ON不是它收到的第一個message,因此我們去糾結一下它是從哪里開始改變PowerOff的狀態:extra1,然后再來看這里的處理吧:5.2。 extra1、mAdapter.enable之前的狀態機轉變 眾所周知,android在啟動之后會啟動一個serverThread的線程,通過這個線程會啟動一系列的服務。我們的藍牙服務也是在這里啟動的,android4.0其實在這個地方對狀態機進行了修改,我們來看一下源碼: 該代碼位于framworks/base/services/java/com/android/server/systemserver.java BluetoothServicebluetooth = null; BluetoothA2dpServicebluetoothA2dp = null; //模擬器上是不支持Bluetooth的,工廠測試模式也沒有Bluetooth(這個不了解) // Skip Bluetooth if we have anemulator kernel // TODO: Use a more reliable checkto see if this product should // support Bluetooth - see bug988521 if(SystemProperties.get("ro.kernel.qemu").equals("1")) { Slog.i(TAG, "No BluetoohService (emulator)"); } else if (factoryTest ==SystemServer.FACTORY_TEST_LOW_LEVEL) { Slog.i(TAG, "No BluetoothService (factory test)"); } else { Slog.i(TAG, "BluetoothService"); //新建Bluetoothservice,并把他加入到ServiceManager中 bluetooth = newBluetoothService(context); ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE,bluetooth); //extra1.1在啟動Bluetooth服務后進行一些初始化,呵呵,這里就對狀態機進行了改變 bluetooth.initAfterRegistration(); //新建了BluetoothA2dpService,并把之加入到了ServiceManager中 bluetoothA2dp= new BluetoothA2dpService(context, bluetooth); ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE, bluetoothA2dp); //extra1.2同樣的要在之后做些init的工作 bluetooth.initAfterA2dpRegistration(); //得到是否飛行 int airplaneModeOn =Settings.System.getInt(mContentResolver, Settings.System.AIRPLANE_MODE_ON, 0); //看Bluetooth是否on,若是打開的狀態(沒有飛行),則這里會調用enable去打開 int bluetoothOn =Settings.Secure.getInt(mContentResolver, Settings.Secure.BLUETOOTH_ON, 0); if (airplaneModeOn == 0&& bluetoothOn != 0) { bluetooth.enable(); } } extra1.1initAfterRegistration分析 public synchronized voidinitAfterRegistration() { //得到default的adapter mAdapter =BluetoothAdapter.getDefaultAdapter(); //創建BluetoothAdapterStateMachine,初始化幾個狀態,并設初始狀態位POWEROFF,這里同時新建了一個EventLoop mBluetoothState = newBluetoothAdapterStateMachine(mContext, this, mAdapter); mBluetoothState.start(); //根據這個xml的bool變量來決定是否先期TURN_HOT,該變量位于frameworks/base/core/res/res/values/config.xml中,默認為true if (mContext.getResources().getBoolean (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { //extra1.2發送TURN_HOT的狀態變化message mBluetoothState.sendMessage(BluetoothAdapterStateMachine.TURN_HOT); } //得到對應的EventLoop mEventLoop =mBluetoothState.getBluetoothEventLoop(); } extra1.2 TURN_HOT message的處理 /** * Bluetooth module's power is off,firmware is not loaded. */ private class PowerOff extends State { @Override public void enter() { if (DBG) log("Enter PowerOff:" + getCurrentMessage().what); } @Override public boolean processMessage(Messagemessage) { log("PowerOff process message:" + message.what); boolean retValue = HANDLED; switch(message.what) { …… case TURN_HOT: //extra1.3這里就是我們尋找了千年的bt_enable所在的地方。我們去看看 if (prepareBluetooth()) { //extra1.5轉變狀態到warmup,在prepareBluetooth真正完成后,這個狀態還會發生改變 transitionTo(mWarmUp); } break; …… extra1.3prepareBluetooth分析 看英文注釋就知道了,不解釋 /** * Turn on Bluetooth Module, Loadfirmware, and do all the preparation * needed to get the Bluetooth Moduleready but keep it not discoverable * and not connectable. * The last step of this method sets upthe local service record DB. * There will be a event reporting thestatus of the SDP setup. */ private boolean prepareBluetooth() { //extra1.4首先還是調用了enableNative的本地方法,到這里你會發現終于和2.3相似了(不過請注意調用的時機不同了,這個在初始化,而2.3在界面的on/off滑動的時候),它還是會調用bt_enable,這個就會調用對應的set_bluetooth_power了 if(mBluetoothService.enableNative() != 0) { return false; } // try to start event loop, give 2attempts //嘗試兩次去start event loop int retryCount = 2; boolean eventLoopStarted = false; while ((retryCount-- > 0)&& !eventLoopStarted) { mEventLoop.start(); // it may take a moment for theother thread to do its // thing. Check periodically for a while. int pollCount = 5; while ((pollCount-- > 0)&& !eventLoopStarted) { if(mEventLoop.isEventLoopRunning()) { eventLoopStarted =true; break; } try { Thread.sleep(100); } catch(InterruptedException e) { log("prepareBluetooth sleep interrupted: " + pollCount); break; } } } //出錯處理 if (!eventLoopStarted) { mBluetoothService.disableNative(); return false; } // get BluetoothService ready //建立native data以及SDP相關的一些操作,這里將會產生PropertyChanged的UUIDs的signal,對該信號的處理會對狀態發生改變,詳細分析見extra1.5 if(!mBluetoothService.prepareBluetooth()) { mEventLoop.stop(); mBluetoothService.disableNative(); return false; } //設置一個prepare的超時處理,在該時間內沒有收到UUID changed的signal將會進行錯誤處理 sendMessageDelayed(PREPARE_BLUETOOTH_TIMEOUT,PREPARE_BLUETOOTH_TIMEOUT_TIME); return true; } } extra1.4bt_enable分析 intbt_enable() { LOGV(__FUNCTION__); int ret = -1; int hci_sock = -1; int attempt; //power的設置,on。不解釋,可加入對應板子的gpio口的處理,默認就只用了rfkill的處理 if (set_bluetooth_power(1) < 0) gotoout; //開始hciattach服務,這個我們也做了修改,加入了rtk_h5 LOGI("Starting hciattachdaemon"); if (property_set("ctl.start","hciattach") < 0) { LOGE("Failed to starthciattach"); set_bluetooth_power(0); goto out; } // Try for 10 seconds, this can onlysucceed once hciattach has sent the // firmware and then turned on hci devicevia HCIUARTSETPROTO ioctl for (attempt = 1000; attempt > 0; attempt--) { //創建hci_sock hci_sock = create_hci_sock(); if (hci_sock < 0) goto out; //調用ioctl的HCIDEVUP,來判斷hciattach是否已經ok了。 ret = ioctl(hci_sock, HCIDEVUP,HCI_DEV_ID); LOGI("bt_enable: ret: %d, errno:%d", ret, errno); if (!ret) { break; } else if (errno == EALREADY) { LOGW("Bluetoothd alreadystarted, unexpectedly!"); break; } close(hci_sock); //等待10 ms后再試一次 usleep(100000); // 100 ms retry delay } //10s都沒有搞定,需要做個失敗的處理 if (attempt == 0) { LOGE("%s: Timeout waiting for HCIdevice to come up, error- %d, ", __FUNCTION__, ret); if (property_set("ctl.stop","hciattach") < 0) { LOGE("Error stoppinghciattach"); } set_bluetooth_power(0); goto out; } //啟動bluetoothd服務 LOGI("Starting bluetoothddeamon"); if (property_set("ctl.start","bluetoothd") < 0) { LOGE("Failed to startbluetoothd"); set_bluetooth_power(0); goto out; } ret = 0; out: //關閉hci_sock if (hci_sock >= 0) close(hci_sock); return ret; } extra 1.5 PropetyChanged的UUIDs的處理 event_filter是用來對bluez的dbus的signal進行監聽的,有signal產生后,會在這里進行處理。因此,我們直接到這里看看該怎么處理。 //Called by dbus during WaitForAndDispatchEventNative() staticDBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void*data) { native_data_t *nat; JNIEnv *env; DBusError err; DBusHandlerResult ret; //err的一個初始化 dbus_error_init(&err); //得到參數 nat = (native_data_t *)data; nat->vm->GetEnv((void**)&env,nat->envVer); if (dbus_message_get_type(msg) !=DBUS_MESSAGE_TYPE_SIGNAL) { LOGV("%s: not interested (not asignal).", __FUNCTION__); returnDBUS_HANDLER_RESULT_NOT_YET_HANDLED; } LOGV("%s: Received signal %s:%s from%s", __FUNCTION__, dbus_message_get_interface(msg),dbus_message_get_member(msg), dbus_message_get_path(msg)); env->PushLocalFrame(EVENT_LOOP_REFS); …… //PropertyChanged這個signal的處理 } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) { //由msg解析參數 jobjectArray str_array =parse_adapter_property_change(env, msg); 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); //檢查Property是否started if (!strncmp(c_property,"Powered", strlen("Powered"))) { //若是powered,則看value是否是true,是ture就得到對應的path 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); //extra1.6調用對應的method_onPropertyChanged函數,該method對應的onPropertyChanged函數 env->CallVoidMethod(nat->me, method_onPropertyChanged, str_array); } elseLOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); goto success; …… extra1.6真正的處理函數onPropertyChanged分析 /** * Called by native code on aPropertyChanged signal from * org.bluez.Adapter. This method is alsocalled from * {@link BluetoothAdapterStateMachine} toset the "Pairable" * property when Bluetooth is enabled. * * @param propValues a string arraycontaining the key and one or more * values. */ /*package*/ void onPropertyChanged(String[]propValues) { BluetoothAdapterPropertiesadapterProperties = mBluetoothService.getAdapterProperties(); //先fill up cache if (adapterProperties.isEmpty()) { // We have got a property changebefore // we filled up our cache. adapterProperties.getAllProperties(); } log("Property Changed: " +propValues[0] + " : " + propValues[1]); String name = propValues[0]; …… //對UUIDs的處理 } else if(name.equals("Devices") || name.equals("UUIDs")) { String value = null; int len =Integer.valueOf(propValues[1]); if (len > 0) { StringBuilder str = newStringBuilder(); for (int i = 2; i <propValues.length; i++) { str.append(propValues[i]); str.append(","); } value = str.toString(); } //把name和value值加入到property的map中 adapterProperties.setProperty(name,value); //extra1.7有UUIDs的change signal會刷新Bluetooth的State if (name.equals("UUIDs")){ mBluetoothService.updateBluetoothState(value); } //對Pairable和Discoverable的處理 } else if(name.equals("Pairable") || name.equals("Discoverable")) { adapterProperties.setProperty(name,propValues[1]); if(name.equals("Discoverable")) { //5.6發送SCAN_MODE_CHANGED的msg,去改變狀態機 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SCAN_MODE_CHANGED); } //設置對應的property String pairable =name.equals("Pairable") ? propValues[1] : adapterProperties.getProperty("Pairable"); String discoverable =name.equals("Discoverable") ? propValues[1] : adapterProperties.getProperty("Discoverable"); // This shouldn't happen, unlessAdapter Properties are null. if (pairable == null ||discoverable == null) return; int mode =BluetoothService.bluezStringToScanMode( pairable.equals("true"), discoverable.equals("true")); if (mode >= 0) { //當pairable和discoverable均為true的時候,會發送一個ACTION_SCAN_MODE_CHANGED的廣播消息 Intent intent = newIntent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mode); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent,BLUETOOTH_PERM); } } …… extra1.7 UUIDs改變帶來的State的刷新 /** * This function is called from BluetoothEvent Loop when onPropertyChanged * for adapter comes in with UUID property. * @param uuidsThe uuids of adapter asreported by Bluez. */ /*package*/ synchronized voidupdateBluetoothState(String uuids) { ParcelUuid[] adapterUuids =convertStringToParcelUuid(uuids); //為什么必須包含所有已經有的uuid??感覺有點反了,再看看 if (mAdapterUuids != null && BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) { //放SERVICE_RECORD_LOADED的信息,此時,處于warm up狀態,看extra1.8分析狀態如何繼續改變 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SERVICE_RECORD_LOADED); } } extra1.8 UUIDs對狀態機改變 /** * Turning on Bluetooth module's power,loading firmware, starting * event loop thread to listen on Bluetoothmodule event changes. */ private class WarmUp extends State { @Override public void enter() { if (DBG) log("Enter WarmUp:" + getCurrentMessage().what); } @Override public boolean processMessage(Messagemessage) { log("WarmUp process message:" + message.what); boolean retValue = HANDLED; switch(message.what) { case SERVICE_RECORD_LOADED: //可以看到,首先會把當時從poweroff過來的一個超時message拿remove了。 removeMessages(PREPARE_BLUETOOTH_TIMEOUT); //轉到hotoff狀態,在hotoff狀態仍會接收到多個SERVICE_RECORD_LOADED的msg,但是那個狀態下該msg將沒有任何handled,因此會一直處于hotoff狀態 transitionTo(mHotOff); break; …… 5.2mAdapter.enable中mBluetoothState.sendMessage后的狀態機處理 由extra的分析可知,此時,Bluetooth的State已經處于HotOff狀態了,所以,從這里開始處理State的變換。 /** * Bluetooth Module has powered, firmwareloaded, event loop started, * SDP loaded, but the modules staysnon-discoverable and * non-connectable. */ private class HotOff extends State { @Override public void enter() { if (DBG) log("Enter HotOff:" + getCurrentMessage().what); } @Override public boolean processMessage(Messagemessage) { log("HotOff process message:" + message.what); boolean retValue = HANDLED; switch(message.what) { case USER_TURN_ON: //發出BluetoothAdapter.STATE_TURNING_ON的廣播消息 broadcastState(BluetoothAdapter.STATE_TURNING_ON); if ((Boolean) message.obj){ //就是把Settings.Secure.BLUETOOTH_ON設為1。用于標志Bluetooth enable了 persistSwitchSetting(true); } // let it fall toTURN_ON_CONTINUE: //$FALL-THROUGH$ //注意上面沒有break哦 case TURN_ON_CONTINUE: //這里就是把Bluetooth設為connectable就是Powered=1,這里就把prepareBluetooth中設置的不可連接重新設置回來了。這個重連會產生一些新的變化,它會發送WRITE_SCAN_ENABLE的cmd,因此在該cmd_complete時會有一些新的處理:5.3,它會再次引起狀態機的改變:5.6 mBluetoothService.switchConnectable(true); //進入到Switching狀態 transitionTo(mSwitching); break; …… 5.3 WRITE_SCAN_ENABLE在cmd_complete后的處理 在bluez中是用cmd_complete函數來監視發出cmd完成后的處理的。該函數具體如下: staticinline void cmd_complete(int index, void *ptr) { structdev_info *dev = &devs[index]; evt_cmd_complete*evt = ptr; uint16_topcode = btohs(evt->opcode); uint8_tstatus = *((uint8_t *) ptr + EVT_CMD_COMPLETE_SIZE); switch(opcode) { …… //WRITE_SCAN_ENABLE命令完成的處理函數,會再發一個READ_SCAN_ENABLE的命令 casecmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE): hci_send_cmd(dev->sk,OGF_HOST_CTL, OCF_READ_SCAN_ENABLE, 0,NULL); break; //5.4緊接著就是對READ_SCAN_ENABLE命令完成的處理,它是通過read_scan_complete來實現的 casecmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE): ptr+= sizeof(evt_cmd_complete); read_scan_complete(index,status, ptr); break; …… } 5.4 read_scan命令完成的處理 staticvoid read_scan_complete(int index, uint8_t status, void *ptr) { structbtd_adapter *adapter; read_scan_enable_rp*rp = ptr; DBG("hci%dstatus %u", index, status); //由index得到對應的adapter adapter= manager_find_adapter_by_id(index); if(!adapter) { error("Unableto find matching adapter"); return; } //5.5這里算是一個通知adapter,mode改變了。 adapter_mode_changed(adapter,rp->enable); } 5.5通知adapter,mode發生了改變 voidadapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode) { constgchar *path = adapter_get_path(adapter); gbooleandiscoverable, pairable; DBG("old0x%02x new 0x%02x", adapter->scan_mode, scan_mode); //若相同,則nothing todo if(adapter->scan_mode == scan_mode){ #ifdefBOARD_HAVE_BLUETOOTH_BCM /*we may reset scan_mode already inbtd_adapter_stop(), so comes to here*/ set_mode_complete(adapter); #endif return; } //把discoverable的timeout清空 adapter_remove_discov_timeout(adapter); //這里開始,是設為SCAN_PAGE| SCAN_INQUIRY switch(scan_mode) { caseSCAN_DISABLED: adapter->mode= MODE_OFF; discoverable= FALSE; pairable= FALSE; break; caseSCAN_PAGE: adapter->mode= MODE_CONNECTABLE; discoverable= FALSE; pairable= adapter->pairable; break; case(SCAN_PAGE | SCAN_INQUIRY): //設一下模式,在有reply要求的情況下,該步驟還是很重要的 adapter->mode= MODE_DISCOVERABLE; discoverable= TRUE; pairable= adapter->pairable; //還要設一個discoverable的時間 if(adapter->discov_timeout != 0) adapter_set_discov_timeout(adapter, adapter->discov_timeout); break; caseSCAN_INQUIRY: /*Address the scenario where a low-level application like * hciconfig changed the scan mode */ if(adapter->discov_timeout != 0) adapter_set_discov_timeout(adapter, adapter->discov_timeout); /*ignore, this event should not be sent */ default: /*ignore, reserved */ return; } /*If page scanning gets toggled emit the Pairable property */ //這里會發一個property_changed的pairable的signal if((adapter->scan_mode & SCAN_PAGE) != (scan_mode & SCAN_PAGE)) emit_property_changed(connection,adapter->path, ADAPTER_INTERFACE,"Pairable", DBUS_TYPE_BOOLEAN,&pairable); if(!discoverable) adapter_set_limited_discoverable(adapter,FALSE); //這里會發一個property_changed的discoverable的signal emit_property_changed(connection,path, ADAPTER_INTERFACE,"Discoverable", DBUS_TYPE_BOOLEAN,&discoverable); adapter->scan_mode= scan_mode; set_mode_complete(adapter); }
5.6 WRTIE_SCAN_ENABLE最終引起的狀態機的變化
在此之前,狀態機處于switching的狀態,收到了SCAN_MODE_CHANGED的msg。
private class Switching extends State { @Override public void enter() { if (DBG) log("Enter Switching:" + getCurrentMessage().what); } @Override public boolean processMessage(Messagemessage) { log("Switching processmessage: " + message.what); boolean retValue = HANDLED; switch(message.what) { case SCAN_MODE_CHANGED: // This event matchesmBluetoothService.switchConnectable action //mPublicState在hotoff到swtiching狀態變化時已經被設為STATE_TURNING_ON了,所以這里if沒有問題 if (mPublicState ==BluetoothAdapter.STATE_TURNING_ON) { // set pairable if it'snot //設置為pairable假如還沒有設置的話,這個會先在bluez中檢查一下當前是否pairable,我們在前面已經設置好了,所以,這里只是一個檢查而已,沒有什么實際性的工作 mBluetoothService.setPairable(); //初始化bond state和profile state,這個會在adapter pairable之后,bluetooth turn on之前發生 mBluetoothService.initBluetoothAfterTurningOn(); //這邊正式進入到bluetoothon的狀態,終于進了這里,哎。。。 transitionTo(mBluetoothOn); //發送STATE_ON的broadcast broadcastState(BluetoothAdapter.STATE_ON); // run bluetooth nowthat it's turned on // Note runBluetoothshould be called only in adapter STATE_ON //連接那些可以自動連接的設備,通知battery,藍牙打開了 mBluetoothService.runBluetooth(); } break; ……
至此,藍牙的使能主要過程已經全部搞定。
原文地址:http://blog.csdn.net/xdyang1987/article/details/7771388
本文由用戶 openkk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!