Android 蓝牙开发——服务启动流程(二)

news2024/10/7 3:40:27

         首先我们要知道,主要系统服务都是在 SystemServer 启动的,蓝牙也是如此:

1、SystemServer

源码路径:/frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    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 {
        t.traceBegin("StartBluetoothService");
        mSystemServiceManager.startService(BluetoothService.class);
        t.traceEnd();
    }
}

        SystemServer 在启动其他服务的方法里,启动了 BluetoothService。

2、BluetoothService

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 而非 BluetoothService。可以通过 ServiceManager.getService(BLUETOOTH_MANAGER _SERVICE) 获取蓝牙服务。

        onBootPhase(int):这个函数应该是 systemserver 在启动的时候会多次调用,参数代表当前启动进行到了什么阶段,用户定义的 service 针对各个阶段需要做怎样的处理或者是不做任何处理。

3、BluetoothManagerService

    BluetoothManagerService(Context context) {
        //创建内部处理msg的handler
        mHandler = new BluetoothHandler(IoThread.get().getLooper());
        mContext = context;
        ......
        //false表示此次enable需要触发auto connect device和保存状态,BluetoothAdapter::enableNoAutoConnect()可以改变此状态
        mQuietEnableExternal = false;
        mEnableExternal = false;
        ......
        IntentFilter filter = new IntentFilter();
        //监听App通过接口修改BT 名称的广播
        filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
        //监听bt地址改变的广播
        filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
        //监听当前设置需要restore回上一次设置的广播,此时需要重新保存name和addr为上一次的信息
        filter.addAction(Intent.ACTION_SETTING_RESTORED);
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        mContext.registerReceiver(mReceiver, filter);
 
        //从数据库中加载本机Bt的local name和address
        loadStoredNameAndAddress();
        //查看上一次关机时,BT是否为enable状态;如果是,这次开机也需要enable BT
        if (isBluetoothPersistedStateOn()) {
            if (DBG) {
                Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
            }
            mEnableExternal = true;//表明开机过程中需要enable BT
        }
    }

        在服务启动到一定阶段就会回调到 SystemService 的 onBootPhase(int) 方法,即 2 中的该方法,然后调用 BMS 中的 handleOnBootPhase() 方法。

    public void handleOnBootPhase() {   
        ......
        final boolean isSafeMode = mContext.getPackageManager().isSafeMode();
        if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && && !isSafeMode) {
            sendEnableMsg(mQuietEnableExternal/*默认false,表示此次enable需要自动连接device/保存enable状态*/,
                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT,
                    mContext.getPackageName());
        } else if (!isNameAndAddressSet()) {
            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:
    int quietEnable = msg.arg1;
 
    mQuietEnable = (quietEnable == 1);//此时为false
    //mBluetooth是后面绑定Bt apk中AdapterService时拿到的Binder代理对象;用以把操作bypass到BT核心框架中
    if (mBluetooth == null) {
        handleEnable(mQuietEnable);
    } else {//如果mBluetooth不是null,说明之前已经启动过了;此时是Restart flow,以MESSAGE_RESTART_BLUETOOTH_SERVICE触发
        mWaitForEnableRetry = 0;
        Message enableDelayedMsg = mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
        mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
    }
    break;

        handleEnable() 去 Bind AdapterService 拿到它的Binder句柄。同样的在调用 BluetoothManagerService 中的 enable()、disable()等方法时,也是调到 handleEnable() 方法,从而最终调用 AdapterService 中的 enable()、disable() 方法。

private void handleEnable(boolean quietMode) {
    mQuietEnable = quietMode;
 
    try {
        mBluetoothLock.writeLock().lock();
        if ((mBluetooth == null) && (!mBinding)) {
            //Start bind timeout and bind
            Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
            mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
            Intent i = new Intent(IBluetooth.class.getName());
            if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT)) {
                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
            } else {
                mBinding = true;
            }
        }
    } finally {
        mBluetoothLock.writeLock().unlock();
    }
}

         然后我们看一下 doBind() 方法中的 mConnection 参数:

private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
 
private class BluetoothServiceConnection implements ServiceConnection {
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        String name = componentName.getClassName();
 
        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();
 
        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);
    }
}

        拿到 AdapterService 服务后,发送MESSAGE_BLUETOOTH_SERVICE_CONNECTED消息且 arg1 = SERVICE_IBLUETOOTH。

case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
    IBinder service = (IBinder) msg.obj;
    try {
        mBluetoothLock.writeLock().lock();
        mBinding = false;
        mBluetoothBinder = service;
        mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));
 
        //Register callback object
        try {
            mBluetooth.registerCallback(mBluetoothCallback, mContext.getAttributionSource());
        } catch (RemoteException re) {
            Slog.e(TAG, "Unable to register BluetoothCallback", re);
        }
        //Inform BluetoothAdapter instances that service is up
        sendBluetoothServiceUpCallback();
 
        //Do enable request
        try {
            if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) {
                Slog.e(TAG, "IBluetooth.enable() returned false");
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Unable to call enable()", e);
        }
 
    } finally {
         mBluetoothLock.writeLock().unlock();
    }
 
    if (!mEnable) {
        waitForState(Set.of(BluetoothAdapter.STATE_ON));
        handleDisable();
        waitForState(Set.of(BluetoothAdapter.STATE_OFF,
        BluetoothAdapter.STATE_TURNING_ON,
        BluetoothAdapter.STATE_TURNING_OFF,
        BluetoothAdapter.STATE_BLE_TURNING_ON,
        BluetoothAdapter.STATE_BLE_ON,
        BluetoothAdapter.STATE_BLE_TURNING_OFF));
    }
}

主要操作:

    1、拿到 bind 服务的 onBinder() 句柄,并转成 IBluetooth 类型

    2、通过 IBluetooth 类型的 obj,调用 enable() 接口,将 flow 转到 AdapterService 中,做一些初始化、并向 stack 下 enable 蓝牙的 cmd

        至此,enable 蓝牙的 flow 就从 BluetoothManagerService 转到 AdapterService 中了;实际上,通过 BluetoothAdapter 下来的大部分 API 调用最终都是调用到 AdapterService,再通过它下cmd 给 stack。

两个常见到的flag:

mEnable:用来标记系统运行时,蓝牙状态的变化,它有些时候跟 mEnableExternal 值一致。但如果蓝牙的状态是因为某些原因,如 stack 崩溃,导致蓝牙需要重启,重新启动时,需要靠这个 flag 来标记这种 case 的 enable/disable 状态。
mEnableExternal:它主要是记录通过用户手动操作导致的BT使能状态,如通过蓝牙功能按钮来 enable/disable 蓝牙。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/169527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

labelme(2)json文件转类别灰度图

首先感谢大佬:https://blog.csdn.net/tzwsg/article/details/114653071一、上代码,json2gray.py:#!/usr/bin/python # -*- coding: UTF-8 -*- # !H:\Anaconda3\envs\new_labelme\python.exe import argparse import json import os import os…

go语言中变量和常量的注意点

1、类型转换:大类型可以转换成小类型但是精度丢失;小类型不能转换成大类型。例如: package mainimport "fmt"//golang中使用" var "关键字来定义变量 //定义变量的语法:1、var var_name1[,var_name2,...] va…

day16|654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

654.最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums 构…

深度学习入门基础CNN系列——填充(padding)与步幅(stride)

填充(padding) 在上图中,输入图片尺寸为333\times333,输出图片尺寸为222\times222,经过一次卷积之后,图片尺寸为222\times222,经过一次卷积之后,图片尺寸变小。卷积输出特征图的尺寸…

el-table表头添加勾选框

el-table表头添加勾选框嘚吧嘚实现嘚吧嘚 table的行勾选是比较常规的操作,但是有的时候就有各种奇葩的需求蹦出来。😭 比如最近有一个需求,不仅需要勾选行,还需要勾选列,其实我心中有了一万头可爱的小羊驼&#xff0c…

NISP三级证书含金量如何

国家信息安全水平测试(NationalInformationSecurityTestProgram,通称NISP),是通过中国信息安全测评中心执行塑造我国网络空间安全优秀人才的一个项目。 为培养大量出色的实践型网络安全人才,中国信息安全测评中心上线…

SpringCloud Alibaba微服务 -- Seata的原理和使用

文章目录一、认识Seata1.1 Seata 是什么?1.2 了解AT、TCC、SAGA事务模式?AT 模式前提整体机制如何实现写隔离如何实现读隔离TCC 模式Saga 模式Saga 模式适用场景Saga 模式优势Saga 模式缺点二、Seata安装2.1 下载2.2 创建所需数据表2.2.1 创建 分支表、全局表、锁表2.2.2 创建…

Qt OpenGL(10)光照模型基础

文章目录物体的光照模型立方体坐标构建立方体的6个面代码框架widget.cpp顶点着色器片元着色器Ambient 环境光Diffuse 漫反色法向量计算漫反射分量Specular Highlight镜面高光计算镜面反射分量补充:半程向量的使用物体的光照模型 出于性能的原因,一般使用…

思科Cisco交换机的基本命令

一、设备的工作模式1、用户模式Switch>可以查看交换机的基本简单信息,且不能做任何修改配置!2、特权模式Switch> enable Switch#可以查看所有配置,且不能修改配置!3、全局配置模式switch# configure terminal switch(config…

Redis基础——SpringDataRedis快速入门

文章目录1. SpringDataRedis介绍2. SpringDataRedis快速入门2.1 SpringDataRedis的使用步骤1. SpringDataRedis介绍 SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis 官方网址 提供了对不同Redi…

参加猿代码超算实习生计划靠谱吗?

猿代码近期推出了超级实习生计划,相比市面上同类型实习类产品,超算实习生计划服务群体范围更小一些,主要服务于有志于从事芯片行业的大学生们,专做芯片赛道实习就业产品。那么至今为止有人参加过猿代码超算实习生计划吗?这个产品…

〖产品思维训练白宝书 - 核心竞争力篇①〗- 产品经理 的核心竞争力解读

大家好,我是 哈士奇 ,一位工作了十年的"技术混子", 致力于为开发者赋能的UP主, 目前正在运营着 TFS_CLUB社区。 💬 人生格言:优于别人,并不高贵,真正的高贵应该是优于过去的自己。💬 &#x1f4e…

Top 命令中的 Irix 模式与 Solaris 模式(解释单个进程cpu占比为何会超过100%?)

文章目录 背景top cpu 栏位说明Solaris 模式Irix ModeTOP -H切换线程总结背景 关于top命令用了很久了,但是一直对单进程占用cpu占比为何会超过100%认识不够深刻。 top cpu 栏位说明 1. %CPU -- CPU UsageThe tasks share of the elapsed CPU time since the last screen…

深度学习入门基础CNN系列——卷积计算

卷积计算 卷积是数学分析中的一种积分变换的方法,在图像处理中采用的是卷积的离散形式。这里需要说明的是,在卷积神经网络中,卷积层的实现方式实际上是数学中定义的互相关 (cross-correlation)运算,与数学…

【项目实战】package.json你需要了解内容

package.json文件^和~区别 在项目开发中常引用npm包,那么package.json文件^和~区别是什么? ^意思是将当前库的版本更新到第一个数字, 例:"^4.1.0"是库会更新到4.X.X的最新版本,但不会更新到5.X.X版本。~意…

基于卷积深度神经网络的句子单子关系分类(附完整版代码)

基于卷积深度神经网络的关系分类 直接先上结果: 用于关系分类的最先进的方法主要基于统计机器学习,并且它们的性能很大程度上取决于提取的特征的质量。提取的特征通常来自预先存在的自然语言处理(NLP)系统的输出,这导致错误在现有工具中的传播和阻碍这些系统的性能。在本文…

计算机网络学习笔记(四)网络层 - 数据层面

文章目录网络层概述1.转发和路由选择2.网络服务模型网际协议1.IPv4(1) IPv4数据报格式(2)IPv4数据报分片(3)IPv4编址2.IPv6(1) IPv6数据报格式(2)IPv4迁移到IPv6网络层概述 1.转发和路由选择 网络层的作用…

【虹科回顾】2022网络安全精选内容回顾

“我们身上最有价值的东西, 不是证书和技能, 而是过去一切经历的总和。” 2022年已落幕,过去再也不会重来,无论是怎样的一年,都是我们自己生命中特别的一年。 2023年已来,我无法给您提供证书或者技能&a…

Jupyter Lab 的 10 个有用技巧

JupyterLab是 Jupyter Notebook「新」界面。它包含了jupyter notebook的所有功能,并升级增加了很多功能。它最大的更新是模块化的界面,可以在同一个窗口以标签的形式同时打开好几个文档,同时插件管理非常强大,使用起来要比jupyter…

el-date-picker实现通过其他方式触发日期选择器

el-date-picker 目前只能通过点击input输入框触发日期选择器,项目中需要通过其他方式触发日期选择器同时把input输入框去掉,如点击按钮 该模块由于后端接口数据传输限制 在前面文章里做了些许改动。 需求左右切换 可以快速找到年份,于是添加…