packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterApp.java
public class AdapterApp extends Application {
static {
System.loadLibrary("bluetooth_jni");
}
}
bluetooth_jni 对应的 so 库文件全称为:libbluetooth_jni.so,加载过程中应该会补全。
libcore/ojluni/src/main/java/java/lang/System.java
@CallerSensitive
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
由于 Runtime 中存在 loadLibrary0() 方法重载,首先需要确定第一个参数的类型。
libcore/ojluni/src/main/java/sun/reflect/Reflection.java
public static Class<?> getCallerClass() {
return VMStack.getStackClass2();
}
libcore/ojluni/src/main/java/java/lang/Runtime.java
void loadLibrary0(Class<?> fromClass, String libname) {
ClassLoader classLoader = ClassLoader.getClassLoader(fromClass);
loadLibrary0(classLoader, fromClass, libname);
}
private synchronized void loadLibrary0(ClassLoader loader, Class<?> callerClass, String libname) {
......
String libraryName = libname;
if (loader != null && !(loader instanceof BootClassLoader)) {
String filename = loader.findLibrary(libraryName); // 1.1
if (filename == null &&
(loader.getClass() == PathClassLoader.class ||
loader.getClass() == DelegateLastClassLoader.class)) {
filename = System.mapLibraryName(libraryName); // 1.2
}
if (filename == null) {
throw new UnsatisfiedLinkError(l"......");
}
String error = nativeLoad(filename, loader); // 1.3
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
getLibPaths(); // 2.1
String filename = System.mapLibraryName(libraryName); // 2.2
String error = nativeLoad(filename, loader, callerClass); // 2.3
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
}
- 第一个分支
- 1.1 ClassLoader.findLibrary(libraryName)
- 1.2 System.mapLibraryName(libraryName)
- 1.3 nativeLoad(filename, loader)
- 第二个分支
- 2.1 getLibPaths()
- 2.2 System.mapLibraryName(libraryName)
- 2.3 nativeLoad(filename, loader, callerClass)
在 Android 11 平台的 Pixel 5 上测试,实际执行的是 1.1 和 1.3,
1.1 ClassLoader.findLibrary()
libcore/ojluni/src/main/java/java/lang/ClassLoader.java
public abstract class ClassLoader {
protected String findLibrary(String libname) {
return null;
}
}
libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
public class BaseDexClassLoader extends ClassLoader {
public String findLibrary(String name) {
return pathList.findLibrary(name);
}
public BaseDexClassLoader(String dexPath,
String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
boolean isTrusted) {
......
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
......
}
}
libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public final class DexPathList {
public String findLibrary(String libraryName) {
String fileName = System.mapLibraryName(libraryName); // 1
for (NativeLibraryElement element : nativeLibraryPathElements) { // 2
String path = element.findNativeLibrary(fileName); // 3
if (path != null) {
return path;
}
}
return null;
}
}
1.1.1 System.mapLibraryName()
public final class System {
public static native String mapLibraryName(String libname);
}
libcore/ojluni/src/main/native/System.c
JNIEXPORT jstring JNICALL
System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
{
int len;
int prefix_len = (int) strlen(JNI_LIB_PREFIX);
int suffix_len = (int) strlen(JNI_LIB_SUFFIX);
jchar chars[256];
len = (*env)->GetStringLength(env, libname);
...... // 长度校验,不能超过 240
cpchars(chars, JNI_LIB_PREFIX, prefix_len);
(*env)->GetStringRegion(env, libname, 0, len, chars + prefix_len);
len += prefix_len;
cpchars(chars + len, JNI_LIB_SUFFIX, suffix_len);
len += suffix_len;
return (*env)->NewString(env, chars, len);
}
将 JNI_LIB_PREFIX、libname、JNI_LIB_SUFFIX 三者拼接成一个字符串,从下面的头文件可知,最终拼接的字符串是:libbluetooth_jni.so,即为目标库名称。
libcore/ojluni/src/main/native/jvm_md.h
#define JNI_LIB_PREFIX "lib"
#define JNI_LIB_SUFFIX ".so"
1.1.2 nativeLibraryPathElements
public final class DexPathList {
public DexPathList(ClassLoader definingContext, String librarySearchPath) {
......
this.nativeLibraryDirectories = splitPaths(librarySearchPath, false); // 1
this.systemNativeLibraryDirectories =
splitPaths(System.getProperty("java.library.path"), true); // 2
this.nativeLibraryPathElements = makePathElements(getAllNativeLibraryDirectories()); // 3
}
private List<File> getAllNativeLibraryDirectories() {
List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
return allNativeLibraryDirectories;
}
}
语句 // 1 与 语句 // 2 构成语句 // 3 所在的集合
1.1.2.1 librarySearchPath
由外部传递
1.1.2.2 System.getProperty("java.library.path")
找了台 Android R 的手机测试一下 “java.library.path”的值 System.getProperty("java.library.path") = /system/lib64:/system_ext/lib64
在 Android R 手机上查看一下这两个目录,说明通过这种方式可以找到目标:libblutooth_jni.so
xxxxxx:/ $ ls system/lib64 | grep libbluetooth
libbluetooth.so
libbluetooth_jni.so
xxxxxx:/ $ ls system_ext/lib64 | grep libbluetooth
1|xxxxxx:/ $
1.1.3 NativeLibraryElement.findNativeLibrary()
public final class DexPathList {
/*package*/ static class NativeLibraryElement {
public String findNativeLibrary(String name) {
maybeInit();
if (zipDir == null) {
String entryPath = new File(path, name).getPath();
if (IoUtils.canOpenReadOnly(entryPath)) {
return entryPath;
}
} else if (urlHandler != null) {
// Having a urlHandler means the element has a zip file.
// In this case Android supports loading the library iff
// it is stored in the zip uncompressed.
String entryName = zipDir + '/' + name;
if (urlHandler.isEntryStored(entryName)) {
return path.getPath() + zipSeparator + entryName;
}
}
return null;
}
}
}
根据绝对路径确定库文件存在性、读写情况等。
1.2 System.mapLibraryName()
如前文 1.1.1 System.mapLibraryName()
1.3 nativeLoad(filename, loader)
public class Runtime {
private static String nativeLoad(String filename, ClassLoader loader) {
return nativeLoad(filename, loader, null);
}
}
public class Runtime {
private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
}
二者 nativeLoad 方法的区别在于最后一个参数的不同。
以下分析为两个分支的相同流程,仅最后一个参数不同。
libcore/ojluni/src/main/native/Runtime.c
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jclass caller)
{
return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
}
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jclass caller) {
ScopedUtfChars filename(env, javaFilename);
std::string error_msg;
{
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
caller,
&error_msg);
if (success) {
return nullptr;
}
}
env->ExceptionClear();
return env->NewStringUTF(error_msg.c_str());
}
art/runtime/jni/java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jclass caller_class,
std::string* error_msg) {
...... // 待填坑
bool was_successful = false;
void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
was_successful = true;
} else {
ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
self->SetClassLoaderOverride(class_loader);
VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
using JNI_OnLoadFn = int(*)(JavaVM*, void*);
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
int version = (*jni_on_load)(this, nullptr);
if (IsSdkVersionSetAndAtMost(runtime_->GetTargetSdkVersion(), SdkVersion::kL)) {
// Make sure that sigchain owns SIGSEGV.
EnsureFrontOfChain(SIGSEGV);
}
self->SetClassLoaderOverride(old_class_loader.get());
if (version == JNI_ERR) {
StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
} else if (JavaVMExt::IsBadJniVersion(version)) {
StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
path.c_str(), version);
} else {
was_successful = true;
}
}
library->SetResult(was_successful);
return was_successful;
}
从以上代码可以看出,若 libbluetooth_jni.so 加载成功,会调用其中的 JNI_OnLoad() 方法。
JNI_OnLoad() 源码如下,
packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp
jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
JNIEnv* e;
int status;
// Check JNI version
if (jvm->GetEnv((void**)&e, JNI_VERSION_1_6)) {
return JNI_ERR;
}
status = android::register_com_android_bluetooth_btservice_AdapterService(e);
if (status < 0) {
return JNI_ERR;
}
......
return JNI_VERSION_1_6;
}
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
return jniRegisterNativeMethods(
env, "com/android/bluetooth/btservice/AdapterService", sMethods,
NELEM(sMethods));
}
// Java 与 Native 方法映射表
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "(ZZI[Ljava/lang/String;Z)Z", (void*)initNative},
......
}
XXXX jniRegisterNativeMethods
找到2个实现,按道理在 Android R 版本应该是 art 虚拟机。为了严谨,后续在 pixel 5上实测一下,填个坑
void jniRegisterNativeMethods(JNIEnv* env,
const char* className,
const JNINativeMethod* methods,
int numMethods) {
jclass c = env->FindClass(className);
if (c == nullptr) {
char* tmp;
const char* msg;
if (asprintf(&tmp,
"Native registration unable to find class '%s'; aborting...",
className) == -1) {
// Allocation failed, print default warning.
msg = "Native registration unable to find class; aborting...";
} else {
msg = tmp;
}
env->FatalError(msg);
}
if (env->RegisterNatives(c, methods, numMethods) < 0) {
char* tmp;
const char* msg;
if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
// Allocation failed, print default warning.
msg = "RegisterNatives failed; aborting...";
} else {
msg = tmp;
}
env->FatalError(msg);
}
}
int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* methods, int numMethods)
{
ALOGV("Registering %s's %d native methods...", className, numMethods);
jclass clazz = (*env)->FindClass(env, className);
ALOG_ALWAYS_FATAL_IF(clazz == NULL,
"Native registration unable to find class '%s'; aborting...",
className);
int result = (*env)->RegisterNatives(env, clazz, methods, numMethods);
(*env)->DeleteLocalRef(env, clazz);
if (result == 0) {
return 0;
}
// Failure to register natives is fatal. Try to report the corresponding exception,
// otherwise abort with generic failure message.
jthrowable thrown = (*env)->ExceptionOccurred(env);
if (thrown != NULL) {
struct ExpandableString summary;
ExpandableStringInitialize(&summary);
if (GetExceptionSummary(env, thrown, &summary)) {
ALOGF("%s", summary.data);
}
ExpandableStringRelease(&summary);
(*env)->DeleteLocalRef(env, thrown);
}
ALOGF("RegisterNatives failed for '%s'; aborting...", className);
return result;
}
2.1 getLibPaths()
private String[] getLibPaths() {
if (mLibPaths == null) {
synchronized(this) {
if (mLibPaths == null) {
mLibPaths = initLibPaths();
}
}
}
return mLibPaths;
}
private static String[] initLibPaths() {
String javaLibraryPath = System.getProperty("java.library.path");
if (javaLibraryPath == null) {
return EmptyArray.STRING;
}
String[] paths = javaLibraryPath.split(":");
// Add a '/' to the end of each directory so we don't have to do it every time.
for (int i = 0; i < paths.length; ++i) {
if (!paths[i].endsWith("/")) {
paths[i] += "/";
}
}
return paths;
}
如前文 1.1.2.2 System.getProperty("java.library.path")
2.2 System.mapLibraryName()
如前文 1.1.1 System.mapLibraryName()
2.3 nativeLoad(filename, loader, callerClass)
如前文 1.3 nativeLoad(filename, loader)
网友评论