Android -- AdapterService之蓝牙启动过程分析

ActiveDeviceManager文件的分析-一个蓝牙耳机的问题 android9.0系统中增加了ActiveDeviceManager文件,该文件负责跟踪连接的A2DP / HFP / AVRCP / HearingAid设备,并选择哪个设备处于活动状态(对于每个配置文件)。 在该文件中注册了以下这些广播: filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(Blu... 阅读详情
AI摘要

本文详细剖析了Android系统中蓝牙启动的过程,从AdapterService的生命周期出发,深入探讨了蓝牙服务的启动、状态转换、Profile激活及与底层协议栈的交互机制。

                  Android -- AdapterService之蓝牙启动过程分析

 

前面介绍BluetoothManagerService启动过程enable BT的部分提到,处理ENABLE msg的最终调用主要是call到AdapterService注册IBluetoothCallback回调监听蓝牙使能状态、以及AdapterService::enable()接口开启蓝牙,下面就接着这部分往下看。

AdapterService是Service的子类,Bind service会触发依次调用它的声明周期函数onCreate() -> onBind():

public class AdapterService extends Service {
......
    static {
        classInitNative();//native层的init部分
    }
......
    @Override
    public void onCreate() {
        super.onCreate();
        debugLog("onCreate()");
        mRemoteDevices = new RemoteDevices(this, Looper.getMainLooper());//重要类型,它实现了接受stack反馈的device信息变化的回调,比如UUID
        mRemoteDevices.init();
        mBinder = new AdapterServiceBinder(this);//AdapterService内部封装的IBluetooth接口实现者
        mAdapterProperties = new AdapterProperties(this);//管理当前Adapter的一些属性,比如配对的device,uuid信息等;有些接口供stack回调上报状态使用
        mAdapterStateMachine = AdapterState.make(this);//一个简单的StateMachine,处理开关BT中间的状态切换
        mJniCallbacks = new JniCallbacks(this, mAdapterProperties);//stack会通过此对象上报一些bt状态和信息
        initNative();//native层的init工作
......
// Phone policy is specific to phone implementations and hence if a device wants to exclude
        // it out then it can be disabled by using the flag below.
        if (getResources().getBoolean(com.android.bluetooth.R.bool.enable_phone_policy)) {
            Log.i(TAG, "Phone policy enabled");
            mPhonePolicy = new PhonePolicy(this, new ServiceFactory());//启动PhonePolicy,它会根据触发重连之前的设备;在available的设备之间处理连接切换等
            mPhonePolicy.start();
        } else {
            Log.i(TAG, "Phone policy disabled");
        }

        mActiveDeviceManager = new ActiveDeviceManager(this, new ServiceFactory());//处理Active Device的管理类;比如Android默认可以连接多个sink设备,但只有一个出声,这个就是active的device;
                                                                                   //要通过AudioManager的接口告知Audio当前系统哪个device是active的,这样Audio系统才能根据策略选择正确的设备播放音频
        mActiveDeviceManager.start();

        setAdapterService(this);//保存当前的AdapterServie实例,因为其他的类里可能要获取它
......
}
......
    @Override
    public IBinder onBind(Intent intent) {
        debugLog("onBind()");
        return mBinder;
    }
......
    /**
     * Handlers for incoming service calls
     */
    private AdapterServiceBinder mBinder;

    /**
     * The Binder implementation must be declared to be a static class, with
     * the AdapterService instance passed in the constructor. Furthermore,
     * when the AdapterService shuts down, the reference to the AdapterService
     * must be explicitly removed.
     *
     * Otherwise, a memory leak can occur from repeated starting/stopping the
     * service...Please refer to android.os.Binder for further details on
     * why an inner instance class should be avoided.
     *
     */
    private static class AdapterServiceBinder extends IBluetooth.Stub {
        private AdapterService mService;

        AdapterServiceBinder(AdapterService svc) {
            mService = svc;
        }

        public void cleanup() {
            mService = null;
        }

        public AdapterService getService() {
            if (mService != null && mService.isAvailable()) {
                return mService;
            }
            return null;
        }

......
        @Override
        public boolean enable() {
            if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!Utils.checkCaller())) {
                Log.w(TAG, "enable() - Not allowed for non-active user and non system user");
                return false;
            }
            AdapterService service = getService();
            if (service == null) {
                return false;
            }
            return service.enable();
        }
......
}
}
}

从code看出,BluetoothManagerService先获取到IBinder对象,再通过它调用AdapterService的对于方法完成工作。为了较好的跟flow,我们先看初始化部分;其他的细节可以参考注释:

加载AdapterService类时,需要完成的static代码块调用:

    static {
        classInitNative();
    }
......
static void classInitNative(JNIEnv* env, jclass clazz) {
  jclass jniUidTrafficClass = env->FindClass("android/bluetooth/UidTraffic");
  android_bluetooth_UidTraffic.constructor =
      env->GetMethodID(jniUidTrafficClass, "<init>", "(IJJ)V");

  jclass jniCallbackClass =
      env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
  sJniCallbacksField = env->GetFieldID(
      clazz, "mJniCallbacks", "Lcom/android/bluetooth/btservice/JniCallbacks;");//获取AdapterService中的mJniCallbacks实例,通知上层stack的变化信息

  method_stateChangeCallback =
      env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V");//

  method_adapterPropertyChangedCallback = env->GetMethodID(
      jniCallbackClass, "adapterPropertyChangedCallback", "([I[[B)V");
  method_discoveryStateChangeCallback = env->GetMethodID(
      jniCallbackClass, "discoveryStateChangeCallback", "(I)V");

  method_devicePropertyChangedCallback = env->GetMethodID(
      jniCallbackClass, "devicePropertyChangedCallback", "([B[I[[B)V");
  method_deviceFoundCallback =
      env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
  method_pinRequestCallback =
      env->GetMethodID(jniCallbackClass, "pinRequestCallback", "([B[BIZ)V");
  method_sspRequestCallback =
      env->GetMethodID(jniCallbackClass, "sspRequestCallback", "([B[BIII)V");

  method_bondStateChangeCallback =
      env->GetMethodID(jniCallbackClass, "bondStateChangeCallback", "(I[BI)V");

  method_aclStateChangeCallback =
      env->GetMethodID(jniCallbackClass, "aclStateChangeCallback", "(I[BI)V");

  method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z");
  method_acquireWakeLock =
      env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z");
  method_releaseWakeLock =
      env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z");
  method_energyInfo = env->GetMethodID(
      clazz, "energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)V");

  if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {//加载bt stack的so,获取操作句柄sBluetoothInterface;用以调用stack的接口完成操作
    ALOGE("No Bluetooth Library found");
  }
}

这部分初始化工作,从code看很清晰;主要做两件事:

  1. 在Jni层获取AdapterService中的callback接口,当stack上报状态时,需要通过这些接口反馈到framework层
  2. 解析bt so,获取它暴露给上层的接口,用来将framework的操作请求真正下发到协议栈中。主要是dlopen方式操作so、拿到操作handle的,source code部分主要在Pie_9.0.0_r3\system\bt\main中

bt_interface_t类型定义的都是上层会涉及到的bt操作,如果connect/disconnect、createBond/removeBond等,最终都会此类型将请求下发到协议栈中:

/** Represents the standard Bluetooth DM interface. */
typedef struct {
  /** set to sizeof(bt_interface_t) */
  size_t size;
  /**
   * Opens the interface and provides the callback routines
   * to the implemenation of this interface.
   */
  int (*init)(bt_callbacks_t* callbacks);

  /** Enable Bluetooth. */
  int (*enable)(bool guest_mode);

  /** Disable Bluetooth. */
  int (*disable)(void);

  /** Closes the interface. */
  void (*cleanup)(void);

  /** Remove Bond */
  int (*remove_bond)(const RawAddress* bd_addr);

  /** Cancel Bond */
  int (*cancel_bond)(const RawAddress* bd_addr);

  /**
   * Get the connection status for a given remote device.
   * return value of 0 means the device is not connected,
   * non-zero return status indicates an active connection.
   */
  int (*get_connection_state)(const RawAddress* bd_addr);


  /** Read Energy info details - return value indicates BT_STATUS_SUCCESS or
   * BT_STATUS_NOT_READY Success indicates that the VSC command was sent to
   * controller
   */
  int (*read_energy_info)();

  /**
   * Get the AvrcpTarget Service interface to interact with the Avrcp Service
   */
  bluetooth::avrcp::ServiceInterface* (*get_avrcp_service)(void);
} bt_interface_t;

静态初始化块完成后,就是Service::onCreate()的调用,除了前面提到的初始化一些重要的Java对象外,还会做一些Native层的初始化:

static bool initNative(JNIEnv* env, jobject obj) {
  ALOGV("%s", __func__);

  android_bluetooth_UidTraffic.clazz =
      (jclass)env->NewGlobalRef(env->FindClass("android/bluetooth/UidTraffic"));

  sJniAdapterServiceObj = env->NewGlobalRef(obj);
  sJniCallbacksObj =
      env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));

  if (!sBluetoothInterface) {
    return JNI_FALSE;
  }

  int ret = sBluetoothInterface->init(&sBluetoothCallbacks);//将需要的一些callback函数注册到stack中,这样stack就可以通过这些函数上报此类事件
  if (ret != BT_STATUS_SUCCESS) {
    ALOGE("Error while setting the callbacks: %d\n", ret);
    sBluetoothInterface = NULL;
    return JNI_FALSE;
  }
  ret = sBluetoothInterface->set_os_callouts(&sBluetoothOsCallouts);//将需要的一些callback函数注册到stack中,这样stack就可以通过这些函数上报此类事件
  if (ret != BT_STATUS_SUCCESS) {
    ALOGE("Error while setting Bluetooth callouts: %d\n", ret);
    sBluetoothInterface->cleanup();
    sBluetoothInterface = NULL;
    return JNI_FALSE;
  }
......
static bt_callbacks_t sBluetoothCallbacks = {
    sizeof(sBluetoothCallbacks), adapter_state_change_callback,
    adapter_properties_callback, remote_device_properties_callback,
    device_found_callback,       discovery_state_changed_callback,
    pin_request_callback,        ssp_request_callback,
    bond_state_changed_callback, acl_state_changed_callback,
    callback_thread_event,       dut_mode_recv_callback,
    le_test_mode_recv_callback,  energy_info_recv_callback};
......
static bt_os_callouts_t sBluetoothOsCallouts = {
    sizeof(sBluetoothOsCallouts), set_wake_alarm_callout,
    acquire_wake_lock_callout, release_wake_lock_callout,
};

将stack需要用到的一些callback函数注册给它,这样在需要的时候,stack就可以通过调用这些函数将信息反馈到上层了。

这条路串起来之后,再回头看AdapterService::enable():

    public synchronized boolean enable(boolean quietMode) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");

        // Enforce the user restriction for disallowing Bluetooth if it was set.
        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM)) {
            debugLog("enable() called when Bluetooth was disallowed");
            return false;
        }

        debugLog("enable() - Enable called with quiet mode status =  " + quietMode);
        mQuietmode = quietMode;
        mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_ON);
        return true;
    }

可以看到只是简单的向AdapterState发送一个BLE_TURN_ON msg,随机进入状态机去处理;

/**
 * This state machine handles Bluetooth Adapter State.
 * Stable States:
 *      {@link OffState}: Initial State
 *      {@link BleOnState} : Bluetooth Low Energy, Including GATT, is on
 *      {@link OnState} : Bluetooth is on (All supported profiles)
 *
 * Transition States:
 *      {@link TurningBleOnState} : OffState to BleOnState
 *      {@link TurningBleOffState} : BleOnState to OffState
 *      {@link TurningOnState} : BleOnState to OnState
 *      {@link TurningOffState} : OnState to BleOnState
 *
 *        +------   Off  <-----+
 *        |                    |
 *        v                    |
 * TurningBleOn   TO--->   TurningBleOff
 *        |                  ^ ^
 *        |                  | |
 *        +----->        ----+ |
 *                 BleOn       |
 *        +------        <---+ O
 *        v                  | T
 *    TurningOn  TO---->  TurningOff
 *        |                    ^
 *        |                    |
 *        +----->   On   ------+
 *
 */

从上图AdapterState的注释及code,我们知道以下几点:

  1. AdapterState中所有State的父类都是BaseAdapterState,所以在enter某个状态机时,会先调用此父类的enter()接口;同时,在exit某个状态机时,也会call此父类的exit()接口
  2. 默认的初始化状态时Off状态,也就是此时BT尚未启动
  3. 因为AdapterState是管理BT启动的逻辑主要部分,所以向外通知当前BT使能进程的消息,应该在这里触发比较合理

因为在某个状态节点,我们都应该向外通知状态,所以这部分被封装到了父类State中:

AdapterState:
    private abstract class BaseAdapterState extends State {

        abstract int getStateValue();

        @Override
        public void enter() {
            int currState = getStateValue();
            infoLog("entered ");
            mAdapterService.updateAdapterState(mPrevState, currState);//通过AdapterService向外通知BT使能的状态信息
            mPrevState = currState;
        }
......
    }

AdapterService:
    void updateAdapterState(int prevState, int newState) {//如果某个服务对蓝牙启动状态过程感兴趣,它就应该注册IBluetootCallback对象;跟BluetoothManagerService一样
        mAdapterProperties.setState(newState);
        if (mCallbacks != null) {
            int n = mCallbacks.beginBroadcast();
            debugLog("updateAdapterState() - Broadcasting state " + BluetoothAdapter.nameForState(
                    newState) + " to " + n + " receivers.");
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks.getBroadcastItem(i).onBluetoothStateChange(prevState, newState);//调用onBluetoothStateChange()回调状态变化
                } catch (RemoteException e) {
                    debugLog("updateAdapterState() - Callback #" + i + " failed (" + e + ")");
                }
            }
            mCallbacks.finishBroadcast();
        }
......
    }

BluetoothManagerService:
    private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
        @Override
        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
            Message msg =
                    mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);//发送MESSAGE_BLUETOOTH_STATE_CHANGE
            mHandler.sendMessage(msg);
        }
    };

这里我们发送的是BLE_TURN_ON msg,看它的处理:

    private class OffState extends BaseAdapterState {

        @Override
        int getStateValue() {
            return BluetoothAdapter.STATE_OFF;
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case BLE_TURN_ON:
                    transitionTo(mTurningBleOnState);
                    break;

                default:
                    infoLog("Unhandled message - " + messageString(msg.what));
                    return false;
            }
            return true;
        }
    }
......
 private class TurningBleOnState extends BaseAdapterState {

        @Override
        int getStateValue() {
            return BluetoothAdapter.STATE_BLE_TURNING_ON;
        }

        @Override
        public void enter() {
            super.enter();
            sendMessageDelayed(BLE_START_TIMEOUT, BLE_START_TIMEOUT_DELAY);
            mAdapterService.bringUpBle();
        }

        @Override
        public void exit() {
            removeMessages(BLE_START_TIMEOUT);
            super.exit();
        }

        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case BLE_STARTED:
                    transitionTo(mBleOnState);
                    break;

                case BLE_START_TIMEOUT:
                    errorLog(messageString(msg.what));
                    transitionTo(mTurningBleOffState);
                    break;

                default:
                    infoLog("Unhandled message - " + messageString(msg.what));
                    return false;
            }
            return true;
        }
    }
......
    }

BLE_TURN_ON的消息处理是transferTo TurningBleOnState,它的enter()会调用mAdapterService.bringUpBle():

    void bringUpBle() {
        debugLog("bleOnProcessStart()");

        if (getResources().getBoolean(
                R.bool.config_bluetooth_reload_supported_profiles_when_enabled)) {
            Config.init(getApplicationContext());
        }

        // Reset |mRemoteDevices| whenever BLE is turned off then on
        // This is to replace the fact that |mRemoteDevices| was
        // reinitialized in previous code.
        //
        // TODO(apanicke): The reason is unclear but
        // I believe it is to clear the variable every time BLE was
        // turned off then on. The same effect can be achieved by
        // calling cleanup but this may not be necessary at all
        // We should figure out why this is needed later
        mRemoteDevices.reset();//RemoteDeivces管理对端设备的一些信息
        mAdapterProperties.init(mRemoteDevices);//AdapterProperties管理当前Adapter的一些信息

        debugLog("bleOnProcessStart() - Make Bond State Machine");
        mBondStateMachine = BondStateMachine.make(this, mAdapterProperties, mRemoteDevices);//创建BondStateMachine对象,它负责管理一个device的配对过程

        mJniCallbacks.init(mBondStateMachine, mRemoteDevices);//JniCallbacks主要负责监听来自stack的callback信息,如设备配对状态或设备更新UUID变化时,framework就可以收到回调

        try {
            mBatteryStats.noteResetBleScan();
        } catch (RemoteException e) {
            Log.w(TAG, "RemoteException trying to send a reset to BatteryStats");
        }
        StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, -1, null,
                StatsLog.BLE_SCAN_STATE_CHANGED__STATE__RESET, false, false, false);

        //Start Gatt service
        setProfileServiceState(GattService.class, BluetoothAdapter.STATE_ON);//启动GattService服务,BLE设备需要GATT协议交换信息
    }

除了创建一些重要对象外,这里主要就是起到GattService了,Ble设备都需要通过GATT协议交换信息;其他可以参考上面的注释。

Bluetooth framework声明的Profile服务基本都是ProfileService的子类,所以参照Android Service的启动过程,某个Profile启动时,会调用到ProfileService的方法:

/**
 * Base class for a background service that runs a Bluetooth profile
 */
public abstract class ProfileService extends Service {


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            ......
            if (state == BluetoothAdapter.STATE_OFF) {
                doStop();//通知AdapterService的此Profile停止成功
            } else if (state == BluetoothAdapter.STATE_ON) {
                doStart();//通知AdapterService的此Profile启动成功
            }
        }
        return PROFILE_SERVICE_MODE;
    }

 private void doStart() {
        ......
        mAdapterService.addProfile(this);//告知AdapterService此Profile启动成功,更新Profile集合

        ......
        mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON);//通知AdapterService此Profile已启动
    }

    private void doStop() {
        ......
        if (mAdapterService != null) {
            mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_OFF);//通知AdapterService此Profile已停止
        }
        if (!stop()) {
            Log.e(mName, "Unable to stop profile");
        }
        if (mAdapterService != null) {
            mAdapterService.removeProfile(this);//告知AdapterService此Profile停止成功,更新Profile集合
        }
        ......
        stopSelf();
    }
}

接着看AdapterService的处理:

/**
     * Register a {@link ProfileService} with AdapterService.
     *
     * @param profile the service being added.
     */
    public void addProfile(ProfileService profile) {
        mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_REGISTERED, profile).sendToTarget();
    }

    /**
     * Unregister a ProfileService with AdapterService.
     *
     * @param profile the service being removed.
     */
    public void removeProfile(ProfileService profile) {
        mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_UNREGISTERED, profile).sendToTarget();
    }

    /**
     * Notify AdapterService that a ProfileService has started or stopped.
     *
     * @param profile the service being removed.
     * @param state {@link BluetoothAdapter#STATE_ON} or {@link BluetoothAdapter#STATE_OFF}
     */
    public void onProfileServiceStateChanged(ProfileService profile, int state) {
        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
            throw new IllegalArgumentException(BluetoothAdapter.nameForState(state));
        }
        Message m = mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
        m.obj = profile;
        m.arg1 = state;
        mHandler.sendMessage(m);
    }

    private static final int MESSAGE_PROFILE_SERVICE_STATE_CHANGED = 1;
    private static final int MESSAGE_PROFILE_SERVICE_REGISTERED = 2;
    private static final int MESSAGE_PROFILE_SERVICE_UNREGISTERED = 3;

    class AdapterServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            debugLog("handleMessage() - Message: " + msg.what);

            switch (msg.what) {
                case MESSAGE_PROFILE_SERVICE_STATE_CHANGED:
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_STATE_CHANGED");
                    processProfileServiceStateChanged((ProfileService) msg.obj, msg.arg1);
                    break;
                case MESSAGE_PROFILE_SERVICE_REGISTERED:
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_REGISTERED");
                    registerProfileService((ProfileService) msg.obj);
                    break;
                case MESSAGE_PROFILE_SERVICE_UNREGISTERED:
                    debugLog("handleMessage() - MESSAGE_PROFILE_SERVICE_UNREGISTERED");
                    unregisterProfileService((ProfileService) msg.obj);
                    break;
            }
        }

        private void registerProfileService(ProfileService profile) {
            if (mRegisteredProfiles.contains(profile)) {
                Log.e(TAG, profile.getName() + " already registered.");
                return;
            }
            mRegisteredProfiles.add(profile);
        }

        private void unregisterProfileService(ProfileService profile) {
            if (!mRegisteredProfiles.contains(profile)) {
                Log.e(TAG, profile.getName() + " not registered (UNREGISTERED).");
                return;
            }
            mRegisteredProfiles.remove(profile);
        }

        private void processProfileServiceStateChanged(ProfileService profile, int state) {
            switch (state) {
                case BluetoothAdapter.STATE_ON://此时是启动GattService,所以是STATE_ON
                    if (!mRegisteredProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " not registered (STATE_ON).");
                        return;
                    }
                    if (mRunningProfiles.contains(profile)) {//防止重复启动某个Profile
                        Log.e(TAG, profile.getName() + " already running.");
                        return;
                    }
                    mRunningProfiles.add(profile);
                    if (GattService.class.getSimpleName().equals(profile.getName())) {
                        enableNativeWithGuestFlag();//如果是启动Gatt profile,则向stack下发enable bt的指令
                    } else if (mRegisteredProfiles.size() == Config.getSupportedProfiles().length
                            && mRegisteredProfiles.size() == mRunningProfiles.size()) {//这里是启动Gatt以外的,在config.xml中声明支持的其他Profile;相当于启动BREDR mode
                        mAdapterProperties.onBluetoothReady();
                        updateUuids();
                        setBluetoothClassFromConfig();
                        mAdapterStateMachine.sendMessage(AdapterState.BREDR_STARTED);
                    }
                    break;
                case BluetoothAdapter.STATE_OFF:
                    if (!mRegisteredProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " not registered (STATE_OFF).");
                        return;
                    }
                    if (!mRunningProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " not running.");
                        return;
                    }
                    mRunningProfiles.remove(profile);
                    // If only GATT is left, send BREDR_STOPPED.
                    if ((mRunningProfiles.size() == 1 && (GattService.class.getSimpleName()
                            .equals(mRunningProfiles.get(0).getName())))) {
                        mAdapterStateMachine.sendMessage(AdapterState.BREDR_STOPPED);
                    } else if (mRunningProfiles.size() == 0) {
                        disableNative();
                        mAdapterStateMachine.sendMessage(AdapterState.BLE_STOPPED);
                    }
                    break;
                default:
                    Log.e(TAG, "Unhandled profile state: " + state);
            }
        }
    }

处理enable 蓝牙:

    private void enableNativeWithGuestFlag() {
        boolean isGuest = UserManager.get(this).isGuestUser();
        if (!enableNative(isGuest)) {
            Log.e(TAG, "enableNative() returned false");
        }
    }

static jboolean enableNative(JNIEnv* env, jobject obj, jboolean isGuest) {
  ALOGV("%s", __func__);

  if (!sBluetoothInterface) return JNI_FALSE;
  int ret = sBluetoothInterface->enable(isGuest == JNI_TRUE ? 1 : 0);
  return (ret == BT_STATUS_SUCCESS || ret == BT_STATUS_DONE) ? JNI_TRUE
                                                             : JNI_FALSE;
}

前面有介绍到stack的state回调会通过JNICallbacks对象回调上来,enable状态也是如此:

final class JniCallbacks {

    void stateChangeCallback(int status) {
        mAdapterService.stateChangeCallback(status);
    }
}


AdapterService:
    void stateChangeCallback(int status) {
        if (status == AbstractionLayer.BT_STATE_OFF) {
            debugLog("stateChangeCallback: disableNative() completed");
        } else if (status == AbstractionLayer.BT_STATE_ON) {//BT State on
            mAdapterStateMachine.sendMessage(AdapterState.BLE_STARTED);
        } else {
            Log.e(TAG, "Incorrect status " + status + " in stateChangeCallback");
        }
    }

BT启动之后,向AdapterState通知状态,TurningBleOnState收到消息后,就会进入BleOnState;并对外通知状态变化,如之前介绍,是通过AdapterService::updateAdapterState()函数处理:

    void updateAdapterState(int prevState, int newState) {//如果某个服务对蓝牙启动状态过程感兴趣,它就应该注册IBluetootCallback对象;跟BluetoothManagerService一样
        mAdapterProperties.setState(newState);
        if (mCallbacks != null) {
            int n = mCallbacks.beginBroadcast();
            debugLog("updateAdapterState() - Broadcasting state " + BluetoothAdapter.nameForState(
                    newState) + " to " + n + " receivers.");
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks.getBroadcastItem(i).onBluetoothStateChange(prevState, newState);//调用onBluetoothStateChange()回调状态变化
                } catch (RemoteException e) {
                    debugLog("updateAdapterState() - Callback #" + i + " failed (" + e + ")");
                }
            }
            mCallbacks.finishBroadcast();
        }
        // Turn the Adapter all the way off if we are disabling and the snoop log setting changed.
        if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON) {
            mSnoopLogSettingAtEnable =
                    SystemProperties.getBoolean(BLUETOOTH_BTSNOOP_ENABLE_PROPERTY, false);
        } else if (newState == BluetoothAdapter.STATE_BLE_ON
                   && prevState != BluetoothAdapter.STATE_OFF) {
            boolean snoopLogSetting =
                    SystemProperties.getBoolean(BLUETOOTH_BTSNOOP_ENABLE_PROPERTY, false);
            if (mSnoopLogSettingAtEnable != snoopLogSetting) {
                mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_OFF);
            }
        }
    }

在介绍BluetoothManagerService时,我们就提到BMS会向AdapterService注册一个callback来监听BT的状态:

    private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
        @Override
        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
            Message msg =
                    mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);//发送MESSAGE_BLUETOOTH_STATE_CHANGE
            mHandler.sendMessage(msg);
        }
    };

处理Msg:

                case MESSAGE_BLUETOOTH_STATE_CHANGE: {
                    int prevState = msg.arg1;
                    int newState = msg.arg2;
                    if (DBG) {
                        Slog.d(TAG,
                                "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(
                                        prevState) + " > " + BluetoothAdapter.nameForState(
                                        newState));
                    }
                    mState = newState;
                    bluetoothStateChangeHandler(prevState, newState);//处理state变化
                    ......
                }

看bluetoothStateChangeHandler()的主要处理:

        // Notify all proxy objects first of adapter state change
        if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) {
            boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
                    && newState == BluetoothAdapter.STATE_BLE_ON);

            if (newState == BluetoothAdapter.STATE_OFF) {
                // If Bluetooth is off, send service down event to proxy objects, and unbind
                if (DBG) {
                    Slog.d(TAG, "Bluetooth is complete send Service Down");
                }
                sendBluetoothServiceDownCallback();
                unbindAndFinish();
                sendBleStateChanged(prevState, newState);
                // Don't broadcast as it has already been broadcast before
                isStandardBroadcast = false;

            } else if (!intermediate_off) {//现在state是BLE_ON
                // connect to GattService
                if (DBG) {
                    Slog.d(TAG, "Bluetooth is in LE only mode");
                }
                if (mBluetoothGatt != null || !mContext.getPackageManager()
                            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {//此时GattService只是启动了,BMS还未拿到它的Binder操作句柄
                    continueFromBleOnState();
                } else {
                    if (DBG) {
                        Slog.d(TAG, "Binding Bluetooth GATT service");
                    }
                    Intent i = new Intent(IBluetoothGatt.class.getName());
                    doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
                            UserHandle.CURRENT);//这里去Bind GattService,然后在BluetoothServiceConnection中触发MESSAGE_BLUETOOTH_SERVICE_CONNECTED msg,并区分是SERVICE_IBLUETOOTHGATT
                }
                sendBleStateChanged(prevState, newState);
                //Don't broadcase this as std intent
                isStandardBroadcast = false;

            } else if (intermediate_off) {
                if (DBG) {
                    Slog.d(TAG, "Intermediate off, back to LE only mode");
                }
                // For LE only mode, broadcast as is
                sendBleStateChanged(prevState, newState);
                sendBluetoothStateCallback(false); // BT is OFF for general users
                // Broadcast as STATE_OFF
                newState = BluetoothAdapter.STATE_OFF;
                sendBrEdrDownCallback();
            }
        } 

BluetoothManagerService中Bind Profile的内容跟之前介绍的一样,我们直接看处理:

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));//获取GattService的句柄
                            continueFromBleOnState();//启动BREDR的profile
                            break;
                        } // else must be SERVICE_IBLUETOOTH
}

获取Gatt profile的Binder句柄,并调用continueFromBleOnState()去启动其他的profile:

    /**
     * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on.
     */
    private void continueFromBleOnState() {
        if (DBG) {
            Slog.d(TAG, "continueFromBleOnState()");
        }
        try {
            mBluetoothLock.readLock().lock();
            if (mBluetooth == null) {
                Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!");
                return;
            }
            if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
                // This triggers transition to STATE_ON
                mBluetooth.onLeServiceUp();//调用onLeServiceUp()
                persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Unable to call onServiceUp", e);
        } finally {
            mBluetoothLock.readLock().unlock();
        }
    }


AdapterService:
    void onLeServiceUp() {
        mAdapterStateMachine.sendMessage(AdapterState.USER_TURN_ON);//发送USER_TURN_ON
    }

//现在处于BleOnState,处理此消息进入TurningOnState
AdapterState:

    private class TurningOnState extends BaseAdapterState {

        @Override
        int getStateValue() {
            return BluetoothAdapter.STATE_TURNING_ON;
        }

        @Override
        public void enter() {
            super.enter();
            sendMessageDelayed(BREDR_START_TIMEOUT, BREDR_START_TIMEOUT_DELAY);
            mAdapterService.startProfileServices();
        }
        ......
}

//启动配置文件中定义的Profile:
AdapterService:
    void startProfileServices() {
        debugLog("startCoreServices()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();
        if (supportedProfileServices.length == 1 && GattService.class.getSimpleName()
                .equals(supportedProfileServices[0].getSimpleName())) {
            mAdapterProperties.onBluetoothReady();
            updateUuids();
            setBluetoothClassFromConfig();
            mAdapterStateMachine.sendMessage(AdapterState.BREDR_STARTED);
        } else {
            setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_ON);
        }
    }

最终的处理是启动全部在Bluetooth.apk的config.xml中定义的Profile,Config.getSupportedProfiles()调用拿到的Profile集合就是声明了true的Service:

    <bool name="profile_supported_a2dp">true</bool>
    <bool name="profile_supported_a2dp_sink">false</bool>
    <bool name="profile_supported_hdp">true</bool>
    <bool name="profile_supported_hs_hfp">true</bool>
    <bool name="profile_supported_hfpclient">false</bool>
    <bool name="profile_supported_hid_host">true</bool>
    <bool name="profile_supported_opp">true</bool>
    <bool name="profile_supported_pan">true</bool>
    <bool name="profile_supported_pbap">true</bool>
    <bool name="profile_supported_gatt">true</bool>
    <bool name="pbap_include_photos_in_vcard">true</bool>
    <bool name="pbap_use_profile_for_owner_vcard">true</bool>
    <bool name="profile_supported_map">true</bool>
    <bool name="profile_supported_avrcp_target">true</bool>
    <bool name="profile_supported_avrcp_controller">false</bool>
    <bool name="profile_supported_sap">false</bool>
    <bool name="profile_supported_pbapclient">false</bool>
    <bool name="profile_supported_mapmce">false</bool>
    <bool name="profile_supported_hid_device">true</bool>
    <bool name="profile_supported_hearing_aid">false</bool>

setAllProfileServiceStates()就是以STATE_ON的状态启动所有这些supported的Profile,它们也都是ProfileService的子类:

    private void setAllProfileServiceStates(Class[] services, int state) {
        for (Class service : services) {
            if (GattService.class.getSimpleName().equals(service.getSimpleName())) {
                continue;
            }
            setProfileServiceState(service, state);
        }
    }

所以也跟GattService启动的flow类似,AdapterService会处理这些Profile的状态变化,这里是STATE_ON:

        private void processProfileServiceStateChanged(ProfileService profile, int state) {
            switch (state) {
                case BluetoothAdapter.STATE_ON://此时是启动GattService,所以是STATE_ON
                    if (!mRegisteredProfiles.contains(profile)) {
                        Log.e(TAG, profile.getName() + " not registered (STATE_ON).");
                        return;
                    }
                    if (mRunningProfiles.contains(profile)) {//防止重复启动某个Profile
                        Log.e(TAG, profile.getName() + " already running.");
                        return;
                    }
                    mRunningProfiles.add(profile);
                    if (GattService.class.getSimpleName().equals(profile.getName())) {
                        enableNativeWithGuestFlag();//如果是启动Gatt profile,则向stack下发enable bt的指令
                    } else if (mRegisteredProfiles.size() == Config.getSupportedProfiles().length
                            && mRegisteredProfiles.size() == mRunningProfiles.size()) {//这里是启动Gatt以外的,在config.xml中声明支持的其他Profile;相当于启动BREDR mode
                        mAdapterProperties.onBluetoothReady();//Profile全部启动完成,初始化变化,并设置当前Bt工作的mode
                        updateUuids();//更新UUID
                        setBluetoothClassFromConfig();
                        mAdapterStateMachine.sendMessage(AdapterState.BREDR_STARTED);//通知AdapterState,随即进入OnState;BT启动成功
                    }
                    break;
......
}

mRegisteredProfiles、mRunningProfiles最终会跟config声明的Profile一致,然后继续做一些设置工作,其中我们可能比较关心的就是设置当前蓝牙的工作模式了:

    void onBluetoothReady() {
        debugLog("onBluetoothReady, state=" + BluetoothAdapter.nameForState(getState())
                + ", ScanMode=" + mScanMode);

        synchronized (mObject) {
            // Reset adapter and profile connection states
            setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
            mProfileConnectionState.clear();
            mProfilesConnected = 0;
            mProfilesConnecting = 0;
            mProfilesDisconnecting = 0;
            // adapterPropertyChangedCallback has already been received.  Set the scan mode.
            setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);//当前设备可以扫描和连接其他设备,其他设备无法扫描到当前设备
            // This keeps NV up-to date on first-boot after flash.
            setDiscoverableTimeout(mDiscoverableTimeout);//设置扫描的超时时长
        }
    }

我们可以在这里设置希望的蓝牙工作模式和扫描超时时长,这样蓝牙的启动工作就基本结束了。

【蓝牙】BluetoothAdapter详解 BluetoothAdapter简介 BluetoothAdapter代表本地蓝牙设备适配器。BluetoothAdapter可以让我们执行基本的蓝牙任务,如发现蓝牙,查询蓝牙已配对列表,通过已知的MAC地址初始化一个BluetoothDevice实例,并且创建一个BluetoothServerSocket来监听其它设备的请求,启动蓝牙低功耗设备扫描等。 要获取代表本地蓝牙适配器Bluetoot... 阅读详情

相关推荐

Android Bluetooth | 蓝牙配对源码分析

基于Android10源码的蓝牙建立配对流程分析,文章较长~

ezsxrtghjmk的博客 4625

Android 蓝牙启动流程(以及设置蓝牙为作为sink模式 & 接收端模式)

本文以Android 7.1为基础 最近在做蓝牙开发,研究了一下蓝牙的启动流程,总结一下 Google在Android源码中推出了它和博通公司一起开发的BlueDroid以替代BlueZ。BlueZ的创始者,高通公司也将在基于其芯片的Android参考设计中去除BlueZ,支持BlueDroid。相比BlueZ,BlueDroid最值得称道的地方就是其框架结构变得更为简洁和清晰。对我们工程师来说这也是个不错的福利,清晰、简洁的架构使我们在debug过程中思路更清晰; 蓝牙的总体流程图 从流程图我们可

Jason_Lee155的博客 6738

【蓝牙】Android 13 蓝牙源码分析

Android 13在蓝牙模块中进行了多项改进和优化。本文将详细分析其核心组件及其工作原理,包括、蓝牙连接管理和JNI接口。

u010870679的博客 2111

android-adapterservice

enable –打开蓝牙 public boolean enable() { android.util.SeempLog.record(56); if (isEnabled()) { if (DBG) Log.d(TAG, "enable(): BT already enabled!"); return tr...

nilingxi的专栏 1548

车载Android开发-蓝牙服务的启动流程

这里判定的是mHandler中是否存在MESSAGE_TIMEOUT_BIND这个消息,全局搜索后发现只有在调用了bindToAdapter这个方法之后才会发送这个消息,所以此时这个函数返回的肯定是false,那么就会进入到bindToAdapter函数里面。这里我只贴出和蓝牙服务相关的代码。既然在BluetoothService的onBootPhase方法中中用到了BMS,那么BMS中也一定做了相关的事情,果然在BMS的代码中有这样一个函数,注释上说明了在phase变成 SystemService.

其实我是一个演员 1377

【MTK平台】蓝牙autoConnect过程和相关案例问题

在已经连接蓝牙耳机的过程中,断开手机端蓝牙,在重新打开蓝牙的过程二 Log分析以下log是关键的log连接过程以下代码可以知道,在wifi打开的似乎会判断mostRecentlyActiveA2dpDevice存储的device是否为NULL,如果不为NULL,会进行连接autoConnect headSet和A2dp的过程//判断蓝牙是否打开581 return;590 return;598 }

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 1278

Android 上层蓝牙enable流程

Android 蓝牙框架 基于Android9.0 Amlogic代码分析, 蓝牙应用通过 Binder 与蓝牙进程进行通信。蓝牙进程使用 JNI 与蓝牙堆栈通信,并向开发者提供对各种蓝牙配置文件的访问权限。下图显示了蓝牙堆栈的常规结构: 应用框架 client: frameworks/base/core/java/android/bluetooth/* servie: framework/base/services/core/java/com/android/server/BluetoothServic

yizhongliu的专栏 991

蓝牙enable的流程分析

本文主要基于高通Android 12 来分析蓝牙enable的流程 framework 流程不做分析,直接从packages\apps\Bluetooth进行分析 packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java private void processProfileServiceStateChanged(ProfileService profile, int state)

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 2624

Android14 蓝牙启动流程

基于Android14 分析了一下蓝牙的开启流程,本来以为比较简单的,但是发现具体处理会有点绕。主要是从应用上层到cpp层蓝牙的开启过程代码进行的一定的分析。BluetoothAdapter、BluetoothManagerService、AdapterService 是蓝牙开启最相关的上层代码。这个对蓝牙开启失败、或者蓝牙其他流程有问题有一定的分析研究价值,有助于确定是哪个流程出现了问题。这个介绍比较简单一点,没有到后面 AdapterService 的代码。

wenzhi的博客 3877

Android 12 Bluetooth Open[2]

Android 12 Bluetooth Open[2] 1.Java 调用 1.1 处理BLE_TURN_ON消息 1.2 AdapterService.bringUpBle 1.2.1 Config.init 1.2.2 mRemoteDevices.reset 1.2.3 mAdapterProperties.init 1.2.4 BondStateMachine.make 1.2.5 mJniCallbacks.init 1.2.6 setProfileServiceState 1.3 GattSe

无线互联&网络介绍 1189

别再死磕MuJoCo了!用PyBullet+Stable-Baselines3搞定机械臂强化学习(保姆级环境配置)

本文详细介绍了如何使用PyBullet和Stable-Baselines3构建机械臂强化学习系统,绕过昂贵的MuJoCo许可壁垒。通过实战指南,包括环境配置、机械臂Gym环境构建、训练策略与调优技巧,帮助开发者快速掌握机器人强化学习技术,提升智能控制效率。

weixin_42667269的博客 264

Android 蓝牙服务启动

Android 蓝牙服务启动

薛文旺 1957

解码运动想象:BCI Competition IV 2a数据集实战指南

本文提供了BCI Competition IV 2a数据集的实战指南,详细解析了该经典运动想象脑电数据集的结构、文件格式与实验范式。通过MNE-Python等工具,逐步演示了从原始信号读取、预处理(通道选择、滤波、分段)到特征提取(CSP、频带功率)和模型训练(LDA、SVM)的完整流程,并强调了受试者独立评估的重要性,为BCI研究者提供了可复现的入门与进阶路径。

weixin_29304985的博客 362

Android Bluetooth蓝牙init过程

Android BlueDroid蓝牙init过程 Android Bluetooth框架 enable的追踪从settings app开始,一直到最底层的driver,基本按照上图的框架,把函数的调用过程代入图中有助于理解整个蓝牙的架构。 源码追踪 以下为Bluetooth的enable从settings app开始往下追踪的过程,当然仅为调用过程。 1.BluetoothEnabler.java(packages\apps\settings\src\com\android\settings\bluet

生命不息,奋斗不止! 2600

医学图像分割评价指标详解:从Dice到MSD的实战解析

本文深入解析医学图像分割的核心评价指标,从最常用的Dice相似系数到衡量边界精度的MSD(最大对称表面距离),通过实战案例与代码演示,系统阐述各指标的计算原理、应用场景及临床意义,帮助读者全面评估模型性能。

weixin_29070721的博客 593

代付系统/代付系统源码/支付宝代付系统/API代付系统

简介: 其他站买的,没有做测试,源码是JAVA的,附带了安装文档,需要的拿无售后 源码下载地址https://www.skpan.cn/2QnIARSZDxG 代付系统/代付系统源码/支付宝代付系统/API代付系统 系统流程: 后台开通代理——开通商户——商户做代付(后台手工提交和 api 提交两种方式) 1、纯代付系统 2、支持个人支付宝全自动 3、支持单笔/批量/API接口方式代付 4、支持白名单、谷歌验证 5、收付款模式采用自动回调/自动转账 一、运营平台 商户管.

刀客源码网 5575

Android -- Bluetooth framework启动过程简析

Android -- Bluetooth framework启动过程简析 蓝牙是Android设备中非常常见的一个feature,设备厂家可以用BT来做RC、连接音箱、设备本身做Sink等常见功能。如果一些设备不需要BT功能,Android也可以通过配置来disable此模块,方便厂家为自己的设备做客制化。APP操作设备的蓝牙功能,一...

To be or not to be, it's a question 3465

嵌入式开发环境Vscode开发STM32单片机程序

STM32单片机非常强大,大多数教程都是使用 keil 编译器,keil 是收费的而 gcc 是开源免费的。这里介绍一些使用 gcc +开发单片机程序的经验。(这里不解释 gcc 是什么)。

嵌入式技术开发 1843

安卓蓝牙启动(第一篇)

了解一下android的蓝牙 setting app(按钮操作) ->framkwork (binder)->bluetooth appservice (JNI_OnLoad)-> native (dlopen)->default.so(dlopen) -> vendor.so -> dev 整个过程的核心在default.so,蓝牙协...

副会长,今天你有空么 2337

使用OpenGL加载和显示DXF格式的3D模型文件编程

这样,我们就完成了使用OpenGL加载和显示DXF格式的3D模型文件的编程。通过实现加载函数和绘制回调函数,我们能够读取DXF文件的数据并使用OpenGL绘制出来。请注意,这只是一个简单的例子,仅涵盖了基本的加载和显示功能。在本文中,我们将探讨如何使用OpenGL编程加载和显示DXF(Drawing Exchange Format)格式的3D模型文件。DXF是一种通用的CAD(计算机辅助设计)文件格式,常用于存储和交换2D和3D图形数据。使用OpenGL加载和显示DXF格式的3D模型文件编程。

2301_79331421的博客 412

12、多端神经形态晶体管:原理、应用与算法探索

本文综述了多端神经形态晶体管的原理、应用与算法探索。从神经元MOS晶体管的发展到基于电解质栅极晶体管(EGT)的新型器件设计,系统阐述了其在低电压、多功能逻辑运算和突触行为模拟方面的优势。重点介绍了其在化学生物传感器(如多巴胺和pH检测)中的高灵敏度应用,以及在树突整合、时空总和、动态逻辑和神经元算术等树突算法中的实现。文章还总结了该类器件在神经形态计算、智能传感等领域的广阔前景,并分析了当前面临的材料工艺、理论建模与系统集成挑战,展望了未来发展方向。

delta的博客 143

Android蓝牙启动流程分析

本文基于Android9.0源码,文中代码分布位于framework/base/services/core/java/com/android/server frameworks/base/core/java/android/bluetooth(平时我们调用的api代码就位于这里) package/app/bluetooth(系统中的蓝牙app,这里是最终实现蓝牙协议的地方) system/bt/binder/bluetooth(这里定义了整个蓝牙服务中使用到的AIDL) 启动蓝牙我们需要调用framewor

拥抱世界 2077

YOLOv11改进策略【Neck】| 适用关键点检测:PSFM,深层语义融合模块 引入深层与浅层交叉注意力机制,动态建模不同层级的全局语义依赖关系

本文记录的是利用PSFM 模块改进 YOLOv11 的颈部融合部分,专门用于关键点检测。PSFM模块(Profound Semantic Fusion Module,深层语义融合模块) 通过在特征提取网络的深层引入跨模态交叉注意力机制,动态建模红外与可见光特征的全局语义依赖关系。本文将其应用到的颈部部分,融合深层与浅层特征,捕捉长距离语义关联,增强融合特征的判别性与场景理解能力,为检测头提供包含全局上下文的高层语义表示,从而提升模型在复杂场景下的目标检测准确率与语义推理鲁棒性。Rethinking the

Limiiiing的博客 649

安卓系统中蓝牙服务层的初始化流程分析

安卓系统中蓝牙服务层的初始化流程分析 蓝牙系统依赖于安卓,从上到下,层次分明。本篇文章我们就来简单分析下蓝牙服务层初始化的相关流程。 首先来了解下安卓系统中的蓝牙模块大致分为以下几部分: 对安卓系统启动时序有大概了解的小伙伴想必都知道蓝牙服务的管理对象BluetoothManagerService会在系统SystemServer启动过程中被初始化,处于系统进程中运行,然后在系统启动的不同阶段执...

坏小子-无线通信技术 2460

哈工大 哈尔滨工业大学计算机软件复试面试常问问题.rar

哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题哈工大 哈尔滨工业大学计算机软件复试面试常问问题

DehazeNet去雾算法

完成毕业设计时写的代码,可以正常运行,附带结果图和输入原图,可以直接使用运行前先安装requirements.txt中需要的库运行muliple_test_inference.py 文件vis_results中是我得到的去雾结果

android 蓝牙driver的启动流程

android 蓝牙driver的启动流程

lijunXie的博客 1307

中国行政区域(省、市、区县三级)划分数据集(4列,3408条记录)XLS

中国行政区域(省、市、区/县三级)划分。字段说明:id、parent_id(父级ID)、region_name、region_type

Android Bluetooth蓝牙scan过程

Android Bluetooth框架 一、Settings界面下scan 1. 打开蓝牙时自动扫描 进入到android设置界面并打开蓝牙,扫描会自动开始。 下面我们从android蓝牙设置界面开始分析: 蓝牙开关按钮 BluetoothSettings.java (packages\apps\settings\src\com\android\settings\bluetooth) public final class BluetoothSettings extends DeviceListPrefer

生命不息,奋斗不止! 6282

全球-全国港口矢量数据集shp数据下载

全球-全国港口矢量数据集shp数据下载该数据详细记录了各港口的地理位置(经纬度、半球、所属国家和区域)、基础设施条件(航道水深、码头类型、装卸能力)、航运条件(潮汐影响、引航需求、锚地情况)以及服务设施(供水、供油、维修能力等)等70余项属性字段,系统、全面地反映了港口的功能类型与运营能力。

上一篇: Android -- Airplane Mode对Bluetooth启动的影响
下一篇: Android -- Bootanimation是如何触发退出的
第一序列丶
博客等级 码龄11年 249粉丝 77原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值