到底是什么?
什么时候加载?
从何处来?
从代码层层剖析,清楚明白。如有不正确的请指正,非常感谢。
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 "android:name"
attribute in your
* AndroidManifest.xml's <application>
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这个库的流程,看看到底是怎么肥事。
源头:
static {
if (DBG) Log.d(TAG,"Loading JNI Library");
System.loadLibrary("bluetooth_jni");
}
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
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流程不在本篇文章目标范围内,不记诉。
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。
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就这么多内容。