System.loadLibrary(libname)

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterApp.java

public class AdapterApp extends Application {
    static {
        System.loadLibrary("bluetooth_jni");
    }
}

bluetooth_jni 對應(yīng)的 so 庫文件全稱為:libbluetooth_jni.so应闯,加載過程中應(yīng)該會補全谓松。

libcore/ojluni/src/main/java/java/lang/System.java

    @CallerSensitive
    public static void loadLibrary(String libname) {
        Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
    }

由于 Runtime 中存在 loadLibrary0() 方法重載拳氢,首先需要確定第一個參數(shù)的類型冻辩。

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.1 ClassLoader.findLibrary(libraryName)
    • 1.2 System.mapLibraryName(libraryName)
    • 1.3 nativeLoad(filename, loader)
  2. 第二個分支
    • 2.1 getLibPaths()
    • 2.2 System.mapLibraryName(libraryName)
    • 2.3 nativeLoad(filename, loader, callerClass)

在 Android 11 平臺的 Pixel 5 上測試,實際執(zhí)行的是 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媒峡,即為目標(biāo)庫名稱瘟栖。

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 構(gòu)成語句 // 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 手機上查看一下這兩個目錄,說明通過這種方式可以找到目標(biāo):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;
        }
    }
}

根據(jù)絕對路徑確定庫文件存在性谅阿、讀寫情況等半哟。

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 方法的區(qū)別在于最后一個參數(shù)的不同。
以下分析為兩個分支的相同流程签餐,僅最后一個參數(shù)不同寓涨。
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);
}

art/openjdkjvm/OpenjdkJvm.cc

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 加載成功贱田,會調(diào)用其中的 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個實現(xiàn)男摧,按道理在 Android R 版本應(yīng)該是 art 虛擬機。為了嚴謹译打,后續(xù)在 pixel 5上實測一下耗拓,填個坑

  1. art/benchmark/micro-native/micro_native.cc
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);
    }
}
  1. libnativehelper/JNIHelp.c
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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奏司,隨后出現(xiàn)的幾起案子乔询,更是在濱河造成了極大的恐慌,老刑警劉巖韵洋,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竿刁,死亡現(xiàn)場離奇詭異,居然都是意外死亡搪缨,警方通過查閱死者的電腦和手機食拜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來副编,“玉大人负甸,你說我怎么就攤上這事。” “怎么了呻待?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵打月,是天一觀的道長。 經(jīng)常有香客問我蚕捉,道長奏篙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任鱼冀,我火速辦了婚禮报破,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘千绪。我一直安慰自己充易,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布荸型。 她就那樣靜靜地躺著盹靴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瑞妇。 梳的紋絲不亂的頭發(fā)上稿静,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音辕狰,去河邊找鬼改备。 笑死,一個胖子當(dāng)著我的面吹牛蔓倍,可吹牛的內(nèi)容都是我干的悬钳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼偶翅,長吁一口氣:“原來是場噩夢啊……” “哼默勾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起聚谁,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤母剥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后形导,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體环疼,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年朵耕,在試婚紗的時候發(fā)現(xiàn)自己被綠了秦爆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡憔披,死狀恐怖等限,靈堂內(nèi)的尸體忽然破棺而出爸吮,到底是詐尸還是另有隱情,我是刑警寧澤望门,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布形娇,位于F島的核電站,受9級特大地震影響筹误,放射性物質(zhì)發(fā)生泄漏桐早。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一厨剪、第九天 我趴在偏房一處隱蔽的房頂上張望哄酝。 院中可真熱鬧,春花似錦祷膳、人聲如沸陶衅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搀军。三九已至,卻和暖如春勇皇,著一層夾襖步出監(jiān)牢的瞬間罩句,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工敛摘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留门烂,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓兄淫,卻偏偏與公主長得像屯远,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拖叙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容