Android 蓝牙服务启动

ops/2024/10/21 6:10:33/

蓝牙是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 988521if (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相关的模块了:

@frameworks/base/services/core/java/com/android/server/BluetoothManagerService.javaclass BluetoothService extends SystemService {private BluetoothManagerService mBluetoothManagerService;public BluetoothService(Context context) {super(context);//创建BluetoothManagerService的实例mBluetoothManagerService = new BluetoothManagerService(context);}........@Overridepublic 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的handlermContext = 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和addressif (isBluetoothPersistedStateOn()) {//查看上一次关机时,BT是否为enable状态;如果是,这次开机也需要enable BTif (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 msgmHandler.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 statetry {mBluetoothLock.readLock().lock();if (mBluetooth != null) { //开机第一次enable,值为nullint state = mBluetooth.getState();if (state == BluetoothAdapter.STATE_BLE_ON) {//大部分设备一般都不是ble only modeSlog.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);//此时为falseif (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句柄,bindServiceAsUser如果服务端没有启动,就会去启动服务端:

 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中,AdapterService是真正的服务端:

@packages/apps/Bluetooth/AndroidManifest.xml<application android:name=".btservice.AdapterApp"android:icon="@mipmap/bt_share"android:persistent="false"android:label="@string/app_name"android:supportsRtl="true"android:usesCleartextTraffic="false"android:directBootAware="trueandroid:defaultToDeviceProtectedStorage="true"<serviceandroid:process="@string/process"android:name = ".btservice.AdapterService"><intent-filter><action android:name="android.bluetooth.IBluetooth" /></intent-filter></service>
@packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java
public class AdapterService extends Service {private static final String TAG = "BluetoothAdapterService";public void onCreate() {super.onCreate();debugLog("onCreate()");    

去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 timeoutmHandler.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 objecttry {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 upsendBluetoothServiceUpCallback();//Do enable requesttry {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

@packages/apps/Bluetooth/AndroidManifest.xml
    <application android:name=".btservice.AdapterApp"
         android:icon="@mipmap/bt_share"
         android:persistent="false"
         android:label="@string/app_name"
         android:supportsRtl="true"
         android:usesCleartextTraffic="false"
         android:directBootAware="true
         android:defaultToDeviceProtectedStorage="true"
        <service
            android:process="@string/process"
            android:name = ".btservice.AdapterService">
            <intent-filter>
                <action android:name="android.bluetooth.IBluetooth" />
            </intent-filter>
        </service>


http://www.ppmy.cn/ops/111015.html

相关文章

Python | Leetcode Python题解之第400题第N位数字

题目&#xff1a; 题解&#xff1a; class Solution:def findNthDigit(self, n: int) -> int:d, count 1, 9while n > d * count:n - d * countd 1count * 10index n - 1start 10 ** (d - 1)num start index // ddigitIndex index % dreturn num // 10 ** (d - d…

php 实现JWT

在 PHP 中&#xff0c;JSON Web Token (JWT) 是一种开放标准 (RFC 7519) 用于在各方之间作为 JSON 对象安全地传输信息。JWT 通常用于身份验证系统&#xff0c;如 OAuth2 或基于令牌的身份验证。 以下是一个基本的 PHP 实现 JWT 生成和验证的代码示例。 JWT 的组成部分 JWT …

【论文阅读】视觉分割新SOTA: Segment Anything(SAM)

导言 随着基于对比文本—图像对的预训练&#xff08;CLIP&#xff09;方法或者模型、聊天生成预训练转换器&#xff08;ChatGPT&#xff09;、生成预训练转换器-4&#xff08;GPT-4&#xff09;等基础大模型的出现&#xff0c;通用人工智能&#xff08; AGI&#xff09;的研究…

Kafka 基于SASL/SCRAM动态认证部署,kafka加账号密码登录部署

文章目录 前言下载 kafka安装启动zookeeper添加账号密码 启动kafka修改kafka配置文件增加jaas授权文件修改启动文件&#xff0c;启动kafka检查是否部署成功 offset explore 连接 前言 其实挺简单的几个配置文件&#xff0c;问大模型一直没说到点上&#xff0c;绕晕了。SASL/SC…

LeetCode题练习与总结:基本计算器 Ⅱ--227

一、题目描述 给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-2^31, 2^31 - 1] 的范围内。 注意&#xff1a;不允许使用任何将字符串作为数学表达式计算…

DesignPattern设计模式

1 前言 1.1 内容概要 理解使用设计模式的目的掌握软件设计的SOLID原则理解单例的思想&#xff0c;并且能够设计一个单例理解工厂设计模式的思想&#xff0c;能够设计简单工厂&#xff0c;理解工厂方法了解建造者模式的思想&#xff0c;掌握其代码风格理解代理设计模式的思想&a…

代码随想录训练营 Day60打卡 图论part10 SPFA算法 Bellman-Ford 之判断负权回路 Bellman-Ford 之单源有限最短路

代码随想录训练营 Day60打卡 图论part10 一、Bellman_ford 队列优化算法&#xff08;又名SPFA&#xff09; 例题&#xff1a;卡码94. 城市间货物运输 I 题目描述 某国为促进城市间经济交流&#xff0c;决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市&#xff0c;通过…

[数据集][目标检测]葡萄成熟度检测数据集VOC+YOLO格式1123张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1123 标注数量(xml文件个数)&#xff1a;1123 标注数量(txt文件个数)&#xff1a;1123 标注…