Android蓝牙服务启动流程分析探索

目录
  • 1、SystemServer
  • 2、BluetoothService
  • 3、BluetoothManagerService

首先我们要知道,主要系统服务都是在 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 蓝牙。

到此这篇关于Android蓝牙服务启动流程分析探索的文章就介绍到这了,更多相关Android蓝牙服务启动内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 通俗易通讲解Android蓝牙键值适配

    以下图中TV VOD两个按键为例,文章中所涉及到的文件只写文件名,因每个方案的路径各不相同,请自行全局搜索文件.  1.获取按键的扫描码 android设备串口或adb shell下执行getevent -l(小写的L),然后按下按键(以VOD键为例),打印结果如下: getevent显示结果中的三个有效信息: 1)000c006b   前四位000c为键值类型(详情请跳到2与阅读hid.h),后四位为蓝牙键值; 2)KEY_BLUE  如果在linux_key.h中有此按键的定义,此处就会显示

  • Android实现蓝牙串口通讯

    本文实例为大家分享了Android实现蓝牙串口通讯的具体代码,供大家参考,具体内容如下 最近在弄蓝牙串口,参考了不少网上的大佬,加上自己早期对C#的学习,写一个给自己的备忘录,如果有大佬看到还请多多指教. 1.简介 Android设备中提供了一整套蓝牙的API,我这边只取了其中需要的部分. 初期权限 <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission andr

  • Android 连接蓝牙扫码器无输入框的实现

    Android 的APP 需要集成一个蓝牙扫码器, 特别的是,需要扫码的地方是没有输入框的(EditText),不能通过直觉上理解的通过对EditText输入事件进行监听处理,取得扫码结果.并且设备也没有提供SDK. 细想了一下, 蓝牙扫码器本质应该是个HID设备,相当于蓝牙键盘.而后豁然开朗. 每一次扫码应该会触发按键事件,通过监听当前Activity的按键事件,应该可以实现,无输入框的情况下取得扫码结果. 重载Activity中的dispatchKeyEvent实现按键监听. @Overri

  • Android实现蓝牙客户端与服务器端通信示例

    一.首先说明:蓝牙通信必须用手机测试,因为avd里没有相关的硬件,会报错! 好了,看看最后的效果图:   二.概述: 1.判断是否支持Bluetooth BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if(bluetoothAdapter == null) { //the device doesn't support bluetooth } else { //the device support

  • android蓝牙简单开发示例教程

    目录 概述 1.权限申请 2.打开蓝牙 3.接收蓝牙状态的改变 4.扫描其他的设备 5.蓝牙配对 6.获取已经配对的设备 7.连接设备 概述 前段时间学习了一些蓝牙开发的知识,记录一下Android中蓝牙的简单开发.下面是最重要的两个类. BluetoothAdapter : 蓝牙适配器,通过getDefaultAdapter ()去获取一个实例,如果设备不支持蓝牙的话,返回的是一个null对象,通过它,可以打开.关闭蓝牙,扫描设备.向指定设备创建socket通道- BluetoothDevic

  • Android12 蓝牙适配的实现步骤

    目录 前言 一.Android版本中蓝牙简介 二.新建项目 ① 配置settings.gradle和build.gradle ② 配置AndroidManifest.xml 三.打开蓝牙 ① 打开蓝牙意图 ② 请求BLUETOOTH_CONNECT权限意图 四.蓝牙扫描 ① 扫描者 ② 扫描回调 ③ 扫描方法 ④ 执行扫描 ⑤ 应用不推导物理位置 五.页面显示扫描设备 ① 蓝牙设备适配器 ② 显示列表设备 六.适配Android12.0以下设备 七.源码 前言 在我的申请下,公司终于购买了一台基

  • Android 8.0实现蓝牙遥控器自动配对

    本文要实现的是在 android 8.0 的平台上,蓝牙遥控器与TV自动配对,具体就是在TV端打开配对界面,TV端开始搜索远程蓝牙设备,按下遥控器按键让蓝牙遥控器进入对码模式,此时蓝牙遥控器就能作为一个远程蓝牙设备被发现,TV端扫描到这个远程蓝牙设备(蓝牙遥控器),就会自动进行配对连接. 话不多说,直接上代码分析. public class RcConnectActivity extends Activity {         private static final String TAG =

  • Android学习笔记之蓝牙功能

    本文实例为大家分享了Android学习笔记之蓝牙功能的具体代码,供大家参考,具体内容如下 蓝牙:短距离无线通讯技术标准.蓝牙协议分为4层,即核心协议层.电缆替代协议层.电话控制协议层和其他协议层.其中核心协议层包括基带.链路管理.逻辑链路控制和适应协议四部分.链路管理(LMP)负责蓝牙组件间的建立.逻辑链路控制与适应协议(L2CAP)位于基带协议层上,属于数据链路层,是一个高层传输和应用层协议屏蔽基带协议的适配协议. 1).第一种打开蓝牙的方式: Intent enableIntent = ne

  • Android okhttp的启动流程及源码解析

    前言 这篇文章主要讲解了okhttp的主要工作流程以及源码的解析. 什么是OKhttp 简单来说 OkHttp 就是一个客户端用来发送 HTTP 消息并对服务器的响应做出处理的应用层框架. 那么它有什么优点呢? 易使用.易扩展. 支持 HTTP/2 协议,允许对同一主机的所有请求共用同一个 socket 连接. 如果 HTTP/2 不可用, 使用连接池复用减少请求延迟. 支持 GZIP,减小了下载大小. 支持缓存处理,可以避免重复请求. 如果你的服务有多个 IP 地址,当第一次连接失败,OkHt

  • 详解Android Activity的启动流程

    前言 activity启动的流程分为两部分:一是在activity中通过startActivity(Intent intent)方法启动一个Activity:二是我们在桌面通过点击应用图标启动一个App然后显示Activity:第二种方式相较于第一种方式更加全面,所以本文会以第二种流程来分析. 简要 我们手机的桌面是一个叫做Launcher的Activity,它罗列了手机中的应用图标,图标中包含安装apk时解析的应用默认启动页等信息.在点击应用图标时,即将要启动的App和Launcher.AMS

  • 全面解析Android系统指纹启动流程

    本章主要整理Android 指纹启动流程,侧重于hal和framework部分. 一.从Android系统启动流程看指纹启动流程 下图图片出处  → 第一阶段 Boot ROM,Android设备上电后,首先会从处理器片上ROM的启动引导代码开始执行,片上ROM会寻找Bootloader代码,并加载到内存.主要就是上电让系统启动. 第二阶段 Bootloader开始执行,首先负责完成硬件的初始化,然后找到Linux内核代码,并加载到内存. 启动过程中,bootloader(默认是bootable

  • 详解Spring IOC 容器启动流程分析

    使用 Spring 时,XML 和注解是使用得最多的两种配置方式,虽然是两种完全不同的配置方式,但对于 IOC 容器来说,两种方式的不同主要是在 BeanDefinition 的解析上.而对于核心的容器启动流程,仍然是一致的. AbstractApplicationContext 的 refresh 方法实现了 IOC 容器启动的主要逻辑,启动流程中的关键步骤在源码中也可以对应到独立的方法.接下来以  AbstractApplicationContext 的实现类  ClassPathXmlAp

  • 在idea打包并上传到云服务项目流程分析

    一.首先 得先在idea打包好. 我使用的是springboot框架的项目 1.先开启自己的项目 在idea的最右侧有个这个边框,点击Maven 如果之前有该项目的打包,可以点击clean去清理之前的缓存,然后点击package进行打包项目 二.打包好了之后我们就需要自己的云服务器了 可以使用SecureCRT或者XShell插件操纵我们的阿里云服务器. 这里我使用的是XShell, 在此之前 ,我们需要云服务的环境搭配好的前提下,java jdk的环境,mysql数据库的搭建,还有tomcat

  • Android开发App启动流程与消息机制详解

    目录 引言 1.第一步了解 ThreadLocal 2.App的启动流程 3.Activity中创建Handler 引言 相信很多人对这个问题不陌生,但是大家回答的都比较简单,如谈到app启动流程有人就会是app的生命周期去了,谈到消息机制有人就会说looper循环消息进行分发,如果是面试可能面试官不会满意,今天我们搞一篇完善的源码解析来进行阐述上面的问题 1.第一步了解 ThreadLocal 什么是ThreadLocal呢,专业的来讲,ThreadLocal 是一个线程内部的数据存储类,通过

  • Spring Boot启动流程分析

    引言 早在15年的时候就开始用spring boot进行开发了,然而一直就只是用用,并没有深入去了解spring boot是以什么原理怎样工作的,说来也惭愧.今天让我们从spring boot启动开始,深入了解一下spring boot的工作原理. 为什么用spring boot 在使用一个东西或者一个工具之前,我们总是会问自己,我为什么要用?用他能给我带来什么好处? * 最大的好处就是spring boot遵从了java**约定大于配置**不用面对一大堆的配置文件,spring boot是根据

  • SpringBoot中WEB的启动流程分析

    目录 一.DispatcherServlet的注册 1.1 把DispatcherServlet注入IOC容器 1.2 把DispatcherServlet注入Servlet容器 想必大家都体验过springboot的便捷,以前想要运行web项目,我们首先需要将项目打成war包,然后再运行Tomcat启动项目,不过自从有了springboot,我们可以像启动jar包一样简单的启动一个web项目,今天我们就来分析下springboot启动web项目整个流程. 老规矩,我们从spring.facto

  • 分析Android中应用的启动流程

    前言 在我们开始之前,希望您能最好已经满足以下条件: 1.有一份编译后的Android源码(亲自动手实践才会有更深入的理解) 2.对Binder机制有一定的了解 本文启动流程分析基于Android 5.1的源码.为什么是5.1的源码呢?因为手边编译完的代码只有这个版本-另外,用什么版本的源码并不重要,大体的流程并无本质上的区别,仅仅是实现细节的调整,找一个你熟悉的版本就好. 1.启动时序图 作为一个轻微强迫症的人,整理的时序图,相信大家按图索骥,一定能搞明白整个启动流程: 说明:为了让大家更清楚

  • 详解Android壁纸服务的启动过程

    壁纸基础 android中的壁纸分为动态壁纸和静态壁纸两种,两种类型的壁纸都以Service的类型运行在系统后台. 静态壁纸:仅以图片的形式进行展示对于静态壁纸,可以使用WallpaperManager中的getDrawable()等接口获取到当前的bitmap图像. 动态壁纸:显示的内容为动态的内容,同时可以对用户的操作做出响应对于动态壁纸的实时图像,是没办法通过android中原生的接口获取到,需要获取到动态壁纸的图像得自己修改源码. 壁纸实现时涉及的几个主要的类: WallpaperSer

随机推荐

其他