Android Bluetooth蓝牙init过程

Android Bluetooth蓝牙init过程

Android Bluetooth框架
在这里插入图片描述
init的过程从AdapterService开始,下面我们就从AdapterService开始分析。

一、AdapterService的创建

在蓝牙enable的时候,BluetoothManagerService在其中enable方法会启动一个service,这个service就是AdapterService。

class BluetoothManagerService extends IBluetoothManager.Stub
|enable()
     |sendEnableMsg(false);
        |mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,0, 0));

在他的内部类BluetoothHandler继承了Handler类,处理传递的消息:

|handleMessage(Message msg)
     |case MESSAGE_ENABLE   
        |handleEnable(msg.arg1 == 1); // msg.arg1 = 0
            |if ((mBluetooth == null) && (!mBinding))
                |Intent i = new Intent(IBluetooth.class.getName());
                |doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);

启动AdapterService服务

|doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
        |mContext.bindServiceAsUser(intent, conn, flags, user);

// 启动AdapterService服务后会回调mConnection的onServiceConnected()方法
| onServiceConnected(ComponentName className, IBinder service) 
    | Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
    | if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) 
        |msg.arg1 = SERVICE_IBLUETOOTH;
    |else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) 
       |msg.arg1 = SERVICE_IBLUETOOTHGATT;
    |msg.obj = service;
    |mHandler.sendMessage(msg);

发出的MESSAGE_BLUETOOTH_SERVICE_CONNECTED消息在handleMessage处理,继续打开蓝牙,此处不做描述。

二、init过程源码跟踪

1.AdapterService.java(packages\apps\bluetooth\src\com\android\bluetooth\btservice)
1)classInitNative

static {
    classInitNative();
}

2)InitNative

public void onCreate
	initNative();

查看packages/apps/Bluetooth下的Android.mk文件

LOCAL_PACKAGE_NAME := Bluetooth
LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
LOCAL_REQUIRED_MODULES := bluetooth.default

可知它加载了动态链接库libbluetooth_jni.so,也就意味着InitNative和enableNative函数的具体实现打包在这个动态链接库中。
进到目录packages/apps/bluetooth/jni目录,查看面的Android.mk,其中有打包成库的名称

LOCAL_MODULE := libbluetooth_jni

正好是我们要找的动态链接库,我们要找的函数就是在这个目录下了。
mk文件中也提供了编译的时候包含的源文件:

LOCAL_SRC_FILES:= \
    com_android_bluetooth_btservice_AdapterService.cpp \
    com_android_bluetooth_btservice_QAdapterService.cpp \
    com_android_bluetooth_hfp.cpp \
    com_android_bluetooth_hfpclient.cpp \
    com_android_bluetooth_a2dp.cpp \
    com_android_bluetooth_a2dp_sink.cpp \
    com_android_bluetooth_avrcp.cpp \
    com_android_bluetooth_avrcp_controller.cpp \
    com_android_bluetooth_hid.cpp \
    com_android_bluetooth_hidd.cpp \
    com_android_bluetooth_hdp.cpp \
    com_android_bluetooth_pan.cpp \
    com_android_bluetooth_gatt.cpp \
    android_hardware_wipower.cpp

JNI函数最终都是通过jniRegisterNativeMethods完成注册,将类名和Native函数对应起来。
进入packages/apps/Bluetooth/jni目录搜索:

$ grep "com/android/bluetooth/btservice/AdapterService" * -nR 
com_android_bluetooth_btservice_AdapterService.cpp:1272:    return jniRegisterNativeMethods(env, "com/android/bluetooth/btservice/AdapterService",

发现类AdapterService对应的JNI函数在com_android_bluetooth_btservice_AdapterService.cpp注册。。

2.com_android_bluetooth_btservice_AdapterService.cpp (packages\apps\bluetooth\jni):
1)classInitNative

static void classInitNative(JNIEnv* env, jclass clazz) {
    int err;
    hw_module_t* module;

    jclass jniCallbackClass =
        env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
    sJniCallbacksField = env->GetFieldID(clazz, "mJniCallbacks",
        "Lcom/android/bluetooth/btservice/JniCallbacks;");

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

    char value[PROPERTY_VALUE_MAX];
    property_get("bluetooth.mock_stack", value, "");
    const char *id = (strcmp(value, "1")? BT_STACK_MODULE_ID : BT_STACK_TEST_MODULE_ID);

    err = hw_get_module(id, (hw_module_t const**)&module);
    hw_device_t* abstraction;
    err = module->methods->open(module, id, &abstraction);
    if (err == 0) {
        bluetooth_module_t* btStack = (bluetooth_module_t *)abstraction;
        sBluetoothInterface = btStack->get_bluetooth_interface();
}

classInitNative主要做两件事:

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

2)initNative

static bool initNative(JNIEnv* env, jobject obj) {
    if (sBluetoothInterface) {
        int ret = sBluetoothInterface->init(&sBluetoothCallbacks);
        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);
        if (ret != BT_STATUS_SUCCESS) {
            ALOGE("Error while setting Bluetooth callouts: %d\n", ret);
            sBluetoothInterface->cleanup();
            sBluetoothInterface = NULL;
            return JNI_FALSE;
        }

        if ( (sBluetoothSocketInterface = (btsock_interface_t *)
                  sBluetoothInterface->get_profile_interface(BT_PROFILE_SOCKETS_ID)) == NULL) {
                ALOGE("Error getting socket interface");
        }
        return JNI_TRUE;
    }

其中的sBluetoothInterface在classInitNative中初始化,实际操作的是Bluetooth.c里面的bluetoothInterface。

3.bluetooth.c (external\bluetooth\bluedroid\btif\src)

|static const bt_interface_t bluetoothInterface
      |init
        |btif_init_bluetooth()

4.btif_core.c (external\bluetooth\bluedroid\btif\src)

btif_init_bluetooth(void)
bt_status_t btif_init_bluetooth()
    btif_config_init();/初始化初始化/data/misc/bluedroid/bt_config.xml中相关数据
    bte_main_boot_entry();
    	GKI_init(); // gki init
    	bte_main_in_hw_init(); 
    		static bt_hc_interface_t *bt_hc_if=NULL;
    		bt_hc_if = (bt_hc_interface_t *) bt_hc_get_interface() // bt_hci_bdroid.c中的 bluetoothHCLibInterface
    	bte_load_conf(BTE_STACK_CONF_FILE); //加载配置文件/etc/bluetooth/bt_stack.conf
#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
    	bte_load_ble_conf(BTE_BLE_STACK_CONF_FILE);  //加载配置文件 etc/bluetooth/ble_stack.conf
#endif
    memset(&btif_local_bd_addr, 0, sizeof(bt_bdaddr_t));
    btif_fetch_local_bdaddr(&btif_local_bd_addr);//获取本地蓝牙地址

    /* 创建 btif task */
    status = GKI_create_task(btif_task, BTIF_TASK, BTIF_TASK_STR,
                (UINT16 *) ((UINT8 *)btif_task_stack + BTIF_TASK_STACK_SIZE),
                sizeof(btif_task_stack));

5.btif_core.c (external\bluetooth\bluedroid\btif\src)
BTIF task处理程序,管理通过蓝牙HAL和BTA传递的所有消息。

static void btif_task(UINT32 params)
{
    UINT16   event;
    BT_HDR   *p_msg;
    for(;;)
    {
        event = GKI_wait(0xFFFF, 0);
        // bt enable时回创建btu_task, btu_task线程运行起来后会发送
        // BT_EVT_TRIGGER_STACK_INIT消息给btif_task,告知蓝牙初始化完成
        if (event == BT_EVT_TRIGGER_STACK_INIT)
        {
            btif_dm_load_ble_local_keys();// 加载bt keys
            BTA_EnableBluetooth(bte_dm_evt); //Enables bluetooth service
        }

        if (event == BT_EVT_HARDWARE_INIT_FAIL)
            bte_main_disable();
            btif_queue_release();
            GKI_task_self_cleanup(BTIF_TASK);
            bte_main_shutdown();
            break;
        }

        if(event & TASK_MBOX_1_EVT_MASK)
        {
            while((p_msg = GKI_read_mbox(BTU_BTIF_MBOX)) != NULL)
            {
                switch (p_msg->event)
                {
                    case BT_EVT_CONTEXT_SWITCH_EVT:
                        btif_context_switched(p_msg);
                        break;
                }
                GKI_freebuf(p_msg);
            }
        }
    }
}

下面简单看一下BTA_EnableBluetooth函数
bta_dm_api.c (external\bluetooth\bluedroid\bta\dm)

/* Calculate start of event enumeration; id is top 8 bits of event */
#define BTA_SYS_EVT_START(id)       ((id) << 8)
#define BTA_ID_DM           1            /* device manager */
BTA_DM_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_DM),
static const tBTA_SYS_REG bta_dm_reg =
{
    bta_dm_sm_execute,
    bta_dm_sm_disable
};
tBTA_STATUS BTA_EnableBluetooth(tBTA_DM_SEC_CBACK *p_cback)
{
    tBTA_DM_API_ENABLE    *p_msg;
    memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));

    bta_sys_register (BTA_ID_DM, &bta_dm_reg );
    bta_sys_register (BTA_ID_DM_SEARCH, &bta_dm_search_reg );
		bta_sys_cb.reg[id] = (tBTA_SYS_REG *) p_reg;
		bta_sys_cb.is_reg[id] = TRUE;
    if ((p_msg = (tBTA_DM_API_ENABLE *) GKI_getbuf(sizeof(tBTA_DM_API_ENABLE))) != NULL)
    {
        p_msg->hdr.event = BTA_DM_API_ENABLE_EVT; 
        p_msg->p_sec_cback = p_cback;
        bta_sys_sendmsg(p_msg); //最终调用bta_dm_enable
        	GKI_send_msg(bta_sys_cb.task_id, p_bta_sys_cfg->mbox, p_msg);
        return BTA_SUCCESS;
    }
    return BTA_FAILURE;
}

代码处理文件流程:

--------------------------------------------bluetooth apk------------------------------------------
1.AdapterService.java(packages\apps\bluetooth\src\com\android\bluetooth\btservice)
--------------------------------------------jni--------------------------------------------
2.com_android_bluetooth_btservice_AdapterService.cpp (packages\apps\bluetooth\jni)
--------------------------------------------bluetooth.default.so------------------------------------------
3.bluetooth.c (external\bluetooth\bluedroid\btif\src)
4.btif_core.c (external\bluetooth\bluedroid\btif\src)
延伸:

bta_sys_sendmsg(p_msg)消息跟踪: 根据event id为BTA_DM_API_ENABLE_EVT的消息一步步跟踪到最终要调用的函数bta_dm_enable。

#define EVENT_MASK(evt)       ((UINT16)(0x0001 << (evt)))
void bta_sys_sendmsg(void *p_msg)
{
    GKI_send_msg(bta_sys_cb.task_id, p_bta_sys_cfg->mbox, p_msg);
    	GKI_send_event(task_id, (UINT16)EVENT_MASK(mbox));
}

查看bta_sys_cb.task_id的赋值:

BTA_API void bta_sys_init(void)
    bta_sys_cb.task_id = GKI_get_taskid();

bta_sys_init在btu_task中被调用,因此task_id 等于btu_task的task id,消息在btu_task中接收处理。

查看p_bta_sys_cfg->mbox的赋值:

// gki.h (external\bluetooth\bluedroid\gki\common)
/************************************************************************
** Mailbox definitions. Each task has 4 mailboxes that are used to
** send buffers to the task.
*/
#define TASK_MBOX_0    0
#define TASK_MBOX_1    1
#define TASK_MBOX_2    2
#define TASK_MBOX_3    3

#define NUM_TASK_MBOX  4

/************************************************************************
** Event definitions.
**
** There are 4 reserved events used to signal messages rcvd in task mailboxes.
** There are 4 reserved events used to signal timeout events.
** There are 8 general purpose events available for applications.
*/
#define MAX_EVENTS              16

#define TASK_MBOX_0_EVT_MASK   0x0001
#define TASK_MBOX_1_EVT_MASK   0x0002
#define TASK_MBOX_2_EVT_MASK   0x0004
#define TASK_MBOX_3_EVT_MASK   0x0008

//bta_sys.h (external\bluetooth\bluedroid\bta\sys)
/* system manager configuration structure */
typedef struct
{
    UINT16          mbox_evt;                       /* GKI mailbox event */
    UINT8           mbox;                           /* GKI mailbox id */
    UINT8           timer;                          /* GKI timer id */
    UINT8           trace_level;                    /* initial trace level */
} tBTA_SYS_CFG;

// bta_sys_cfg.c (external\bluetooth\bluedroid\bta\sys)	
/* GKI task mailbox event for BTA. */
#define BTA_MBOX_EVT                TASK_MBOX_2_EVT_MASK

/* GKI task mailbox for BTA. */
#define BTA_MBOX                    TASK_MBOX_2

const tBTA_SYS_CFG bta_sys_cfg =
{
    BTA_MBOX_EVT,               /* GKI mailbox event */
    BTA_MBOX,                   /* GKI mailbox id */
    BTA_TIMER,                  /* GKI timer id */
    APPL_INITIAL_TRACE_LEVEL    /* initial trace level */
};

tBTA_SYS_CFG *p_bta_sys_cfg = (tBTA_SYS_CFG *)&bta_sys_cfg;

所以p_bta_sys_cfg->mbox等于TASK_MBOX_2, EVENT_MASK(mbox)则等于TASK_MBOX_2_EVT_MASK。

btu_task.c (external\bluetooth\bluedroid\stack\btu)


/*******************************************************************************
**
** Function         btu_task
**
** Description      This is the main task of the Bluetooth Upper Layers unit.
**                  It sits in a loop waiting for messages, and dispatches them
**                  to the appropiate handlers.
**
** Returns          should never return
**
*******************************************************************************/
BTU_API UINT32 btu_task (UINT32 param)
{
    UINT16           event;
    BT_HDR          *p_msg;
    UINT8            i;
    UINT16           mask;
    BOOLEAN          handled;

    /* Initialize the mandatory core stack control blocks
       (BTU, BTM, L2CAP, and SDP)
     */
    btu_init_core();

    /* Initialize any optional stack components */
    BTE_InitStack();

#if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE)
    bta_sys_init(); // bta init
#endif

    /* Send a startup evt message to BTIF_TASK to kickstart the init procedure */
    GKI_send_event(BTIF_TASK, BT_EVT_TRIGGER_STACK_INIT);

    /* Wait for, and process, events */
    for (;;)
    {
        event = GKI_wait (0xFFFF, 0);
        if (event & TASK_MBOX_0_EVT_MASK)
        {
            /* Process all messages in the queue */
        }
        if (event & TIMER_0_EVT_MASK) {

        }

#if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE)
        if (event & TASK_MBOX_2_EVT_MASK)
        {
            while ((p_msg = (BT_HDR *) GKI_read_mbox(TASK_MBOX_2)) != NULL)
            {
                bta_sys_event(p_msg);
					UINT8       id;
					BOOLEAN     freebuf = TRUE;	
					//event等于BTA_DM_API_ENABLE_EVT,id等于BTA_ID_DM,对应的cb函数bta_dm_reg(在BTA_EnableBluetooth注册)
					id = (UINT8) (p_msg->event >> 8);
					//调用bta_dm_reg的bta_dm_sm_execute函数
					freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);

        if (event & TIMER_1_EVT_MASK)
        {
            bta_sys_timer_update();
        }
#endif
        if (event & TIMER_3_EVT_MASK) {

        }
        if (event & EVENT_MASK(APPL_EVT_7))
            break;
    }

    return(0);
}

bta_dm_pm.c (external\bluetooth\bluedroid\bta\dm)

/* action function list */
const tBTA_DM_ACTION bta_dm_action[] =
{

    /* device manager local device API events */
    bta_dm_enable,            /* 0  BTA_DM_API_ENABLE_EVT */
    bta_dm_disable,           /* 1  BTA_DM_API_DISABLE_EVT */
    bta_dm_set_dev_name,      /* 2  BTA_DM_API_SET_NAME_EVT */
    bta_dm_set_visibility,    /* 3  BTA_DM_API_SET_VISIBILITY_EVT */
    bta_dm_set_afhchannels,   /* 4  BTA_DM_API_SET_AFH_CHANNELS_EVT */
    bta_dm_signal_strength,   /* 5  BTA_API_DM_SIG_STRENGTH_EVT */
    bta_dm_vendor_spec_command,/* 6  BTA_DM_API_VENDOR_SPECIFIC_COMMAND_EVT */
    bta_dm_tx_inqpower,       /* 7  BTA_DM_API_SIG_STRENGTH_EVT */
    bta_dm_acl_change,        /* 8  BTA_DM_ACL_CHANGE_EVT */
    bta_dm_add_device,        /* 9  BTA_DM_API_ADD_DEVICE_EVT */
    bta_dm_close_acl,         /* 10 BTA_DM_API_ADD_DEVICE_EVT */

    /* security API events */
    bta_dm_bond,              /* 11  BTA_DM_API_BOND_EVT */
    bta_dm_bond_cancel,       /* 12  BTA_DM_API_BOND_CANCEL_EVT */
    bta_dm_pin_reply,         /* 13 BTA_DM_API_PIN_REPLY_EVT */
    bta_dm_link_policy,       /* 14 BTA_DM_API_LINK_POLICY_EVT */
    bta_dm_auth_reply,        /* 15 BTA_DM_API_AUTH_REPLY_EVT */

    /* power manger events */
    bta_dm_pm_btm_status,     /* 16 BTA_DM_PM_BTM_STATUS_EVT */
    bta_dm_pm_timer,          /* 17 BTA_DM_PM_TIMER_EVT*/

    /* simple pairing events */
    bta_dm_confirm,           /* 18 BTA_DM_API_CONFIRM_EVT */

    bta_dm_set_encryption,    /* BTA_DM_API_SET_ENCRYPTION_EVT */

#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
    bta_dm_passkey_cancel,    /* 19 BTA_DM_API_PASKY_CANCEL_EVT */
#endif
#if (BTM_OOB_INCLUDED == TRUE)
    bta_dm_loc_oob,           /* 20 BTA_DM_API_LOC_OOB_EVT */
    bta_dm_ci_io_req_act,     /* 21 BTA_DM_CI_IO_REQ_EVT */
    bta_dm_ci_rmt_oob_act,    /* 22 BTA_DM_CI_RMT_OOB_EVT */
#endif /* BTM_OOB_INCLUDED */

    bta_dm_remove_device,      /*  BTA_DM_API_REMOVE_DEVICE_EVT */

#if BLE_INCLUDED == TRUE
    bta_dm_add_blekey,          /*  BTA_DM_API_ADD_BLEKEY_EVT           */
    bta_dm_add_ble_device,      /*  BTA_DM_API_ADD_BLEDEVICE_EVT        */
    bta_dm_ble_passkey_reply,   /*  BTA_DM_API_BLE_PASSKEY_REPLY_EVT    */
    bta_dm_security_grant,
    bta_dm_ble_set_bg_conn_type,
    bta_dm_ble_set_conn_params,      /* BTA_DM_API_BLE_CONN_PARAM_EVT */
    bta_dm_ble_set_scan_params,      /* BTA_DM_API_BLE_SCAN_PARAM_EVT */
    bta_dm_ble_observe,
    bta_dm_ble_update_conn_params,   /* BTA_DM_API_UPDATE_CONN_PARAM_EVT */
#if BLE_PRIVACY_SPT == TRUE
    bta_dm_ble_config_local_privacy,   /* BTA_DM_API_LOCAL_PRIVACY_EVT */
#endif
    bta_dm_ble_set_adv_params,     /* BTA_DM_API_BLE_SCAN_PARAM_EVT */
    bta_dm_ble_set_adv_config,     /* BTA_DM_API_BLE_SET_ADV_CONFIG_EVT */
    bta_dm_ble_set_scan_rsp,       /* BTA_DM_API_BLE_SET_SCAN_RSP_EVT */
    bta_dm_ble_broadcast,          /* BTA_DM_API_BLE_BROADCAST_EVT */
#if BLE_ANDROID_CONTROLLER_SCAN_FILTER == TRUE
    bta_dm_cfg_filter_cond,         /* BTA_DM_API_CFG_FILTER_COND_EVT */
    bta_dm_scan_filter_param_setup, /* BTA_DM_API_SCAN_FILTER_SETUP_EVT */
    bta_dm_enable_scan_filter,      /* BTA_DM_API_SCAN_FILTER_ENABLE_EVT */
#endif
    bta_dm_ble_multi_adv_enb,           /*  BTA_DM_API_BLE_MULTI_ADV_ENB_EVT*/
    bta_dm_ble_multi_adv_upd_param,     /*  BTA_DM_API_BLE_MULTI_ADV_PARAM_UPD_EVT */
    bta_dm_ble_multi_adv_data,          /*  BTA_DM_API_BLE_MULTI_ADV_DATA_EVT */
    btm_dm_ble_multi_adv_disable,       /*  BTA_DM_API_BLE_MULTI_ADV_DISABLE_EVT */
    bta_dm_ble_setup_storage,      /* BTA_DM_API_BLE_SETUP_STORAGE_EVT */
    bta_dm_ble_enable_batch_scan,  /* BTA_DM_API_BLE_ENABLE_BATCH_SCAN_EVT */
    bta_dm_ble_disable_batch_scan, /* BTA_DM_API_BLE_DISABLE_BATCH_SCAN_EVT */
    bta_dm_ble_read_scan_reports,  /* BTA_DM_API_BLE_READ_SCAN_REPORTS_EVT */
    bta_dm_ble_track_advertiser,   /* BTA_DM_API_BLE_TRACK_ADVERTISER_EVT */
    bta_dm_ble_get_energy_info,    /* BTA_DM_API_BLE_ENERGY_INFO_EVT */
#endif

#if ( BTM_EIR_SERVER_INCLUDED == TRUE )&&( BTA_EIR_CANNED_UUID_LIST != TRUE )&&(BTA_EIR_SERVER_NUM_CUSTOM_UUID > 0)
    bta_dm_update_eir_uuid,     /*  BTA_DM_API_UPDATE_EIR_UUID_EVT      */
#endif
#if (BTM_EIR_SERVER_INCLUDED == TRUE)
    bta_dm_set_eir_config,      /*  BTA_DM_API_SET_EIR_CONFIG_EVT       */
#endif

    bta_dm_enable_test_mode,    /*  BTA_DM_API_ENABLE_TEST_MODE_EVT     */
    bta_dm_disable_test_mode,   /*  BTA_DM_API_DISABLE_TEST_MODE_EVT    */
    bta_dm_execute_callback,     /*  BTA_DM_API_EXECUTE_CBACK_EVT        */
    bta_dm_set_afh_channel_assesment      /* BTA_DM_API_SET_AFH_CHANNEL_ASSESMENT_EVT */
};

BOOLEAN bta_dm_sm_execute(BT_HDR *p_msg)
{
    UINT16  event = p_msg->event & 0x00ff;

    /* execute action functions */
    if(event < BTA_DM_NUM_ACTIONS)
    {
        (*bta_dm_action[event])( (tBTA_DM_MSG*) p_msg); // 调用bta_dm_enable
    }

    return TRUE;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值