【在aosp中,那些情况下可以拉起蓝牙服务进程】

发布于:2025-05-17 ⋅ 阅读:(23) ⋅ 点赞:(0)

1. 背景

在我平时 对 车机 蓝牙的维护中, 经常遇到一个问题就是:

  1. 是谁打开了蓝牙?
  2. 是谁将蓝牙进程拉起来的?

这里有人会问, 打开蓝牙时, 不就把 蓝牙进程拉起来了呀,只要 搞清楚是谁打开的蓝牙,不就知道 进程是被谁拉起来的。为啥还要专门需要搞清楚,他俩的区别呢?

不知道小伙伴有没有遇到一种情况是, 蓝牙进程被拉起来了。 但是蓝牙并没有被打开。如果有遇到 这种情况,但是不知道为何的。 那你就不要着急划走,仔细听笨叔给你娓娓道来。 看完绝对有收获。

2. 蓝牙被打开的几种情况:

1. 系统侧

我们的系统中, 会在如下几种情况下自动打开蓝牙:

  1. BluetoothManagerService 中回去打开蓝牙:
    1. 上次下电前,蓝牙处于打开状态。 再次上电后 BluetoothManagerService 会帮我们再次打开蓝牙。否则不会自动开蓝牙
    2. 上电时,如果 没有获取到 车机蓝牙的名字 和 蓝牙mac地址, 这里也会去打开蓝牙, 但是 获取到之后,蓝牙进程就结束退出了
  2. 休眠唤醒后, 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}]


网站公告

今日签到

点亮在社区的每一天
去签到