Android4.0藍牙使能的詳細解析

毫無疑問,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類的getInstancepublic 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的getInstancestatic 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得到DefaultAdapterpublic 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構建BluetoothEventManagerBluetoothEventManager(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 offif (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 switchesif (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中的setScanModereturn 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.javaBluetoothServicebluetooth = 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 bug988521if(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中,默認為trueif (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 {@Overridepublic void enter() {if (DBG) log("Enter PowerOff:" + getCurrentMessage().what);}@Overridepublic 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 loopint 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.5if(!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 ioctlfor (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_sockif (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是否startedif (!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 cacheif (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的Stateif (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 {@Overridepublic void enter() {if (DBG) log("Enter WarmUp:" + getCurrentMessage().what);}@Overridepublic 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 {@Overridepublic void enter() {if (DBG) log("Enter HotOff:" + getCurrentMessage().what);}@Overridepublic 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 todoif(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);#endifreturn;}//把discoverable的timeout清空
 
adapter_remove_discov_timeout(adapter);//這里開始,是設為SCAN_PAGE| SCAN_INQUIRYswitch(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的signalif((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 {@Overridepublic void enter() {if (DBG) log("Enter Switching:" + getCurrentMessage().what);}@Overridepublic 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://www.cnblogs.com/yaowen/p/4980587.html,如需轉載請自行聯系原作者


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454182.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454182.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454182.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第一次沖刺

本人小組分工角色&#xff1a;產品負責人 本組沖刺訂單介紹&#xff1a;經過小組的成員討論&#xff0c;我們大概確立了一個沖刺的訂單是完成一個簡易的長沙學院網站項目。 最后完成情況概述&#xff1a;經過大約兩周的努力&#xff0c;我們組的編程人員運用python構建了一個簡…

移動端導出excel_連載系列【4】Excel開發移動端quot;APPquot;

前三篇文章介紹了百度地圖生成器、源代碼編輯器、GPS經緯度批量轉換工具、源代碼編輯器中添加自定義功能按鈕和地圖控件。這些寫好的Java Script代碼雖然可以實現所有期望的結果&#xff0c;但畢竟不是一個HTML文件&#xff0c;不便于傳播和使用&#xff0c;更無法變成一個類似…

《操作系統》OS學習(二):啟動、中斷、異常

Bootloader:加載OS。操作系統一開始是放在DISK&#xff08;硬盤&#xff09;中&#xff0c;并不是放在內存中。 BIOS&#xff1a;基本I/O處理系統。存放在ROMRead-Only Memory&#xff09;只讀存儲中 BIOS&#xff08;Basic Input/Output System&#xff09;基本輸入輸出系統。…

[GCC for C]編譯選項---IDE掩蓋下的天空

編譯選項 ---------IDE掩蓋下的天空 /*************************************** * gcc for c language ***************************************/ Single Source to Executable $ gcc helloworld.c [-o howdy] 默認生成的名字a.exe ______________________________________ …

2016級算法第二次上機-F.ModricWang's Number Theory II

891 ModricWangs Number Theory II 思路 使得序列的最大公約數不為1&#xff0c;就是大于等于2&#xff0c;就是找到一個大于等于2的數&#xff0c;它能夠整除序列中的所有數。 考慮使得一個數d整除數組中所有數的代價&#xff1a; 如果一個數不能被b整除&#xff0c;那么可以花…

常用css屬性集(持續更新…)

禁止換行&#xff0c;超出部分顯示…&#xff1a;a. 代碼&#xff1a;.hide_word{ max-width: 100px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } b. 效果&#xff1a; 本文轉自 bilinyee博客&#xff0c;原文鏈接&#xff1a; http://blog.51cto.co…

parallels網絡初始化失敗_33 個神經網絡「煉丹」技巧

自然語言處理Andrej Karpathy 是深度學習計算機視覺領域、與領域的研究員。博士期間師從李飛飛。在讀博期間&#xff0c;兩次在谷歌實習&#xff0c;研究在 Youtube 視頻上的大規模特征學習&#xff0c;2015 年在 DeepMind 實習&#xff0c;研究深度強化學習。畢業后&#xff0…

《操作系統》OS學習(三):系統調用

例子 首先看一個標準C庫的例子&#xff1a;當我們程序中使用了C庫中的printf()函數&#xff0c;實際在底層是在內核態中調用了write()函數。圖中右側則是將程序代碼與C庫都算到應用程序中&#xff0c;內核提供了一個系統調用接口。 從這個例子我們可以得到以下幾點&#xff1a…

cygwin/gcc與MinGW

cygwin/gcc和MinGW都是gcc在windows下的編譯環境&#xff0c;但是它們有什么區別?在實際工作中如何選擇這兩種編譯器呢?cygwin/gcc完全可以和在linux下的gcc劃等號&#xff0c;這個從boost庫的劃分中就可以看出來端倪&#xff0c;cygwin下的gcc和linux下的gcc使用的是相同的T…

JavaScript服務器端開發技術(對象屬性的枚舉與查詢)

既然對象是屬性的集合&#xff0c;那么檢測與枚舉集合中的屬性就是一項重要任務。對此&#xff0c;我們來分別看一下ES3和ES5提供的解決方案。 1) ES3枚舉方案 示例代碼&#xff1a; var contacts{ ID:[0,1,2,3,4,5], names:["Zero","One","Two&q…

treelistview 所有節點失去焦點_垃圾詢盤過濾,焦點科技的 Milvus 實踐

文章作者&#xff1a;黎陽&#xff0c;焦點科技軟件開發工程師李成龍&#xff0c;Zilliz 數據工程師Milvus (https://milvus.io/) 向量搜索引擎開源半年以來&#xff0c;全球已經有數百家企業或組織用戶。焦點科技是一家以 B2B 外貿交易為主營業務的科技公司&#xff0c;也是 M…

《操作系統》OS學習(四):計算機體系結構、內存層次和地址生成

計算機除了計算能力之外還有存儲能力&#xff0c;存儲能力即計算機擁有一系列的存儲介質&#xff0c;我們可以在存儲介質上存儲我們的代碼和數據。計算機體系結構中約定了哪些地方可以用來存儲數據&#xff1a;CPU內的寄存器、內存和外存。不同的存儲介質&#xff0c;容量、速度…

GCC中SIMD指令的應用方法

X86架構上的多媒體應用開發&#xff0c;如果能夠使用SIMD指令進行優化&#xff0c; 性能將大大提高。目前&#xff0c;IA-32的SIMD指令包括MMX&#xff0c;SSE&#xff0c;SSE2等幾級。 在GCC的開發環境中&#xff0c;有幾種使用SIMD指令的方式&#xff0c;本文逐一介紹。X86的…

使用angular4和asp.net core 2 web api做個練習項目(二), 這部分都是angular

上一篇: http://www.cnblogs.com/cgzl/p/7755801.html 完成client.service.ts: import { Injectable } from angular/core; import { Http, Headers } from angular/http; import { Observable } from rxjs/Observable; import { ErrorHandler } from angular/core; import rxj…

leelen可視對講怎么接線_樓宇對講系統怎么布線 樓宇對講系統布線方式【介紹】...

隨著智能小區規模不斷增加&#xff0c;樓宇可視對講系統應用越來越廣泛&#xff0c;因而視頻信號的傳輸方式與布線設計顯得越來越重要。視頻信號與數據和音頻信號不同&#xff0c;可行的一種傳輸方式為視頻信號基帶傳輸&#xff0c;下面小編就簡要介紹一下這種傳輸方式和布線方…

路由匯總實例

5.2.2.2 路由匯總策略 之前提到過&#xff0c;在網絡管理員計劃好子網選擇并進行預期地路由匯總時&#xff0c;手動路由匯總工作能取得最佳效果。例如&#xff0c;之前的例子設定好了一個考慮周全的計劃&#xff0c;管理員只使用遠離Yosemite路由器并以10.2開頭的子網。這個規定…

《操作系統》OS學習(五):連續內存分配 內存碎片、動態分配、碎片整理、伙伴系統

內存碎片 在沒有其他方式輔助的情況下&#xff0c;我們分配給一個進程的內存是連續的。在分配時候我們需要有動態分配與碎片處理。如何理解呢&#xff1f;就是每個進程需要一塊內存&#xff0c;我們要選取合適的位置的內存分配給它。當有的進程先結束了內存還給操作系統&#…

GCC 中文手冊 - 摘自純C論壇

GCC Section: GNU Tools (1) Updated: 2003/12/05 Index Return to Main Contents NAME gcc,g-GNU工程的C和C編譯器(egcs-1.1.2) 總覽(SYNOPSIS) gcc[option|filename ]... g[option|filename ]... 警告(WARNING) 本手冊頁內容摘自GNU C編譯器的完整文檔,僅限于解釋選項的含義…

python如何實現支持中文

#codingutf-8print("我要python支持中文") 默認情況下&#xff0c;python是不支持中文的。 如果要實現python支持中文&#xff08;我是從python3.6開始學習的&#xff09;&#xff0c;只要在python文檔的開頭加入&#xff1a;“#codingutf-8"就可以了。轉載于:h…

世界之窗瀏覽器刪除文本框信息_文本框——Excel里的便利貼

工作表里面的單元格應該是足夠我們來記錄數據和信息了。但是文本框這個功能在工作表中還是存在&#xff0c;可以理解為便利貼功能。插入文本框1.點擊“插入”選項卡。2.然后點擊“文本框”。3.在下拉菜單里面&#xff0c;有兩種可供選擇&#xff1a;橫排文本框和垂直文本框。在…