在前面的UI分析的文章中我们已经发现,其实不管是设置中的开关和fragment之后的开关最终都是关联到BluetoothEnabler中去的,所以,我们直接去看这个里面对于开关的处理,开关的处理当然就是onCheckedChanged这个函数了,哈哈~~直接分析。。
1、蓝牙打开的按键处理
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Show toast message if Bluetooth is not allowed in airplane mode
//其实这个就是飞行模式打开的时候,就显示一个“正处于飞行模式下”
//不过这种情况很少遇到的,因为在飞行模式的时候,其实按钮时反灰的,基本走不到这里
//不过,在那个下拉菜单中点击打开蓝牙的时候还是会遇到的,大家可以去试试看
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
buttonView.setChecked(false);
}
//只要蓝牙是正常的,这里必然就不是null喽。。
if (mLocalAdapter != null) {
mLocalAdapter.setBluetoothEnabled(isChecked);
}
//把switch反灰,在打开的过程中,开关一直是灰的
mSwitch.setEnabled(false);
}
1.1 setBluetoothEnabled分析
在按钮打开的时候,就是通过这个函数来和framework层的adapter进行交互的。
public void setBluetoothEnabled(boolean enabled) {
//根据传入的enabled值,决定是打开还是关闭
//我们这边必然就是调用enable了
//具体分析见1.1.1
boolean success = enabled
? mAdapter.enable()
: mAdapter.disable();
//这里就是设置状态为正在打开
//注意的是这里success是enable或者disable的返回值,不是enabled的值哦,呵呵~~
if (success) {
//所以,会在enable或者disable返回后面设置状态。
//当然这个返回并不是说蓝牙打开成功了,因为蓝牙打开的操作是异步的
//详细见1.1.2
setBluetoothStateInt(enabled
? BluetoothAdapter.STATE_TURNING_ON
: BluetoothAdapter.STATE_TURNING_OFF);
} else {
if (Utils.V) {
Log.v(TAG, "setBluetoothEnabled call, manager didn't return " +
"success for enabled: " + enabled);
}
//若是失败了,还是要同步一下状态的。
//就是把adapter的状态和当前状态进行一下同步
syncBluetoothState();
}
}
1.1.1 bluetoothAdapter的enable函数分析
其实我们会发现,这个函数最终还是调用的bluetoothService中的enable函数,所以,这里就不详细说了,直接去看bluetoothService中的enable
/** Bring up BT and persist BT on in settings */
public boolean enable() {
//这个没什么好说的,直接看下面
return enable(true);
}
/**
* Enable this Bluetooth device, asynchronously.
* This turns on/off the underlying hardware.
*
* @param saveSetting If true, persist the new state of BT in settings
* @return True on success (so far)
*/
//注意这里是异步的打开蓝牙
public synchronized boolean enable(boolean saveSetting) {
//检查权限,这就是我们为什么要在AndroidManifest中加入各种权限了
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
// Airplane mode can prevent Bluetooth radio from being turned on.
//看是否是飞行模式打开了
if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
return false;
}
//就是wifi和bt能否共存,若不能,则需要检查wifi是否打开,打开了就不能打开bt了,一般情况下,我们不会进入这样的case,后面类似的内容不再解释
if (mBootCompleted && !supportBtWifiCoexit) {
if (mWifiManager == null)
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
if (mWifiManager != null
&& (WifiManager.WIFI_STATE_DISABLED != mWifiManager.getWifiState()
|| WifiManager.WIFI_AP_STATE_DISABLED != mWifiManager.getWifiApState())) {
return false;
}
}
//发送USER_TURN_ON的msg,哈哈,这个我们在蓝牙状态机改变的那篇文章中已经相信解释过了,所以这里就和那边关联起来了。
mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);
return true;
}
1.1.2 setBluetoothStateInt
这个函数是上层对蓝牙状态改变的操作,就是设置成不同的状态:
synchronized void setBluetoothStateInt(int state) {
//其实针对turning on和turning of并没做什么特殊的东西
//就是设置mState的状态
mState = state;
if (mProfileManager != null) {
if (state == BluetoothAdapter.STATE_ON) {
//这两个地方我们在打开和关闭完成之后再来分析
// if mProfileManager hasn't been constructed yet, it will
// get the adapter UUIDs in its constructor when it is.
mProfileManager.setBluetoothStateOn();
} else if (state == BluetoothAdapter.STATE_OFF) {
//closeProfileProxy to unbindService
mProfileManager.setBluetoothStateOff();
}
}
}
2.深入理解蓝牙状态变换所做的工作
我们在蓝牙状态变换的那篇文章中仔细的分析了蓝牙打开所涉及的一些操作,然而当时的重点还是分析了状态之间的变换,并没有分析真正去做了些什么,这篇文章下面的内容将会深入去分析一下这些内容。
现在我们先假设quick swtich是关闭的,也就是说在我们打开之前蓝牙的状态是位于poweroff的,我们就从这边开始来分析好了:
private class PowerOff extends State {
@Override
public void enter() {
if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);
}
@Override
public boolean processMessage(Message message) {
log("PowerOff process message: " + message.what);
boolean retValue = HANDLED;
switch(message.what) {
case USER_TURN_ON:
// starts turning on BT module, broadcast this out
//这里就是比较关键了,先发送一个状态改变的广播
//哪里会进行这个广播的处理?详见2.1
broadcastState(BluetoothAdapter.STATE_TURNING_ON);
//到warmup状态
transitionTo(mWarmUp);
//蓝牙的一系列初始化操作,详细见2.2
if (prepareBluetooth()) {
// this is user request, save the setting
if ((Boolean) message.obj) {
//保存打开与否的状态
persistSwitchSetting(true);
}
// We will continue turn the BT on all the way to the BluetoothOn state
//状态的转换就不再详细分析了,具体见我的蓝牙状态机分析那篇文章
deferMessage(obtainMessage(TURN_ON_CONTINUE));
} else {
Log.e(TAG, "failed to prepare bluetooth, abort turning on");
transitionTo(mPowerOff);
broadcastState(BluetoothAdapter.STATE_OFF);
}
break;
2.1 bluetoothAdapter状态转换广播的处理分析
我们先来看一下究竟是发出的那个broadcast,然后再来看有哪些地方注册了这个broadcast的receiver。
private void broadcastState(int newState) {
log("Bluetooth state " + mPublicState + " -> " + newState);
// mPublicState这是用来保存目前状态的变量,会先进行比较,看是否真的改变了
//若是没有改变也就不需要做什么了
if (mPublicState == newState) {
return;
}
//否则就需要发送BluetoothAdapter.ACTION_STATE_CHANGED的broadcast
Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
//两个参数
//一个就是原有的状态,一个是新的状态
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mPublicState);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mPublicState = newState;
mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
}
2.1.1注册的BluetoothAdapter.ACTION_STATE_CHANGED的receiver
1)BluetoothEnabler中的receiver:
这个地方主要是对按钮的处理,我们知道BluetoothEnabler主要是对bluetooth的打开和关闭进行处理的,所以很容易就联想到,在打开的时候按钮反灰之类的操作应该是这里来实现的,我们来看具体的代码:
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i("BluetoothEnabler", "action = " + action);
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
//得到目前的状态
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
//对状态的改变进行处理,见下面。
handleStateChanged(state);
A,handleStateChanged的分析
这个函数主要用来处理状体改变后的开关的变化,主要就是打开过程中的反灰,打开之后的设置为打开(就是那个高亮),关闭之后的按钮到左边的那些操作。
void handleStateChanged(int state) {
switch (state) {
case BluetoothAdapter.STATE_TURNING_ON:
//正在打开的过程中,按钮不可选中
mSwitch.setEnabled(false);
break;
case BluetoothAdapter.STATE_ON:
//打开了,就是可以选中,checked设为true
mSwitch.setChecked(true);
mSwitch.setEnabled(true);
break;
case BluetoothAdapter.STATE_TURNING_OFF:
//关闭过程和打开过程是一样的
mSwitch.setEnabled(false);
break;
//关闭的话,就把按钮的checked设为false
case BluetoothAdapter.STATE_OFF:
mSwitch.setChecked(false);
//同时根据是否是飞行模式,来设置按钮能否选中
//这也就是我们打开飞行模式后,若是蓝牙是开着的,那么打开按钮是不能再被选中的
mAirplaneMode.set(isAirplaneModeOn());
if (!mAirplaneMode.get()) {
mSwitch.setEnabled(true);
}
break;
default:
//默认就是关闭可选中
mSwitch.setChecked(false);
mSwitch.setEnabled(true);
}
//这个东西不再分析了
if (mSupportBtWifiCoexist == false && isWifiAndWifiApStateDisabled() == false) {
mSwitch.setChecked(false);
mSwitch.setEnabled(false);
}
}
2)BluetoothEventManager中的handler
我们前面分析过BluetoothEventManager是用来从BluetoothAPI中接收broadcast,并把他们分析到对应的UI线程中去。
这句代码表明了对broadcast的处理:
// Bluetooth on/off broadcasts
addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
它的实现如下:
private class AdapterStateChangedHandler implements Handler {
public void onReceive(Context context, Intent intent,
BluetoothDevice device) {
//得到新的状态
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
// update local profiles and get paired devices
//根据状态来刷新本地的profile,但是,他主要是对on和off的state进行操作的,所以,我们暂时不分析,因为会涉及到turning on中的一些处理。
mLocalAdapter.setBluetoothStateInt(state);
// send callback to update UI and possibly start scanning
synchronized (mCallbacks) {
//调用注册的callbacks的onBluetoothStateChanged函数
//这个callback是通过registerCallback来进行注册的,我们在下面的A中进行分析
for (BluetoothCallback callback : mCallbacks) {
callback.onBluetoothStateChanged(state);
}
}
}
}
A、BluetoothEventManager中注册的callback的分析
我们从代码中搜索一下会发现,真正注册到这里的callback只有一个,就是DeviceListPreferenceFragment,它的代码如下:
public void onResume() {
……
mLocalManager.getEventManager().registerCallback(this);
}
在前面,我们看到的就是BluetoothSettings是扩展这个类的,然而不幸的时候,BluetoothSettings中有自己的onResume函数,并不会走到这里,所以,我们再来看看这里的callback还有别的地方会用到么?
我们发现除了BletoothSettings还有一个地方,就是DevicePickerFragment也是扩展这个类的,然而同样的,它也重写了onResume函数,哈哈,原来的这个callback没有人注册啊,所以也就不会调用了。呵呵~~
至此BluetoothEventManger中的handler就分析完成了,不幸的是好像什么工作都没有做。好吧,我们过去了。
3)其它。
其实还有一些别的地方也注册了这个broadcast,但是至少目前在打开的时候他们还没有注册,所以,这里就暂时不分析了,我们到了具体的情景时在去具体分析好了。比如以下地方:
1)BluetoothA2dpService,BluetoothController.java,PhoneStatusBarPolicy.java等,这些地方基本都是一些状态位的重置。
2)BluetoothOppBtEnablingActivity.java,BluetoothOppReceiver.java,BluetoothPbapReceiver.java,BluetoothHeadsetService.java这些地方都是对一些profile的处理,我们在具体分析到这里的时候再来具体看。
3)BluetoothNameDialogFragment,这个只有在打开了修改名字才会使用到。
4)RequestPermissionActivity,这个是别的应用要求搜索或者打开蓝牙的时候才会使用。
5)SettingsAppWidgetProvider,窗口小部件中进行一些功耗相关的控制的时候会用到。
以上几个,我们暂时就不会进行分析了,避免分叉得太厉害。
所以,从这里可以看到,基本在正在打开这个状态,就只有对按钮的一个反灰的操作了,其它的就没有什么别的特殊的了。
2.2 prepareBluetooth的分析
从注释来看,这个函数就是打开蓝牙模块了,包括加载fw等一系列操作,各家的方案的区别可能就是体现在这里了,所以,需要根据不同的厂商来进行不同的设置了。理所当然的是,这里就有了各自的verdon operation了。需要注意的是他是不能discoverable和connectable,这也是因为在quick swtich打开的情况下,让外界并不能发现我们是打开的。
private boolean prepareBluetooth() {
//这是一个jni层的操作,涉及的内容及其广泛,我们会在3中进行详细介绍
if (mBluetoothService.enableNative() != 0) {
return false;
}
// try to start event loop, give 2 attempts
//启动event loop,event loop是framework层用来接收来自jni层各种event的代码,他会根据不同的event来所不同的操作
int retryCount = 2;
boolean eventLoopStarted = false;
//这里会尝试进行开始两次,每次会有一个eventloop run的检查,会检查5次,每次间隔100ms,所以这里最长大概会有1s左右的时间,事实上,我们并不会有这么长的时间,呵呵~~
while ((retryCount-- > 0) && !eventLoopStarted) {
//详细见2.2.1
mEventLoop.start();
// it may take a moment for the other 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;
}
}
}
//没有能够启动eventloop会直接把蓝牙disable掉的
if (!eventLoopStarted) {
mBluetoothService.disableNative();
return false;
}
// get BluetoothService ready
//紧接着的BluetoothService中的一些处理,详细见2.2.2
if (!mBluetoothService.prepareBluetooth()) {
mEventLoop.stop();
mBluetoothService.disableNative();
return false;
}
//这里会在10s之后发送一个timeout的msg,以用来恢复状态机,详细见我状态机变化的那篇文章的分析。
sendMessageDelayed(PREPARE_BLUETOOTH_TIMEOUT, PREPARE_BLUETOOTH_TIMEOUT_TIME);
return true;
}
}
2.2.1 Eventloop的启动
BluetoothEventLoop在状态机初始化的时候就已经建构了,这里就不再分析了。我们直接从start函数来看,看eventloop究竟做了什么:
/* package */ void start() {
//看是否已经在run了
if (!isEventLoopRunningNative()) {
if (DBG) log("Starting Event Loop thread");
//开始eventloop
startEventLoopNative();
}
}
很快大家就发现,这原来还是要到jni层啊,看来我们还是要好好看进去哦,不知道这个和enableNative会不会有关系,毕竟他是在前面啊,所以,这里我们还是不着急分析,把它放到enableNative分析完成之后再去看。详细分析见4。
2.2.2 BluetoothService中的PrepareBluetooth函数
/* package */ synchronized boolean prepareBluetooth() {
//建立native的data,就是一些数据的关联,没有什么实在的东西
if (!setupNativeDataNative()) {
return false;
}
//那powered的property设为false,就是把discoverable和connectable设为false了,为什么这么做,上面已经讲过了
switchConnectable(false);
//更新sdp列表,这个我们需要重点看一下,详细分析见下面的A
updateSdpRecords();
return true;
}
A、updateSdpRecords分析
private synchronized void updateSdpRecords() {
ArrayList uuids = new ArrayList();
// Add the default records
//支持HSP的AG端
uuids.add(BluetoothUuid.HSP_AG);
//支持opp
uuids.add(BluetoothUuid.ObexObjectPush);
//看是否支持voice,这里是支持的话,就加入HFP的AG和pbap
if (mContext.getResources().
getBoolean(com.android.internal.R.bool.config_voice_capable)) {
uuids.add(BluetoothUuid.Handsfree_AG);
uuids.add(BluetoothUuid.PBAP_PSE);
}
// Add SDP records for profiles maintained by Android userspace
//加入支持的uuid,这里详细分析见下面的2.2.2.1
addReservedSdpRecords(uuids);
// Enable profiles maintained by Bluez userspace.
//使能包含的profile,这个又通过jni层去实现了,所以,我们同样放到后面2.2.2.2中分析
setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
BluetoothPanProfileHandler.NAP_BRIDGE);
// Add SDP records for profiles maintained by Bluez userspace
//上面那些uuid是通过bluez去控制的,所以我们会有一些交互
//下面这几个是上层直接控制就可以了
//audiosource,avrcpTarget和NAP的角色
uuids.add(BluetoothUuid.AudioSource);
uuids.add(BluetoothUuid.AvrcpTarget);
uuids.add(BluetoothUuid.NAP);
// Cannot cast uuids.toArray directly since ParcelUuid is parcelable
//因为uuids是parcel格式的,所以,我们一个一个读出来好了,加入到mAdapterUuids中
mAdapterUuids = new ParcelUuid[uuids.size()];
for (int i = 0; i < uuids.size(); i++) {
mAdapterUuids[i] = uuids.get(i);
}
}
2.2.2.1 uuid的记录
SDP相关的注册
private synchronized void addReservedSdpRecords(final ArrayList uuids) {
//Register SDP records.
int[] svcIdentifiers = new int[uuids.size()];
for (int i = 0; i < uuids.size(); i++) {
//从128bit的uuid得到16bit/32bit的内容
svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i));
}
//这个就到了jni层的分析了,我们在后面会详细分析
mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers);
}
至此,jni层之上的所有操作都已经全部分析完成了,后面我们在进行分析jni层之下的内容,那又将是一个非常庞大的体系。
若您觉得该文章对您有帮助,请在下面用鼠标轻轻按一下“顶”,哈哈~~·