Android -- Bluetooth framework啓動過程簡析
藍牙是Android設備中非常常見的一個feature,設備廠家可以用BT來做RC、連接音箱、設備本身做Sink等常見功能。如果一些設備不需要BT功能,Android也可以通過配置來disable此模塊,方便廠家爲自己的設備做客製化。APP操作設備的藍牙功能,一般是通過標準API-BluetoothAdapter實現,這裏我們先不關心具體API的實現flow,先來了解Bluetooth framework的啓動過程,這樣可以爲後面介紹一些BT 常見flow,做鋪墊。
我們都知道Android的主要系統服務都是在system_server中啓動的,BT也不例外:
// Skip Bluetooth if we have an emulator kernel
// TODO: Use a more reliable check to see if this product should
// support Bluetooth - see bug 988521
if (isEmulator) {
Slog.i(TAG, "No Bluetooth Service (emulator)");
} else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
Slog.i(TAG, "No Bluetooth Service (factory test)");
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
} else {
traceBeginAndSlog("StartBluetoothService");
mSystemServiceManager.startService(BluetoothService.class);
traceEnd();
}
在SystemServer中,BT framework啓動的起點以服務啓動的形式表現,這在Android中非常常見;我們可以通過配置feature xml文件來表明系統當前是否支持藍牙,在一些沒有藍牙模組的設備上,就可以去掉hardware feature bluetooth的聲明,這樣設備啓動時就不會啓動BT framework相關的模塊了:
class BluetoothService extends SystemService {
private BluetoothManagerService mBluetoothManagerService;
public BluetoothService(Context context) {
super(context);
//創建BluetoothManagerService的實例
mBluetoothManagerService = new BluetoothManagerService(context);
}
........
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
//將BluetoothManagerService實例發佈到系統中,這樣就可以Context根據BT的service名去獲取它的Binder代理操作API了
publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE,
mBluetoothManagerService);
} else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
//此時系統應該啓動到一個比較晚的階段了,可以使用AMS去Bind需要的Service了
mBluetoothManagerService.handleOnBootPhase();
}
}
........
}
BluetoothManagerService構造過程中,跟開機flow中自動enable bt相關的主要是mEnableExternal這個flag:
BluetoothManagerService(Context context) {
mHandler = new BluetoothHandler(IoThread.get().getLooper());//創建內部處理msg的handler
mContext = context;
mPermissionReviewRequired = context.getResources()
.getBoolean(com.android.internal.R.bool.config_permissionReviewRequired);
mCrashes = 0;
mBluetooth = null;
mBluetoothBinder = null;
mBluetoothGatt = null;
mBinding = false;
mUnbinding = false;
mEnable = false;
mState = BluetoothAdapter.STATE_OFF;
mQuietEnableExternal = false;//false表示此次enable需要觸發auto connect device和保存狀態,BluetoothAdapter::enableNoAutoConnect()可以改變此狀態
mEnableExternal = false;
mAddress = null;
mName = null;
mErrorRecoveryRetryCounter = 0;
mContentResolver = context.getContentResolver();
// Observe BLE scan only mode settings change.
registerForBleScanModeChange();
mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);//監聽App通過接口修改BT local name的廣播(對端設備搜索你顯示的字符串)
filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);//監聽bt地址改變的廣播
filter.addAction(Intent.ACTION_SETTING_RESTORED);//監聽當前設置需要restore回上一次設置的廣播,此時需要重新保存name和addr爲上一次的信息
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mReceiver, filter);
loadStoredNameAndAddress();//從數據庫中加載本機Bt的local name和address
if (isBluetoothPersistedStateOn()) {//查看上一次關機時,BT是否爲enable狀態;如果是,這次開機也需要enable BT
if (DBG) {
Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
}
mEnableExternal = true;//表明開機過程中需要enable BT
}
String airplaneModeRadios =
Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
if (airplaneModeRadios == null || airplaneModeRadios.contains(
Settings.Global.RADIO_BLUETOOTH)) {
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);//監聽Airplane mode改變,相應改變BT狀態;這在TV plateform上基本沒用
}
int systemUiUid = -1;
try {
// Check if device is configured with no home screen, which implies no SystemUI.
boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
if (!noHome) {
systemUiUid = mContext.getPackageManager()
.getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
UserHandle.USER_SYSTEM);
}
Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
}
mSystemUiUid = systemUiUid;
}
它爲true,表明上一次關機前BT就是enable的,這次開機也需要enable;其他的主要是一些廣播、數據庫字段的初始化動作。
回到前面BluetoothService啓動的過程,當系統啓動到合適的階段,就回調SystemService相應的函數,當phase爲PHASE_ACTIVITY_MANAGER_READY時,調用到BMS::handleOnBootPhase(),進行BT framework的啓動:
/**
* Send enable message and set adapter name and address. Called when the boot phase becomes
* PHASE_SYSTEM_SERVICES_READY.
*/
public void handleOnBootPhase() {
if (DBG) {
Slog.d(TAG, "Bluetooth boot completed");
}
UserManagerInternal userManagerInternal =
LocalServices.getService(UserManagerInternal.class);
userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
final boolean isBluetoothDisallowed = isBluetoothDisallowed();
if (isBluetoothDisallowed) {
return;
}
if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
if (DBG) {
Slog.d(TAG, "Auto-enabling Bluetooth.");
}
sendEnableMsg(mQuietEnableExternal/*默認false,表示此次enable需要自動連接device/保存enable狀態*/,
BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT,
mContext.getPackageName());
} else if (!isNameAndAddressSet()) {
if (DBG) {
Slog.d(TAG, "Getting adapter name and address");
}
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
}
}
handleOnBootPhase()的內容比較單一,根據一些flag判斷是否需要enable BT;而enable藍牙這裏是通過觸發send msg實現:
private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
//發送MESSAGE_ENABLE msg
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));
addActiveLog(reason, packageName, true);
mLastEnabledTime = SystemClock.elapsedRealtime();
}
case MESSAGE_ENABLE:
if (DBG) {
Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth);
}
mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
mEnable = true;
// Use service interface to get the exact state
try {
mBluetoothLock.readLock().lock();
if (mBluetooth != null) { //開機第一次enable,值爲null
int state = mBluetooth.getState();
if (state == BluetoothAdapter.STATE_BLE_ON) {//大部分設備一般都不是ble only mode
Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
mBluetooth.onLeServiceUp();
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
break;
}
}
} catch (RemoteException e) {
Slog.e(TAG, "", e);
} finally {
mBluetoothLock.readLock().unlock();
}
mQuietEnable = (msg.arg1 == 1);//此時爲false
if (mBluetooth == null) {//mBluetooth是後面綁定Bt apk中AdapterService時拿到的Binder代理對象;用以把操作bypass到BT核心框架中
handleEnable(mQuietEnable);
} else {//如果mBluetooth不是null,說明之前已經啓動過了;此時是Restart flow,以MESSAGE_RESTART_BLUETOOTH_SERVICE觸發
//
// We need to wait until transitioned to STATE_OFF and
// the previous Bluetooth process has exited. The
// waiting period has three components:
// (a) Wait until the local state is STATE_OFF. This
// is accomplished by "waitForOnOff(false, true)".
// (b) Wait until the STATE_OFF state is updated to
// all components.
// (c) Wait until the Bluetooth process exits, and
// ActivityManager detects it.
// The waiting for (b) and (c) is accomplished by
// delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE
// message. On slower devices, that delay needs to be
// on the order of (2 * SERVICE_RESTART_TIME_MS).
//
waitForOnOff(false, true);
Message restartMsg =
mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);
}
break;
handleEnable()去Bind AdapterService拿到它的Binder句柄:
private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
......
private class BluetoothServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName componentName, IBinder service) {
String name = componentName.getClassName();
if (DBG) {
Slog.d(TAG, "BluetoothServiceConnection: " + name);
}
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
msg.arg1 = SERVICE_IBLUETOOTH;
} else if (name.equals("com.android.bluetooth.gatt.GattService")) {
msg.arg1 = SERVICE_IBLUETOOTHGATT;
} else {
Slog.e(TAG, "Unknown service connected: " + name);
return;
}
msg.obj = service;
mHandler.sendMessage(msg);
}
public void onServiceDisconnected(ComponentName componentName) {
// Called if we unexpectedly disconnect.
String name = componentName.getClassName();
if (DBG) {
Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
}
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
msg.arg1 = SERVICE_IBLUETOOTH;
} else if (name.equals("com.android.bluetooth.gatt.GattService")) {
msg.arg1 = SERVICE_IBLUETOOTHGATT;
} else {
Slog.e(TAG, "Unknown service disconnected: " + name);
return;
}
mHandler.sendMessage(msg);
}
}
......
boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
Slog.e(TAG, "Fail to bind to: " + intent);
return false;
}
return true;
}
IBluetooth.class對於的Service聲明在Bluetooth apk的manifest xml中:
<service
android:process="@string/process"
android:name = ".btservice.AdapterService">
<intent-filter>
<action android:name="android.bluetooth.IBluetooth" />
</intent-filter>
</service>
去Bind AdapterService服務,並拿到它的Binder句柄,用以後面操作它的接口。AdapterService的初始化下篇再講,現在先回到BMS binder成功之後發送MESSAGE_BLUETOOTH_SERVICE_CONNECTED(arg1:SERVICE_IBLUETOOTH)的處理:
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
if (DBG) {
Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
}
IBinder service = (IBinder) msg.obj; //保存Service的onBinder()句柄
try {
mBluetoothLock.writeLock().lock();
if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
mBluetoothGatt =
IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));
continueFromBleOnState();
break;
} // else must be SERVICE_IBLUETOOTH
//Remove timeout
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
mBinding = false;
mBluetoothBinder = service;
mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));//再轉成IBluetooth對象
if (!isNameAndAddressSet()) {
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
if (mGetNameAddressOnly) {
return;
}
}
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);//mBluetoothCallback用來監聽BT的enable狀態,如state_on/state_off的狀態變化
} catch (RemoteException re) {
Slog.e(TAG, "Unable to register BluetoothCallback", re);
}
//Inform BluetoothAdapter instances that service is up
sendBluetoothServiceUpCallback();
//Do enable request
try {
if (!mQuietEnable) {
if (!mBluetooth.enable()) {//Service bind成功之後,call AdpaterService的enable()接口;做一些初始化動作,並向stack發送enable命令啓動藍牙
Slog.e(TAG, "IBluetooth.enable() returned false");
}
} else {
if (!mBluetooth.enableNoAutoConnect()) {
Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");
}
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to call enable()", e);
}
} finally {
mBluetoothLock.writeLock().unlock();
}
if (!mEnable) {
waitForOnOff(true, false);
handleDisable();
waitForOnOff(false, false);
}
break;
}
主要操作:
- 拿到bind服務的onBinder()句柄,並轉成IBluetooth類型
- 通過IBluetooth類型的obj,調用enable()接口,將flow轉到AdapterService中,做一些初始化、並向stack下enable bt的cmd
也要注意到,如果BMS中和AdapterService的連接斷掉了,最終會通過MESSAGE_RESTART_BLUETOOTH_SERVICE msg去重新bind它;這在場景可能是由於模組異常導致Bluetooth apk crash(會產生tombstone),與AdapterService的bind連接斷掉;這裏觸發Restart,會導致重走一遍介紹的enable BT流程。
到此,enable BT的flow就從BluetoothManagerService轉到AdapterService中了;實際上,通過BluetoothAdapter下來的大部分API調用最終都是call到AdapterService,再通過它下cmd給stack。
AdapterService是藍牙框架中一個非常重要的服務,它的啓動和初始化部分,我們下篇再單獨介紹。
PS:
可以看到BT啓動過程中有兩個常見到的flag:
- mEnable:它主要是用來標記系統運行時,藍牙狀態的變化,它有些時候時跟mEnableExternal值一致的。但如果藍牙的狀態是因爲某些原因,如stack崩潰,導致藍牙需要reset、重新啓動時,需要靠這個flag來標記這種case的enable/disable狀態
- mEnableExternal:它主要是記錄通過用戶手動操作導致的BT使能狀態,如通過藍牙功能按鈕來enable/disable BT