1. 背景
在我平时 对 车机 蓝牙的维护中, 经常遇到一个问题就是:
- 是谁打开了蓝牙?
- 是谁将蓝牙进程拉起来的?
这里有人会问, 打开蓝牙时, 不就把 蓝牙进程拉起来了呀,只要 搞清楚是谁打开的蓝牙,不就知道 进程是被谁拉起来的。为啥还要专门需要搞清楚,他俩的区别呢?
不知道小伙伴有没有遇到一种情况是, 蓝牙进程被拉起来了。 但是蓝牙并没有被打开。如果有遇到 这种情况,但是不知道为何的。 那你就不要着急划走,仔细听笨叔给你娓娓道来。 看完绝对有收获。
2. 蓝牙被打开的几种情况:
1. 系统侧
我们的系统中, 会在如下几种情况下自动打开蓝牙:
- BluetoothManagerService 中回去打开蓝牙:
- 上次下电前,蓝牙处于打开状态。 再次上电后 BluetoothManagerService 会帮我们再次打开蓝牙。否则不会自动开蓝牙
- 上电时,如果 没有获取到 车机蓝牙的名字 和 蓝牙mac地址, 这里也会去打开蓝牙, 但是 获取到之后,蓝牙进程就结束退出了
- 休眠唤醒后, car.server 中根据电源状态,会去根据,休眠前是否开蓝牙。来决定是否打开蓝牙。
1. BluetoothManagerService 中自动打开蓝牙
// service/java/com/android/server/bluetooth/BluetoothManagerService.java
public void handleOnBootPhase() {
if (DBG) {
Log.d(TAG, "Bluetooth boot completed");
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
if (!isBluetoothAvailable()) {
if (DBG) {
Log.e(TAG, "enable(): not enabling - bluetooth not available");
}
return;
}
final boolean isBluetoothDisallowed = isBluetoothDisallowed();
if (isBluetoothDisallowed) {
return;
}
final boolean isSafeMode = mContext.getPackageManager().isSafeMode();
if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()/*如果上次os 关机之前蓝牙是打开的*/ && !isSafeMode) {
if (DBG) {
Log.d(TAG, "Auto-enabling Bluetooth.");
}
// 这个地方就会自动 打开蓝牙
sendEnableMsg(mQuietEnableExternal,
BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT,
mContext.getPackageName());
} else if (!isNameAndAddressSet()) {
if (DBG) {
Log.d(TAG, "Getting adapter name and address");
}
// 如果 没有获取到 车机蓝牙的名字 和 蓝牙mac地址, 这里也会去打开蓝牙, 但是 获取到之后,蓝牙进程就结束退出了
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
}
mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext);
if (mBluetoothAirplaneModeListener != null) {
mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
}
registerForProvisioningStateChange();
mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG, mContext);
}
这里记录一下 上次下电前,处于打开蓝牙的状态。 并且本次开机也获取到蓝牙的名字和mac地址的日志:
01-01 21:11:57.149870 1742 1742 I BluetoothManagerService: BluetoothManagerService(): mAdapterIndex = 0, mDualAdapterMode = disabled
# 这里已经加载成功加载了 蓝牙名字和 mac 地址
01-01 21:11:57.150666 1742 1742 D BluetoothManagerService: Loading stored name and address
01-01 21:11:57.150756 1742 1742 D BluetoothManagerService: Stored bluetooth Name=Mycar95812,Address=EC:A7:AD:23:7C:57
# 上一次 掉电前 蓝牙是 打开状态。所以本次,上电依然保持原状。打开蓝牙
01-01 21:11:57.150803 1742 1742 D BluetoothManagerService: Bluetooth persisted state: 1
01-01 21:11:57.150813 1742 1742 D BluetoothManagerService: Startup: Bluetooth persisted state is ON.
01-01 21:11:57.151045 1742 1742 D BluetoothManagerService: Detected SystemUiUid: 10034
01-01 21:11:57.609194 1742 2028 D BluetoothManagerService: Trying to bind to profile: 2, while Bluetooth was disabled
01-01 21:11:57.770450 1742 1742 D BluetoothManagerService: Bluetooth boot completed
01-01 21:11:57.770639 1742 1742 D BluetoothManagerService: Auto-enabling Bluetooth.
01-01 21:11:57.770788 1742 1978 D BluetoothManagerService: MESSAGE_ENABLE(0): mBluetooth = null
01-01 21:11:57.770850 1742 1978 D BluetoothManagerService: Persisting Bluetooth Setting: 1
01-01 21:11:57.771023 1742 1742 D BluetoothManagerService: Trying to bind to profile: 2, while Bluetooth was disabled
# bind 蓝牙服务
01-01 21:11:57.771165 1742 1978 D BluetoothManagerService: Start binding to the Bluetooth service with intent=Intent { act=android.bluetooth.IBluetooth cmp=com.android.bluetooth/.btservice.AdapterService }
# 此时system_sever 就会去 拉起一个 蓝牙进程, 2202 就是新进程的进程号
01-01 21:11:57.964464 1742 1910 I am_proc_start: [0,2202,1002,com.android.bluetooth,service,{com.android.bluetooth/com.android.bluetooth.btservice.AdapterService}]
# -----蓝牙服务进程-------------
01-01 21:11:58.073940 2202 2202 D BluetoothAdapterApp: Loading JNI Library
01-01 21:11:58.161035 2202 2202 D BluetoothAdapterApp: REFCOUNT: Constructed com.android.bluetooth.btservice.AdapterApp@9c3a925 Instance Count = 1
01-01 21:11:58.173296 2202 2202 D BluetoothAdapterApp: onCreate
01-01 21:11:58.231689 2202 2202 D BluetoothAdapterService: onCreate()
01-01 21:11:58.372011 2202 2202 D BluetoothAdapterService: onBind()
01-01 21:11:58.420819 2202 2254 D BluetoothAdapterService: enable() - Enable called with quiet mode status = false
2. car.server 中自动打开蓝牙
在 车机中, 冷启动打开蓝牙的逻辑只在 BluetoothManagerService 中处理。
那休眠唤醒后, 是否打开蓝牙。这个逻辑只在 car.server 中处理。 那 car.server 如何区分是冷启动还是 休眠唤醒呢?
- 关键在 mUserManager.isUserUnlocked 的判断中。
- 如果 是冷启动 mUserManager.isUserUnlocked 返回 false
- 如果是 休眠再唤醒后, mUserManager.isUserUnlocked 返回 true
// service/src/com/android/car/bluetooth/BluetoothPowerPolicy.java
private final ICarPowerPolicyListener mPowerPolicyListener =
new ICarPowerPolicyListener.Stub() {
@Override
public void onPolicyChanged(CarPowerPolicy appliedPolicy,
CarPowerPolicy accumulatedPolicy) {
Slogf.d(TAG, "onPolicyChanged: " + appliedPolicy + " " + accumulatedPolicy);
boolean isOn = accumulatedPolicy.isComponentEnabled(PowerComponent.BLUETOOTH);
if (!mUserManager.isUserUnlocked(UserHandle.of(mUserId))) {
if (DBG) {
Slogf.d(TAG, "User %d is locked, ignoring bluetooth power change %s",
mUserId, (isOn ? "on" : "off"));
}
return;
}
if (isOn) {
if (isBluetoothPersistedOn()) { // 如果休眠时处于打开蓝牙的状态, 那么此时唤醒后,回去打开蓝牙
enableBluetooth();
}
} else {
// we'll turn off Bluetooth to disconnect devices and better the "off"
// illusion
if (DBG) {
Slogf.d(TAG, "Car power policy turns off bluetooth."
+ " Disable bluetooth adapter");
}
disableBluetooth();
}
}
};
private void enableBluetooth() {
mBluetoothAdapter.enable();
}
private void disableBluetooth() {
mBluetoothAdapter.disable();
}
- 这里可以看到, car.server 打开关闭蓝牙,操作和应用侧是一样的。
1. mUserManager.isUserUnlocked
单独领出来讲解一下这块内容:
- 这段代码的作用是:在用户未解锁的情况下,忽略蓝牙电源状态的更改请求。
if (!mUserManager.isUserUnlocked(UserHandle.of(mUserId))) {
if (DBG) {
Slogf.d(TAG, "User %d is locked, ignoring bluetooth power change %s",
mUserId, (isOn ? "on" : "off"));
}
return;
}
mUserManager.isUserUnlocked(...)
:判断指定用户是否已解锁(unlocked)。- Android 系统中,每个用户的加密存储在设备刚启动时默认是锁定的,只有在用户输入密码(解锁设备)后,系统才会解锁该用户的数据。
UserHandle.of(mUserId)
:将用户 ID 包装成UserHandle
对象。!
:取反,意思是“如果用户未解锁”。
这段代码逻辑可以总结为:
如果当前用户尚未解锁(即设备还在锁屏状态下),则忽略此次蓝牙开关请求,不做任何处理。
这是 Android 为了保护加密用户数据而设立的一种安全机制。
1. 背景解释
Android 从 Android 7.0(Nougat)开始引入了“文件加密”的机制,其中包括:
- Direct Boot(直接启动)模式:设备启动后,在用户解锁前,系统处于一个限制状态,只有少数服务能运行。
- 所有用户数据默认处于加密状态,直到用户解锁。
因此,在某些系统服务中,比如 Bluetooth、Wi-Fi 等,如果用户未解锁,尝试操作这些功能可能会被系统主动忽略或延迟处理。
2.实际应用场景
假设手机/车机刚启动,系统还没输入锁屏密码。在这种状态下,蓝牙服务检测到用户未解锁,就会跳过蓝牙的开关逻辑,避免在“加密用户空间”尚未可用时执行敏感操作。
2. 上层 app 侧
app 侧打开蓝牙,这个情况就很多了。 不同的产品需求,上层 app 实现都不一样。 但是对于我们来说这个都很好筛。 我这里分享一下如何去筛这些东东。
1. app 侧打开蓝牙
app 打开蓝牙很简单,都是统一调用:BluetoothAdapter.enable();
我们来看看 应用侧调用完 enable 后,会有那些日志打印:
# 这里会 详细记录: 是 com.xxxx.xxx 应用,调用了 BluetoothAdapter.enable。 根据这个信息就能知道是谁打开了蓝牙
01-01 21:26:59.480246 1693 4049 D BluetoothManagerService: enable(com.xxxx.xxx): mBluetooth =null mBinding = false mState = OFF mAdapterIndex = 0
01-01 21:26:59.488920 1693 1989 D BluetoothManagerService: MESSAGE_ENABLE(0): mBluetooth = null
01-01 21:26:59.489008 1693 1989 D BluetoothManagerService: Persisting Bluetooth Setting: 1
01-01 21:26:59.489355 1693 1989 D BluetoothManagerService: Start binding to the Bluetooth service with intent=Intent { act=android.bluetooth.IBluetooth cmp=com.android.bluetooth/.btservice.AdapterService }
01-01 21:26:59.503157 1693 4049 D BluetoothManagerService: enable returning
# 此时创建了 4958 服务进程
01-01 21:26:59.548853 1693 1879 I am_proc_start: [0,4958,1002,com.android.bluetooth,service,{com.android.bluetooth/com.android.bluetooth.btservice.AdapterService}]
#--------蓝牙服务进程---------
01-01 21:27:00.044515 4958 4958 D BluetoothAdapterApp: Loading JNI Library
3. 蓝牙进程被拉起的几种情况
首先, 第二章节中介绍的 蓝牙打开的几种情况中,肯定是可以拉起我们的蓝牙进程的。这个毋庸置疑。这里不是重点。
重点, 介绍一下。 通过 ps -aux 是可以看到蓝牙进程的, 但是此时蓝牙是没有打开的情况。
先看一下日志:
# 本次冷启动过后, 启动 BluetoothManagerService 服务
05-13 16:41:17.658908 1644 1644 I BluetoothManagerService: BluetoothManagerService(): mAdapterIndex = 0, mDualAdapterMode = disabled
# 此时获取到了 蓝牙的名字和 mac 地址
05-13 16:41:17.659866 1644 1644 D BluetoothManagerService: Loading stored name and address
05-13 16:41:17.659985 1644 1644 D BluetoothManagerService: Stored bluetooth Name=Mycar27839,Address=EC:A7:AD:1D:F3:8A
# 这里发现 上次下电之前蓝牙时关闭的, 所以此时 BluetoothManagerService 没有主动去打开蓝牙
05-13 16:41:17.660054 1644 1644 D BluetoothManagerService: Bluetooth persisted state: 0
05-13 16:41:17.660275 1644 1644 D BluetoothManagerService: Detected SystemUiUid: 10034
05-13 16:41:18.375452 1644 1644 D BluetoothManagerService: Bluetooth boot completed
05-13 16:41:22.953491 1644 1812 I am_proc_start: [0,5385,1002,com.android.bluetooth,service,{com.android.bluetooth/com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService}]
# --------蓝牙进程起来了------------------
05-13 16:41:23.353795 5385 5385 D BluetoothAdapterApp: Loading JNI Library
- 这里就让人很疑惑? 也没有 BluetoothManagerService: enable 这个log. 也看不出来是被那个应用拉起来的。
- 那这个问题该如何 check?
我们可以从如下日志展开
05-13 16:41:22.953491 1644 1812 I am_proc_start: [0,5385,1002,com.android.bluetooth,service,{com.android.bluetooth/com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService}]
1. am_proc_start 标签介绍
am_proc_start 标签的格式如下:
am_proc_start: [caller_uid, pid, uid, package_name, component_type, {component}]
1. 我们可以得出哪些结论?
1. 启动的是哪个进程?
- 进程名是
com.android.bluetooth
,对应 Bluetooth 模块。
2. 启动的组件是什么类型?
service
表示这是通过Context.startService()
或bindService()
启动的一个 服务。
3. 启动了哪个组件?
BluetoothMediaBrowserService
是一个完整类名,表明此服务 是负责蓝牙 AVRCP(媒体控制)相关的浏览器功能。
4. 启动者是谁?
caller_uid=0
通常意味着这是 系统进程(root) 发起的启动动作。uid=1002
表示进程运行在system
用户下,具备一定权限,通常是系统服务。
2.能不能判断是 Activity?
可以。am_proc_start
第五个字段明确告诉你组件类型:
类型字段值 | 含义 |
---|---|
activity |
是一个 Activity |
service |
是一个 Service |
broadcast |
是广播接收器 |
provider |
是一个 ContentProvider |
unknown |
无法判断 |
所以在你的例子中,service
明确表示这个进程是为了运行某个 服务 而被启动的,而不是 Activity。
3. 应用场景
通过监控 am_proc_start
,我们可以实现:
- 分析系统或 App 进程启动时机。
- 判断某个组件(服务、Activity)是否真正启动。
- 诊断系统启动过程中的服务依赖或错误。
- 逆向分析某个服务或功能何时、由谁触发启动。
2. 那此时我们的蓝牙进程是被谁拉起来的?
思路如下:
- 每一个被 system_server 来起来的进程,都会有如下日志打印
05-13 16:41:22.953491 1644 1812 I am_proc_start: [0,5385,1002,com.android.bluetooth,service,{com.android.bluetooth/com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService}]
从上面的日志我们可以分析出如下信息:
字段序号 | 字段值 | 含义说明 |
---|---|---|
0 | 0 |
调用方的 UID,一般为 0 表示 system 进程启动的 |
1 | 5385 |
该进程的 PID(Process ID) |
2 | 1002 |
启动该进程的用户 ID,代表系统用户(通常 system UID) |
3 | com.android.bluetooth |
启动的应用包名 |
4 | service |
启动的组件类型,这里说明是一个 Service 被启动,非 Activity 或 BroadcastReceiver 等 |
5 | {com.android.bluetooth/com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService} |
被启动的具体组件(格式为 {package/component} )是一个具体的服务类 |
可以很明确的看到,当前蓝牙进程是因为 {com.android.bluetooth/com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService}
服务被拉起来的。
service
表示这是通过 Context.startService()
或 bindService()
启动的一个 服务。
这个服务的拉起和 蓝牙音乐app 相关。 一般情况下蓝牙音乐app 为了实现蓝牙音乐的控制。需要 MediaBrowser 服务。
- app 侧可以通过如下代码拉起 蓝牙的 BluetoothMediaBrowserService 服务。
/**
* 连接媒体浏览器服务
* 1、android-7(N版本) ~ android-9(P版本):
* String package = "com.android.bluetooth";
* String class = "com.android.bluetooth.a2dpsink.mbs.A2dpMediaBrowserService"
* <p>
* 2、Android10以上的版本服务包名和文件名分别为:
* String package = "com.android.bluetooth";
* String class = "com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService"
*/
private void connectMediaBrowser() {
//1.绑定服务,Android10以上的版本服务名称为
ComponentName componentName = new ComponentName("com.android.bluetooth", "com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService");
// 2.创建MediaBrowser
mMediaBrowser = new MediaBrowser(getContext(), componentName, connectionCallback, null);
//3.连接MediaBrowser
mMediaBrowser.connect();
}
3. 此时我们的蓝牙可用吗?
此时我们的蓝牙 是否可用?
- 我们知道我们的蓝牙进程此时是由 BluetoothMediaBrowserService 拉起来的。 而蓝牙如果功能上要使用,需要拉起 AdapterService 服务。所以此时蓝牙并没有打开。
- 蓝牙没有被打开,那么对应的芯片的电也没有上。 hal进程中也没有对应的交互。协议栈也不会启动。
4.小结
看到这里,不知道 各位有没有学废啊?
- 其实关键在于 对 am_proc_start 的理解。
// 蓝牙打开时:
01-01 21:11:57.964464 1742 1910 I am_proc_start: [0,2202,1002,com.android.bluetooth,service,{com.android.bluetooth/com.android.bluetooth.btservice.AdapterService}]
// 蓝牙进程被拉起, 但是蓝牙没有被打开
05-13 16:41:22.953491 1644 1812 I am_proc_start: [0,5385,1002,com.android.bluetooth,service,{com.android.bluetooth/com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService}]