天天看点

android 蓝牙之bluetooth_jni

android 蓝牙之bluetooth_jni

    • AdapterApp分析
    • loadLibrary流程
    • libbluetooth_jni.so

通过阅读该篇文章,你可以知道bluetooth_jni

到底是什么?

什么时候加载?

从何处来?

从代码层层剖析,清楚明白。如有不正确的请指正,非常感谢。

AdapterApp分析

Bluetooth.apk里面有个AdapterApp.java,该类继承自android.app.Application, Application类的解释如下:

/**
 * Base class for maintaining global application state. You can provide your own
 * implementation by creating a subclass and specifying the fully-qualified name
 * of this subclass as the <code>"android:name"</code> attribute in your
 * AndroidManifest.xml's <code>&lt;application&gt;</code> tag. The Application
 * class, or your subclass of the Application class, is instantiated before any
 * other class when the process for your application/package is created.
 **/
           

由这解释可以可以看出,继承自Application的子类需要在AndroidManifest.xml里注册,然后子类便会在你进程的其它所有类create之前实例化。

也就是说,在蓝牙进程启动时,AdapterApp类是第一个跑起来的可见类。

public class AdapterApp extends Application {
    private static final String TAG = "BluetoothAdapterApp";
    private static final boolean DBG = false;
    //For Debugging only
    private static int sRefCount=0;

    static {
        if (DBG) Log.d(TAG,"Loading JNI Library");
        System.loadLibrary("bluetooth_jni");
    }

    public AdapterApp() {
        super();
        if (DBG) {
            synchronized (AdapterApp.class) {
                sRefCount++;
                Log.d(TAG, "REFCOUNT: Constructed "+ this + " Instance Count = " + sRefCount);
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (DBG) Log.d(TAG, "onCreate");
        Config.init(this);
    }

    @Override
    protected void finalize() {
        if (DBG) {
            synchronized (AdapterApp.class) {
                sRefCount--;
                Log.d(TAG, "REFCOUNT: Finalized: " + this +", Instance Count = " + sRefCount);
            }
        }
    }
}
           

AdapterApp的代码很简单,完成的工作也很简单。

第一步,执行静态代码块,load一个叫

bluetooth_jni

的库;

第二步,如代码所见,构造方法只执行父类的构造方法;

第三步,执行父类的onCreate()方法,并执行Config.init(this),该方法的作用是将支持的协议列表以ArrayList的形式放起来,不是本篇文章的重点。

蓝牙启动时,AdapterApp的工作就完成了。如上所见,应该有个叫

bluetooth_jni

的库,可是当搜索源码的时候并没有这个库,于是,本篇跟踪一下load这个库的流程,看看到底是怎么肥事。

loadLibrary流程

源头:

static {
        if (DBG) Log.d(TAG,"Loading JNI Library");
        System.loadLibrary("bluetooth_jni");
    }
           
  1. System是java.lang下面的,方法的实现:
public static void loadLibrary(String libname) {
        Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
    }
           
  1. 每个Java应用进程都有一个 Runtime的单例实例,通过getRuntime()获取这个唯一实例,它允许应用程序与运行应用程序的环境进行交互。
synchronized void loadLibrary0(ClassLoader loader, String libname) {
        if (libname.indexOf((int)File.separatorChar) != -1) {
            throw new UnsatisfiedLinkError(
    "Directory separator should not appear in library name: " + libname);
        }
        String libraryName = libname;
        if (loader != null) {
            String filename = loader.findLibrary(libraryName);
            if (filename == null) {
                // It's not necessarily true that the ClassLoader used
                // System.mapLibraryName, but the default setup does, and it's
                // misleading to say we didn't find "libMyLibrary.so" when we
                // actually searched for "liblibMyLibrary.so.so".
                throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                               System.mapLibraryName(libraryName) + "\"");
            }
            String error = doLoad(filename, loader);
            if (error != null) {
                throw new UnsatisfiedLinkError(error);
            }
            return;
        }

        String filename = System.mapLibraryName(libraryName);
        List<String> candidates = new ArrayList<String>();
        String lastError = null;
        for (String directory : getLibPaths()) {
            String candidate = directory + filename;
            candidates.add(candidate);

            if (IoUtils.canOpenReadOnly(candidate)) {
                String error = doLoad(candidate, loader);
                if (error == null) {
                    return; // We successfully loaded the library. Job done.
                }
                lastError = error;
            }
        }

        if (lastError != null) {
            throw new UnsatisfiedLinkError(lastError);
        }
        throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
    }
           

loader != null,所以

bluetooth_jni

要经过String filename = loader.findLibrary(libraryName)转换,方法的实现在BaseDexClassLoader中:

@Override
    public String findLibrary(String name) {
        return pathList.findLibrary(name);
    }
           

pathList在系统启动时初始化,在load APK的时候将APK对应的内容加进去。

public String findLibrary(String libraryName) {
        String fileName = System.mapLibraryName(libraryName);

        for (Element element : nativeLibraryPathElements) {
            String path = element.findNativeLibrary(fileName);

            if (path != null) {
                return path;
            }
        }

        return null;
    }
           

这里的第一步是通过System.mapLibraryName转化

bluetooth_jni

/** Maps a platform independent library name to a platform dependent name. */
	public String mapLibraryName (String libraryName) {
		if (isWindows) return libraryName + (is64Bit ? "64.dll" : ".dll");
		if (isLinux) return "lib" + libraryName + (isARM ? "arm" + abi : "") + (is64Bit ? "64.so" : ".so");
		if (isMac) return "lib" + libraryName + (is64Bit ? "64.dylib" : ".dylib");
		return libraryName;
	}
           

判断是什么系统,我们android基于linux,所以经过转化后,如果是32位的,

bluetooth_jni

变成

libbluetooth_jni.so

回到上一段代码,遍历nativeLibraryPathElements,nativeLibraryPathElements在系统启动时初始化,存的是各个库的

运行时

的绝对路径,

libbluetooth_jni.so

对应的路径是

/system/app/Bluetooth/lib/arm/libbluetooth_jni.so

.

此外,该文件在系统中的路径是

/system/lib/libbluetooth_jni.so

3. 由此知道了

bluetooth_jni

对应的库文件,回到loadLibrary0()中,调用doLoad()函数加载指定文件,就OK啦。Load流程不在本篇文章目标范围内,不记诉。

libbluetooth_jni.so

  1. packages/apps/Bluetooth/jni/Android.mk :
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    com_android_bluetooth_btservice_AdapterService.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_hdp.cpp \
    com_android_bluetooth_pan.cpp \
    com_android_bluetooth_gatt.cpp \
    com_android_bluetooth_sdp.cpp \
    com_android_bluetooth_btservice_vendor.cpp

ifneq ($(TARGET_SUPPORTS_WEARABLES),true)
LOCAL_C_INCLUDES += \
     $(JNI_H_INCLUDE) \
     vendor/qcom/opensource/bluetooth/hal/include
else
LOCAL_C_INCLUDES += \
     $(JNI_H_INCLUDE) \
     device/qcom/msm8909w/opensource/bluetooth/hal/include
endif

LOCAL_SHARED_LIBRARIES := \
    libandroid_runtime \
    libnativehelper \
    libcutils \
    libutils \
    liblog \
    libhardware

LOCAL_MULTILIB := 32

LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter

LOCAL_MODULE := libbluetooth_jni
LOCAL_MODULE_TAGS := optional

include $(BUILD_SHARED_LIBRARY)
           

从中可以看到该目录下的代码编出来便是libbluetooth_jni.so。

  1. 再看,packages/apps/Bluetooth/Android.mk:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := \
        $(call all-java-files-under, lib/mapapi)

LOCAL_MODULE := bluetooth.mapsapi
LOCAL_MULTILIB := 32
include $(BUILD_STATIC_JAVA_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := \
        $(call all-java-files-under, src) \
        $(call all-proto-files-under, src)

LOCAL_PACKAGE_NAME := Bluetooth
LOCAL_CERTIFICATE := platform

LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
LOCAL_JAVA_LIBRARIES := javax.obex telephony-common libprotobuf-java-micro services.net
LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard bluetooth.mapsapi sap-api-java-static android-support-v4 services.net
LOCAL_STATIC_JAVA_LIBRARIES += com.android.emailcommon
LOCAL_PROTOC_OPTIMIZE_TYPE := micro

LOCAL_REQUIRED_MODULES := bluetooth.default
LOCAL_MULTILIB := 32

LOCAL_PROGUARD_ENABLED := disabled

include $(BUILD_PACKAGE)

include $(call all-makefiles-under,$(LOCAL_PATH))
           

从中可以看到,在编译Bluetooth时将libbluetooth_jni作为JNI的共享库进行引用,从而编译进

Bluetooth.apk

OK啦,关于bluetooth_jni就这么多内容。

继续阅读