在具体讲述每个模块前我们先来看看一些公共的知识点,如Binder、JNI、Service、AIDL、Broadcast等,它们都是Android/Java基础的知识点,在网络上有许多相关的文章,本文就不深入的讲述它们,但在Bluetooth Framework或者app中都大量的使用了它们,所以本文主要是结合代码做一个回顾,后续文章可能一笔带过,不再花费篇幅讲解,主要集中在具体代码逻辑中。
Service是一种可在后台执行长时间运行的操作的应用组件。不提供界面。启动后,即使用户切换到其他应用,服务也可能会继续运行一段时间。此外,组件可以绑定到服务以与其交互,甚至可以执行进程间通信 (IPC)。例如,服务可以在后台处理网络事务、播放音乐、执行文件 I/O 或与 content provider 交互。
引用自googl开发文档 https://developer.android.com/develop/background-work/services?hl=zh-cn
服务有三种类型:前台服务、后台服务、绑定服务,在Android蓝牙中使用的都是绑定服务,因此另外两种就不做过多介绍,主要看看绑定服务。
实现一个Service,需要实现一个Service
的子类或者使用现有子类,在蓝牙服务中AdapterService
就是它的子类(public class AdapterService extends Service {}
),ProfileService
继承自Service
,而其他所有的Profile如A2DP、GATT、HFP等都是ProfileService
的子类,然后实现一些回调方法:
onStartCommand()
startService()
请求启动服务时会执行该方法,启动后服务在后台无限期运行,直到调用stopSelf()
或者stopService()
。每次调用startService()
时都会调用该方法,如果只想提供绑定,则不需要实现该方法,如AdapterService
,而ProfileService
则实现了该方法,即可以绑定也可以直接启动。public class AdapterService extends Service {
private void setProfileServiceState(Class service, int state) {
Intent intent = new Intent(this, service);
intent.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
startService(intent); //启动或停止Profile服务
}
}
public abstract class ProfileService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
...
String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_OFF)
doStop(); //由各具体的Profile Service实现
else if (state == BluetoothAdapter.STATE_ON)
doStart(); //由各具体的Profile Service实现
}
return PROFILE_SERVICE_MODE;
}
}
onBind()
bindService()
时会调用此方法,它必须返回一个IBinder
以提动接口供客户端和服务端通信,如果不允许绑定也需要实现此方法,返回null
即可。public class AdapterService extends Service {
private AdapterServiceBinder mBinder;
public IBinder onBind(Intent intent) {
debugLog("onBind()");
return mBinder;
}
}
public class BluetoothManagerService extends IBluetoothManager.Stub {
boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
ComponentName comp = resolveSystemService(intent, mContext.getPackageManager(), 0);
intent.setComponent(comp);
//绑定服务
if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
Log.e(TAG, "Fail to bind to: " + intent);
return false;
}
return true;
}
}
onCreate()
onStartCommand()
或onBind()
之前)会调用此方法,即该方法只会调用一次。public abstract class ProfileService extends Service {
public void onCreate() {
super.onCreate();
mAdapter = BluetoothAdapter.getDefaultAdapter();
mBinder = initBinder();
create();
}
}
onDestroy()
除了实现上面方法外,还需要在AndroidManifest.xml文件中声明服务,通过intent-filter标签声明可以过滤的action,在启动或绑定时只有声明中指定的action才可以启动或者绑定。
<application>
<service android:process="@string/process"
android:name="com.android.bluetooth.btservice.AdapterService"
android:exported="true"
android:permission="android.permission.ACCESS_BLUETOOTH_SHARE">
<intent-filter>
<action android:name="android.bluetooth.IBluetooth"/>
</intent-filter>
</service>
</application>
启动服务和绑定服务生命周期如下:
服务可以同时支持启动(startService()
)和绑定(bindService()
)两种方式,只需要同时实现onStartCommand()
以及onBind()
返回一个非空的IBinder
,当使用startService()
启动服务后,所有客户端去掉绑定时服务不会停止,只能通过调用stopSelf()
或者stopService()
停止服务。蓝牙中的Profile就是这样实现的。具体代码参考前面的onStartCommand()
和onBind()
处的描述。
蓝牙中的服务都是绑定服务,它们为App提供了进程间通信的接口,绑定服务的的创建有三种方式:扩展Binder类、使用Messenger、使用AIDL,同样的蓝牙中只使用了AIDL,因此重点分析AIDL,这里简单看看它们的区别和使用场景(扩展Binder类和使用Messenger可以参考google开发文档的详细描述)。
aidl
文件,然后实现对应的接口扩展即可实现跨进程通信。采用 Messenger 的方法实际上是以 AIDL 作为其底层原理。客户端可以通过bindService()
绑定到服务,然后系统会调用服务的onBind()
方法,该方法返回可以和服务交互的IBinder
,绑定操作是异步的,客户端要接收IBinder
,需要创建一个ServiceConntion
实例,调用bindService()
时传递该实例,实现步骤如下:
ServiceConnection
,必须实现它的两个回调方法:onServiceConnected()
IBinder
返回给客户端onServiceDisconnected()
bindService()
,并将第一步实现的ServiceConnection
实例传入。onServiceConnected()
时,客户端就可以获得IBinder
并调用服务。unbindService()
。AdapterService
为例。private class BluetoothServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName componentName, IBinder service) {
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
...
msg.obj = service; //获得Service的IBinder
mHandler.sendMessage(msg);
}
public void onServiceDisconnected(ComponentName componentName) {
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
...
mHandler.sendMessage(msg);
}
}
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
...
IBinder service = (IBinder) msg.obj;
mBluetooth = IBluetooth.Stub.asInterface(service); //获得Service提供的接口
...
}
//绑定服务
private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
Intent i = new Intent(IBluetooth.class.getName()); //AndroidManifest.xml文件设置的intent-filter过滤标签
doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT));
// 调用接口
mBluetooth.enable(quietMode, attributionSource, recv);
mBluetooth.getName(attributionSource, recv);
接下来将剖析一下AIDL的实现,在分析AIDL之前先看看Binder机制。
Binder是Android中一种实现跨进程通信的方式,底层实现了Binder驱动,用于连接Service进程、Client进程、ServiceManager进程,上层实现的Binder类,它实现了IBinder接口,为进程间通信提供接口。Binder的模型如下:
从Android系统角度分为两部分: Android系统框架部分(由系统框架实现,是一个通用的框架)、Android应用层部分(不同应用的client和service实现不同);从进程角度分为两部:内核空间(Binder驱动)、用户空间(ServiceManager进程、Client进程、Service进程),这两个部分之间通过系统调用进行通信。
通信过程如下:
copy_from_user()
和copy_to_user()
函数进行拷贝,Linux常见的进程通信机制如pipe、FIFO、socket等都需要进程两次拷贝,即进程A -> 内核 -> 进程B,而Binder则只需要一次拷贝,实现原理如下:mmap()
),此时内核空间和用户空间之间传输数据不需要拷贝可以直接访问,当发送进程从用户空间将数据拷贝到内核间时,通知接收进程从映射内存在获取数据(内核空间中内存本身就是共享的),此时从发送进程将数据传输到接收进程就只使用了一次拷贝。为什么不在两个进程之间直接使用内存映射?
直接内存映射虽然不需要拷贝数据,但没有Client与Service之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题,而Binder驱动采用了C/S架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好,其性能也仅次于内存映射。
AIDL也是基于Binder机制实现的,下面将详细分析一下AIDL的实现。
前面了解了绑定服务的三种实现方式,蓝牙中的所有服务都不是仅仅供自己的应用专用,它们需要向开发者提供蓝牙的各种能力,因此使用AIDL(Android Interface Definition Language)是最适合的。在实际的开发过程中,我们不需要自己去从头开始实现整个AIDL的底层机制,Android SDK提供了一套完整的方案,开发时只需要编写.aidl
文件,然后在Service中实现.aidl
文件中定义的接口,最后Client就可以调用Service提供的这些能力了,看一下蓝牙中的实现:
package android.bluetooth;
import android.app.PendingIntent;
...
interface IBluetooth
{
oneway void setName(in String name, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
oneway void getName(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
}
public static class AdapterServiceBinder extends IBluetooth.Stub {
@Override
public void setName(String name, AttributionSource source, SynchronousResultReceiver receiver) {
try {
receiver.send(setName(name, source));//
} catch (RuntimeException e) {
receiver.propagateException(e);
}
}
@Override
public void getName(AttributionSource source, SynchronousResultReceiver receiver) {
try {
receiver.send(getName(source));
} catch (RuntimeException e) {
receiver.propagateException(e);
}
}
}
public class AdapterService extends Service
private AdapterServiceBinder mBinder = new AdapterServiceBinder(this);
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
以上步骤都是开发人员需要完成的内容,接下来的内主要有Android SDK中的工具完成,它会将.aidl
文件转换成什么,如何实现的Client和Service的通行。
编译之后,.aidl
文件会生成一个同名的但将后缀替换为.java
的文件,其内容如下(以IBluetooth.aidl为例,仅保留了核心代码):
package android.bluetooth;
public interface IBluetooth extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements android.bluetooth.IBluetooth
{
private static final java.lang.String DESCRIPTOR = "android.bluetooth.IBluetooth";
public Stub() { this.attachInterface(this, DESCRIPTOR); }
public static android.bluetooth.IBluetooth asInterface(android.os.IBinder obj)
{
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.bluetooth.IBluetooth)))
return ((android.bluetooth.IBluetooth)iin);
return new android.bluetooth.IBluetooth.Stub.Proxy(obj);
}
public android.os.IBinder asBinder() { return this; }
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case TRANSACTION_setName: {
java.lang.String _arg0 = data.readString();
boolean _result = this.setName(_arg0);
reply.writeInt(((_result)?(1):(0)));
return true;
}
case TRANSACTION_getName: {
java.lang.String _result = this.getName();
reply.writeString(_result);
return true;
}
}
}
private static class Proxy implements android.bluetooth.IBluetooth
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) { mRemote = remote; }
public android.os.IBinder asBinder() { return mRemote; }
public boolean setName(java.lang.String name)
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
return (0!=_reply.readInt());
}
public java.lang.String getName()
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
return _reply.readString();
}
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
}
public boolean setName(java.lang.String name);
public java.lang.String getName();
}
总体可分为三个部分:
IBluetooth
它是一个接口,即IBluetooth.aidl文件中定义的接口,直接转换成了java的interface
,格式也基本相同,aidl文件中定义了几个,这里就有几个接口,如setName()
、getName()
。
IBluetooth.Stub
它是Service端的一个抽象类且继承了IBluetooth
中的接口,因此Service必须继承Stub
并实现里面的所有接口(前面已经介绍过),当收到Client Proxy调用时它会回调onTransace
方法,在该方法中根据code
判断调用子类实现的具体方法,如this.setName()
、this.getName()
。code
是一组不会重复的整数,如TRANSACTION_setName
、TRANSACTION_getName
, 该类的构造函数中调用Binder的attachInterface
方法想ServiceManager
注册服务,在客户端绑定服务时可以获取到服务的IBinder
,通过IBinder
可以调用到asInterface
方法,并获取到IBluetooth
,在asInterface
方法中还会判断是本地调用还是远程调用,如果是本地调用直接返回本地的IBluetooth
,如果是远程调用才返回Proxy
,mBluetooth = IBluetooth.Stub.asInterface(service);
IBluetooth.Stub.Proxy
它是Client端对Service端的代理实现,它不是一个抽象类可以直接创建实例,它也继承了IBluetooth
,并在类中实现了这些接口,不过这些接口都是通过调用远程IBinder中的transact
方法发起远程调用。
这几个类和接口之间的关系如下:
JNI(Java Native Interface)是Java字节码与C/C++进行交互的方法,简单来说即JNI可以实现Java调用C/C++,或则C/C++调用Java。在Android中有很多引用第三发的库或者有些模块对性能要求很高,它们都可能是C/C++实现的,蓝牙协议栈Flouride就是其中之一。这里不会介绍JNI的底层原理,只会描述Framework和Native之间蓝牙JNI的使用,代码实现路径为android/app/jni, 包含以下几部分内容:
System.loadLibrary()
加载C++实现的Java与C++之间交互的代码,生成的动态库为libbluetooth_jni.so
,编译脚本在Android.bp文件中。cc_library_shared {
name: "libbluetooth_jni",
defaults: ["fluoride_basic_defaults"],
srcs: ["jni/**/*.cpp"],
}
加载动态库的代码如下:
public class AdapterApp extends Application {
static {
System.loadLibrary("bluetooth_jni");
}
}
System.loadLibrary()
加载动态库时会调用动态库中实现的JNI_OnLoad()
,该函数是JNI初始化的入口,函数原型为jint JNI_OnLoad(JavaVM* jvm, void* reserved)
。在该函数中调用jniRegisterNativeMethods()
注册Native实现的方法,函数原型为int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
。jvm->GetEnv()
获取"com/android/bluetooth/btservice/AdapterService"
。JNINativeMethod
的定义如下:typedef struct {
const char* name; //java中声明的方法名称
const char* signature; // 函数签名,即参数、返回值等定义
void* fnPtr; // C++中对应的该方法的函数地址
} JNINativeMethod;
AdapterService
部分方法定义如下:
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z", (void*)initNative},
{"cleanupNative", "()V", (void*)cleanupNative},
{"enableNative", "()Z", (void*)enableNative},
{"disableNative", "()Z", (void*)disableNative},
...
};
env->FindClass()
查找java的class实例,FindClass()
方法的参数是java class的名称,如:"com/android/bluetooth/btservice/JniCallbacks"
,然后调用env->GetMethodID()
获取JniCallbacks
中实现的方法,参数包括获取到的java class的实例、方法名称、函数签名。static void classInitNative(JNIEnv* env, jclass clazz) {
...
jclass jniCallbackClass =
env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
method_oobDataReceivedCallback =
env->GetMethodID(jniCallbackClass, "oobDataReceivedCallback",
"(ILandroid/bluetooth/OobData;)V");
method_stateChangeCallback =
env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V");
...
}
env->GetFieldID()
获取实例,参数包括需要获取的实例对象的实例、实例的成员名称、实例的类型,最后调用方法。static void classInitNative(JNIEnv* env, jclass clazz) {
sJniCallbacksField = env->GetFieldID(
clazz, "mJniCallbacks", "Lcom/android/bluetooth/btservice/JniCallbacks;");
}
static bool initNative(JNIEnv* env, jobject obj, ...) {
sJniCallbacksObj =
env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
}
static void adapter_state_change_callback(bt_state_t status) {
...
sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback,
(jint)status);
}
jniRegisterNativeMethods()
传入的名称相同,声明的方法名称也必须和传入数组中的方法名称相同。public class AdapterService extends Service {
static {
classInitNative(); //调用方法
}
...
public void onCreate() {
...
initNative(mUserManager.isGuestUser(), isCommonCriteriaMode(), configCompareResult,
getInitFlags(), isAtvDevice, getApplicationInfo().dataDir);
}
...
static native void classInitNative();
native boolean initNative(boolean startRestricted, boolean isCommonCriteriaMode,
int configCompareResult, String[] initFlags, boolean isAtvDevice,
String userDataDirectory);
native void cleanupNative();
/*package*/
native boolean enableNative();
/*package*/
native boolean disableNative();
...
}
FindClass()
传入的名称相同,实例名称必须和第4中GetFieldID()
传入的名称、类型相同。public class AdapterService extends Service {
...
private JniCallbacks mJniCallbacks;
public void onCreate() {
...
mJniCallbacks = new JniCallbacks(this, mAdapterProperties);
}
}
final class JniCallbacks {
...
void stateChangeCallback(int status) {
mAdapterService.stateChangeCallback(status);
}
void discoveryStateChangeCallback(int state) {
mAdapterProperties.discoveryStateChangeCallback(state);
}
...
}
Android 应用可以从 Android 系统和其他 Android 应用发送或接收广播消息,类似于发布-订阅设计模式。这些广播会在相关事件发生时发送。例如,Android 系统会在发生各种系统事件时发送广播,如系统启动或设备开始充电时。应用还可以发送自定义广播,例如,通知其他应用它们可能感兴趣的内容(例如,一些新数据已下载)。
系统会优化广播的传送,以保持最佳的系统运行状况。因此,广播的传递时间无法保证。需要进行低延迟进程间通信的应用应考虑绑定服务。
应用可以注册接收特定的广播。发送广播后,系统会自动将广播路由到已订阅接收该特定类型的广播的应用。
一般来说,广播可用作跨应用和正常用户流之外的消息传递系统。但是,您必须格外小心,不要滥用在后台响应广播和运行作业的机会,否则可能会导致系统性能变慢。
引用自google开发文档 https://developer.android.com/develop/background-work/background-tasks/broadcasts?hl=zh-cn
在蓝牙Framework中Broadcast用于通知各状态变化,如:
// 发送端
Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
// 添加附带的数据
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
// 设置flag
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Utils.sendBroadcast(mA2dpService, intent, BLUETOOTH_CONNECT,
Utils.getTempAllowlistBroadcastOptions());
//接收端
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
// 设置关注的action
filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
//注册接收器
mAdapterService.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// 获取数据
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);
final int previousState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
final int currentState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
switch (action) {
// 根据action做处理
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
...
break;
}
}
}