bluetooth模块学习总结

Android系统中的bluetooth模块

1. 蓝牙是什么

蓝牙是一种低功耗的无线连接技术,是一种设备间短距离的无线通讯方式,这句话表明蓝牙以下几个特性:

  1. 蓝牙是一种无线通讯方式,即表示该通讯需要有对应的协议支持(蓝牙无线通信协议标准) 。
  2. 蓝牙跨设备使用。
  3. 低耗能技术。
  4. 蓝牙属于短距离通讯方式。

2. 蓝牙有什么

蓝牙技术有两种类型:

  1. Basic Rate/Enhanced Data Rate (BR/EDR)基本速率/增强数据速率即所谓的传统蓝牙技术(蓝牙版本2.0/2.1):仅支持P2P一种通信方式,即1:1设备间通信,具有持续无线连接、优化音频流的特点,所以是蓝牙耳机、蓝牙扬声器等音频传输的理想方案
  2. Low Energy (LE)低功耗即所谓的新型的低功耗蓝牙技术(蓝牙版本4.0/4.1/4.2/4.3)
    所以蓝牙模块可以分为经典蓝牙模块(v1.1/1.2/2.0/2.1/3.0),低功耗蓝牙模块(v4.0/4.1/4.2),以及蓝牙双模模块(支持蓝牙所有版本,兼容低功耗蓝牙及经典蓝牙)。

蓝牙技术4种通信方式:

  1. P2P通信方式(旧版本):1:1设备间通信,具有持续无线连接、优化音频流的特点,所以是蓝牙耳机、蓝牙扬声器等音频传输的理想方案
  2. P2P(point-to-point)(点对点):1:1支持短时间无限连接,优化了数据传输能量消耗,可用于无线键盘、无线鼠标等
  3. broadcast(广播信息):1:m。可以实现本地化信息共享。广播信息顾名思义,一设备广播信息,其他对该信息感兴趣的设备接受该信息并进行处理。比如beacon
  4. mesh(网格):m:m

蓝牙常用协议:

.含义作用举例
OppProfileObject Push Profie文件传输协议:用于蓝牙设备间的文件传输手机间的文件传输
PbapServerProfilePhone Book Access Profile(PSE)读取联系人协议:作为server,本设备的联系人可共享给其他设备提供联系人列表
PbapClientProfilePhone Book Access Profile(PCE)读取联系人协议:作为client角色,本设备可读取server端的联系人读取联系人列表
A2dpProfileAdvanced Audio Distribution Profile(SRC:Source)高级音频分发协议:作为server提供音频源例如可以提供音频源的手机
A2dpSinkProfileAdvanced Audio Distribution Profile(SINK)高级音频分发协议:作为client播放接收到的音频车载蓝牙,蓝牙音响
HeadsetProfileHeadset Profile耳机协议:提供手机音频连接蓝牙耳机
HfpClientProfileHands-Free Profile免提设备:播放音频蓝牙耳机
HidProfileHuman Interface Device人机接口设备蓝牙鼠标,蓝牙键盘
MapProfileMessage Access Profile读取短消息协议
SapProfileSIM Access Profile读取sim卡协议

3. 蓝牙需要改什么

蓝牙与Android关系:

  1. Google推出的各Android系统:所支持的蓝牙协议profile均是开启状态
  2. 芯片提供商(常见的诸如高通、mtk)修改后的Android源码–开发中称之为base代码:新增或者修改某些蓝牙profile
  3. 开发商拿到base代码进行进一步加工:新增或者修改某些profile

蓝牙堆栈的常规结构:
这里写图片描述
* 应用框架:
处于应用框架级别的是应用代码,它利用 android.bluetooth API 与蓝牙硬件进行交互。此代码在内部通过 Binder IPC 机制调用蓝牙进程。

  • 蓝牙系统服务
    蓝牙系统服务(位于 packages/apps/Bluetooth 中)被打包为 Android 应用,并在 Android 框架层实现蓝牙服务和配置文件。该应用通过 JNI 调用 HAL 层。
  • JNI
    与 android.bluetooth 相关联的 JNI 代码位于 packages/apps/Bluetooth/jni 中。当发生特定蓝牙操作时(例如发现设备时),JNI 代码会调用 HAL 层并从 HAL 接收回调。
  • HAL
    硬件抽象层定义了 android.bluetooth API 和蓝牙进程会调用的标准接口,并且您必须实现该接口才能使蓝牙硬件正常工作。蓝牙 HAL 的头文件是 hardware/libhardware/include/hardware/bluetooth.h。另外,请查看所有 hardware/libhardware/include/hardware/bt_*.h 文件。
  • 蓝牙堆栈
    系统为您提供了默认蓝牙堆栈(位于 system/bt 中)。该堆栈会实现常规蓝牙 HAL,并通过扩展程序和更改配置对其进行自定义。
  • 供应商扩展程序
    要添加自定义扩展程序和用于跟踪的 HCI 层,一般可以创建一个 libbt-vendor 模块并指定这些组件。

蓝牙核心架构:
这里写图片描述

蓝牙代码的实现主要包括3个方面:

  1. 界面UI
    1. 设置应用中蓝牙的ui
    2. 蓝牙本身这个系统应用中的ui
  2. 蓝牙开关默认值
  3. 协议配置开关:手机是否要支持各种协议

蓝牙代码分布:

  1. 系统应用设置Settings中的蓝牙相关,包括蓝牙开关,蓝牙扫描,蓝牙配对框,蓝牙重命名框,蓝牙选择框等等。
    这里写图片描述

  2. 系统中有个蓝牙应用Bluetooth,包含蓝牙文件传入传出历史记录,蓝牙配对框,蓝牙文件传输框等等。
    这里写图片描述
    这里写图片描述

  3. 蓝牙协议的具体实现
    这里写图片描述

  4. 集成的一些蓝牙接口
    这里写图片描述

4. 对蓝牙功能添加/修改

开发一个测试工具,主要对bluetooth的Channel进行测试

  1. 通过暗码进行打开这个测试工具
  2. 可以测试不通的Channel

关于这个工具的开发其实系统已经提供了相应的接口,接口的实现都是不用管的,只要将接口层层包装,并在工具apk中调用包装好的接口即可。

4.1 首先,看看系统什么地方定义了该接口:

在文件hardware/libhardware/include/hardware/bluetooth.h中有个如下结构体:

/** 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);

  /*此处省略*/
    /** Get Bluetooth profile interface */
    const void* (*get_profile_interface) (const char *profile_id);

    /** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs */
    /* Configure DUT Mode - Use this mode to enter/exit DUT mode */
    int (*dut_mode_configure)(uint8_t enable);

    /* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */
    int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len);
    /** BLE Test Mode APIs */
    /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */
    int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len);
/*  该结构体中的函数指针dut_mode_configure && le_test_mode就是提供给上层使用的负责蓝牙的开关及基本控制标准接口,本次开发主要就使用这2个接口。*/
} bt_interface_t;

4.2 其次,问题就转换为如何调用这个2接口了。 在 apk中调用c/c++层的接口需要通过JNI层将这2接口个进行包装

  1. 在文件packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp文件中添加对应方法
#define HCI_LE_TRANSMITTER_TEST_OPCODE 0x201E
#define HCI_LE_END_TEST_OPCODE 0x201F
static jint setTestModeNative(JNIEnv *env, jobject object, jint mode) {
    ALOGD("%s setTestModeNative mode = %d",__FUNCTION__, mode);
    #if defined (HAVE_BLUETOOTH) && defined (ENG_MODE)
        ALOGI("%s mode = %d",__FUNCTION__, mode);
        if (!sBluetoothInterface) return -1;
        return sBluetoothInterface->dut_mode_configure(mode);/*调用蓝牙hal层的接口*/
    #endif
        return -1;
}
static jint setBtChannelNative(JNIEnv *env, jobject object, jint position) {
    ALOGD("%s setBtChannelNative position = %d",__FUNCTION__, position);
    #if defined (HAVE_BLUETOOTH) && defined (ENG_MODE)
        ALOGI("%s position = %d",__FUNCTION__, position);
        if (!sBluetoothInterface) return -1;
        unsigned char buf[3];
        memset(buf, 0, sizeof(buf));
        if (position < 0) {
             return sBluetoothInterface->le_test_mode(HCI_LE_END_TEST_OPCODE, buf, 0);
        }
        buf[0] = position; /* tx_channel 0-39*/
        buf[1] = 0x25; /* length of test data 0-37*/
        buf[2] = 0; /* packet payload <9*/
        return sBluetoothInterface->le_test_mode(HCI_LE_TRANSMITTER_TEST_OPCODE, buf, 3);
    #endif
        return -1;
}
static JNINativeMethod sMethods[] = {
    /* name, signature, funcPtr */
    {"setSocketOptNative", "(III[BI)I", (void*) setSocketOptNative}
     /*在数组sMethods中注册新加的2个方法为native方法供java层使用*/
    ,{"setTestModeNative", "(I)I", (void *)setTestModeNative},
    {"setBtChannelNative","(I)I",(void *)setBtChannelNative}
    /**/
};
  1. 将新增的2个方法包装到AdapterService.java服务中,运行不同进程的应用调用。

    a. 在文件frameworks/base/core/java/android/bluetooth/IBluetooth.aidl中添加新方法的调用接口,具体如下:

interface IBluetooth
{
/*此处省略*/
 int setBtTestMode(int mode);
 int setBtChannel(int position);
/*此处省略*/
}

在packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java中实现IBluetooth中的2个接口,这样对于任何持有AdapterService句柄的对象都可以使用新加的2个方法。

import android.bluetooth.IBluetooth;/*导入aidl接口,实现方法跨进程调用*/
public class AdapterService extends Service {
/*此处省略*/
    static {
        System.loadLibrary("bluetooth_jni");/*load jni静态库*/
        classInitNative();/*调用native中的init方法,为结构体bt_interface_t赋值*/
    }
    private native int setTestModeNative(int mode);
    private native int setBtChannelNative(int position);
    /**
     * Handlers for incoming service callsH
     */
    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 {
/*此处省略*/
         public int setBtTestMode(int mode) {
             Log.d(TAG, "setBtTestMode mode " + mode);
             AdapterService service = getService();
             if (service == null) return -1;
             return service.setTestModeNative(mode);
         }
         public int setBtChannel(int position) {
             Log.d(TAG, "setBtChannel position " + position);
             AdapterService service = getService();
             if (service == null) return -1;
             return service.setBtChannelNative(position);
         }
    }
/*此处省略*/
}
  1. 到目前为止,添加的2个方法还处在packages/apps/Bluetooth模块中,继续将新加的方法包装到BluetoothAdapter.java中,方便应用程序调用,具体修改如下。
public final class BluetoothAdapter {
/*此处省略*/
    public int setBtTestMode(int mode) {
        Log.d(TAG, "setBtTestMode mode : " + mode);
        //IBluetooth service = mBluetoothAdapter.mService;
        if (mService == null) {
            return -1;
        }
        try {
            return mService.setBtTestMode(mode);
        } catch (Exception ex) {
            Log.w(TAG, "Unhandled exception: " + ex);
        }
        return -1;
    }

    public int setBtChannel(int position) {
        Log.d(TAG, "setBtChannel position : " + position);
        //IBluetooth service = mBluetoothAdapter.mService;
        if (mService == null) {
            return -1;
        }
        try {
            return mService.setBtChannel(position);
        } catch (Exception ex) {
            Log.w(TAG, "Unhandled exception: " + ex);
        }
        return -1;
    }
   /*此处省略*/
}

4.3 最后,写个测试apk,调用封装好的方法。

package com.android.BluetoothTestMode;
import com.android.BluetoothTestMode.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.content.Context;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Toast;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Spinner;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Handler;
import android.os.Message;

public class BluetoothTestMode extends Activity implements AdapterView.OnItemSelectedListener {
    private Context mContext;
    private BluetoothAdapter mBtAdapter;

    private final String TAG = "BluetoothTestMode";

    private static final int ENABLE_BT_TEST_MODE_DELAY = 3;

    private Button mButton01 = null;
    private Button mButton02 = null;
    private Spinner mSpinner = null;
    private int mBluetoothChannel = 0;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mContext = this;

        // Get the local Bluetooth adapter
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();

        setContentView(R.layout.main);

        mButton01 = (Button) findViewById(R.id.Button01);
        mButton02 = (Button) findViewById(R.id.Button02);
//bluetooth test mode channel setting
        mSpinner = (Spinner) findViewById(R.id.Bluetooth_Channel_Spinner);
        mSpinner.setSelection(0, true);
        mSpinner.setOnItemSelectedListener(this);
        mButton01.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                mButton01.setEnabled(false);
                mButton02.setEnabled(true);
                if (!mBtAdapter.isEnabled()) {
                    mBtAdapter.enable();
                    synchronized(mHandler) {
                        if(mHandler.hasMessages(ENABLE_BT_TEST_MODE_DELAY)){
                            mHandler.removeMessages(ENABLE_BT_TEST_MODE_DELAY);
                        }
                        mHandler.sendEmptyMessageDelayed(ENABLE_BT_TEST_MODE_DELAY, 5000);
                    }
                    Toast.makeText(BluetoothTestMode.this, "BT is turning on, please wait...", Toast.LENGTH_SHORT).show();
                }else {
                    int ret = mBtAdapter.setBtTestMode(1);
                    if (ret == -1) {
                        mButton01.setEnabled(true);
                        mButton02.setEnabled(false);
                        Toast.makeText(BluetoothTestMode.this, "Test mode enable failed", Toast.LENGTH_SHORT).show();
                        }
                    Log.d(TAG, "BT already ON! enableBtTestMode " + ret);
                }
            }
        });

        mButton02.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
                mButton01.setEnabled(true);
                mButton02.setEnabled(false);
                if (mBtAdapter.isEnabled()) {
                    mBtAdapter.setBtChannel(-1);
                    mBtAdapter.setBtTestMode(0);
                    mBtAdapter.disable();
                    Toast.makeText(BluetoothTestMode.this, "BT disabled...", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(BluetoothTestMode.this, "BT already OFF!", Toast.LENGTH_SHORT).show();
                }
            }
        });

    }

    /**
     * Called when the activity will start interacting with the user.
     */
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();

    }

    /**
     * Called when the system is about to start resuming a previous activity.
     */
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();

    }

    /**
     * The final call you receive before your activity is destroyed.
     */
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if (mBtAdapter.isEnabled()) {
            mBtAdapter.setBtChannel(-1);
            mBtAdapter.setBtTestMode(0);
            mBtAdapter.disable();
         }
    }
//bluetooth test mode channel setting
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (parent == mSpinner) {
            mBluetoothChannel = position;
            if (!mBtAdapter.isEnabled()) {
                mBtAdapter.enable();
             }
            int ret = mBtAdapter.setBtChannel(-1);
            Log.d(TAG,"setBtChannel end test " + ret);
            ret = mBtAdapter.setBtChannel(mBluetoothChannel);
            Log.d(TAG,"setBtChannel start test " + ret);
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        //
    }
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == ENABLE_BT_TEST_MODE_DELAY){
                int ret = mBtAdapter.setBtTestMode(1);
                if (ret == -1) {
                    mButton01.setEnabled(true);
                    mButton02.setEnabled(false);
                    Toast.makeText(BluetoothTestMode.this, "Test mode enable failed", Toast.LENGTH_SHORT).show();
                }
                Log.d(TAG, "enableBtTestMode " + ret);
            }
        }
    };
}

参考:带你解锁蓝牙skill

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值