I. System integration third-party library

Under normal circumstances, the system integrates third-party SO, and the provider will provide 32bit and 64bit libraries respectively. In this way, we only need to choose which one to integrate according to the current Android version of ARCH, taking Libbytelink.so as an example

ifeq ($(TARGET_IS_64_BIT),true) LOCAL_PREBUILT_LIBS +=libbytelink:libs/arm64-v8a/libbytelink.so else LOCAL_PREBUILT_LIBS  +=libbytelink:libs/armeabi-v7a/libbytelink.so endif LOCAL_MODULE_TAGS := optional include $(BUILD_MULTI_PREBUILT)Copy the code
  1. First of all, we need to create a libs directory in the root directory of the system APP, and then create armeabi-V7A and ARM64-V8A respectively to store the corresponding 32bit SO and 64bit SO

Here, ARmeabi-V7A and ARM64-V8A represent CPU architectures supported by android platform. Armeabi-v7a: arm64-V8A: arm64-V8A: arm64-V8A: arm64-V8A: ARM64-V8A: ARM64-V8A

  1. For so file, before reference, need to precompile, in the corresponding directory mk, by TARGET_IS_64_BIT to determine the current Android system 32bit or 64bit, Then use LOCAL_PREBUILT_LIBS to copy the so files already stored in the corresponding directory

TARGET_IS_64_BIT is set in the Android make-compiled board config, which specifies whether the above value is true or false based on the current CPU architecture. In the Case of Qualcomm 865, this value is true

ifneq ($(filter %64,$(TARGET_ARCH)),)
  TARGET_IS_64_BIT := true
endif
Copy the code
  1. Then, when building the corresponding package, import it through the following method
LOCAL_JNI_SHARED_LIBRARIES := libbytelink
LOCAL_REQUIRED_MODULES := libbytelink
include $(BUILD_PACKAGE)
Copy the code

System integration with the third-party 32bit library

If for some reason, the supplier can only provide 32bit SO for the time being, in this case, how can we integrate it in 64bit Android

If armeabi-v7a only stores the vendor’s so, and then precompile normally, android system loadLibrary will report the following error:

05-08 10:43:29. 260 7747 7747 E AndroidRuntime: Java. Lang. UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/system/priv-app/ScreenMirrorServer/ScreenMirrorServer.apk"],nativeLibraryDirectories=[/system/priv-app/ScreenMirrorSer ver/lib/arm, /system/priv-app/ScreenMirrorServer/ScreenMirrorServer.apk!/lib/armeabi-v7a, /system/lib, /system/product/lib, /system/lib, /system/product/lib]nativeLibraryFindDir=[directory "/system/priv-app/ScreenMirrorServer/lib/arm", zip file "/system/priv-app/ScreenMirrorServer/ScreenMirrorServer.apk", dir "lib/armeabi-v7a", directory "/system/lib", directory "/system/product/lib", directory "/system/lib", directory "/system/product/lib", directory "/apex/com.android.runtime/lib", Directory "/vendor/lib"]] couldn't find "libbytelink. So "05-08 10:43:29.260 7747 7747e The at Java. Lang. Runtime. LoadLibrary0 (1087). The Runtime Java: 05-08 10:43:29. 260, 7747, 7747 E AndroidRuntime: The at Java. Lang. Runtime. LoadLibrary0 (1027). The Runtime Java: 05-08 10:43:29. 260, 7747, 7747 E AndroidRuntime: at java.lang.System.loadLibrary(System.java:1691)Copy the code

3. Mechanism of loading so in Android system

Error: libbytelink.so cannot be found while loadLibrary is not available

couldn’t find “libbytelink.so”

Why is this error reported? How does Android system load so? Let’s analyze it here

1. How does the system loadLibrary

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

@CallerSensitive
public static void loadLibrary(String libname) {
    Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
Copy the code
libcore/ojluni/src/main/java/java/lang/Runtime.java if (loader ! = null && ! (loader instanceof BootClassLoader)) { 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) + "\""); }Copy the code

The UnsatisfiedLinkError exception is found in findLibrary. The UnsatisfiedLinkError exception is found in findLibrary. FindLibrary is implemented in its subclass basedexClassLoader.java

libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

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

And then this goes into the findLibrary of the DexPathList

libcore/dalvik/src/main/java/dalvik/system/DexPathList.java public String findLibrary(String libraryName) { String fileName = System.mapLibraryName(libraryName); for (NativeLibraryElement element : nativeLibraryPathElements) { String path = element.findNativeLibrary(fileName); if (path ! = null) { return path; } } return null; }Copy the code

So let’s first look at what mapLibraryName does, which is a native call.

Actually combine the given libname with a prexfix and suffix to call the file names together to what we end up with. In our example filename is Bytelink. The end result is that the name of the so we are actually looking for in the path is libbytelink.so

libcore/ojluni/src/main/native/System.c

System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
{
    int len;
    // #define JNI_LIB_PREFIX "lib"
    int prefix_len = (int) strlen(JNI_LIB_PREFIX);
    // #define JNI_LIB_SUFFIX ".so"
    int suffix_len = (int) strlen(JNI_LIB_SUFFIX);

    jchar chars[256];
    if (libname == NULL) {
        JNU_ThrowNullPointerException(env, 0);
        return NULL;
    }
    len = (*env)->GetStringLength(env, libname);
    if (len > 240) {
        JNU_ThrowIllegalArgumentException(env, "name too long");
        return NULL;
    }
    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);
Copy the code

Then go back to the above findLibrary method, you can see is traversal nativeLibraryPathElements the array to find the corresponding path, then went to have a look at nativeLibraryPathElements how the array is assigned

libcore/dalvik/src/main/java/dalvik/system/DexPathList.java // Native libraries may exist in both the system and // application library paths, and we use this search order: // // 1. This class loader's library path for application libraries (librarySearchPath): Path to libraries in apk-files // 2. The VM's library Path from The system directories property for system libraries // also known as java.library.path // // This order was reversed prior to Gingerbread; see http://b/2933456. this.nativeLibraryDirectories = splitPaths(librarySearchPath, false); this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true); this.nativeLibraryPathElements = makePathElements(getAllNativeLibraryDirectories());Copy the code
libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

private List<File> getAllNativeLibraryDirectories() {
    List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
    allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
    return allNativeLibraryDirectories;
}
Copy the code

As you can see from the code above, NativeLibraryPathElements nativeLibraryDirectories and systemNativeLibraryDirectories is the sum of the two List NativeLibraryDirectories are added to the list first, so subsequent searches will start from the app directories first

NativeLibraryDirectories are the application’s library path, as the name suggests, a directories in the app’s root directory. Focus on systemNativeLibraryDirectories under this here is how to find

System.getproperty (“java.library.path”) properties, so we can’t find a direct setProp. Like system library path that is certainly can’t be changed, so we look at the system static block of code of this class, initUnchangeableSystemProperties this method

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

// Android-added: Undocumented properties that exist only on Android.
p.put("android.icu.library.version", ICU.getIcuVersion());
p.put("android.icu.unicode.version", ICU.getUnicodeVersion());
p.put("android.icu.cldr.version", ICU.getCldrVersion());

// Property override for ICU4J : this is the location of the ICU4C data. This
// is prioritized over the properties in ICUConfig.properties. The issue with using
// that is that it doesn't play well with jarjar and it needs complicated build rules
// to change its default value.
String icuDataPath = TimeZoneDataFiles.generateIcuDataPath();
p.put("android.icu.impl.ICUBinary.dataPath", icuDataPath);

parsePropertyAssignments(p, specialProperties());
Copy the code

In this method, we put a bunch of immutable system prop. We didn’t find the “java.library.path” we needed, but we see a specialProperties() method here, All of the prop that was put up here is going to be parsed through this as a parameter, as a native function, and look at the internal implementation

libcore/ojluni/src/main/native/System.c

 const char* library_path = getenv("LD_LIBRARY_PATH");
#if defined(__ANDROID__)
    if (library_path == NULL) {
        android_get_LD_LIBRARY_PATH(path, sizeof(path));
        library_path = path;
    }
#endif
    if (library_path == NULL) {
        library_path = "";
    }
    char* java_path = malloc(strlen("java.library.path=") + strlen(library_path) + 1);
    strcpy(java_path, "java.library.path=");
    strcat(java_path, library_path);
Copy the code

Android_get_LD_LIBRARY_PATH (path, sizeof(path)), assigns the value to library_PATH, concatenates it with a javA_PATH, and returns it to the Java layer. Android_get_LD_LIBRARY_PATH = android_get_LD_LIBRARY_PATH = android_get_LD_LIBRARY_PATH = android_get_LD_LIBRARY_PATH

bionic/linker/linker.cpp void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) { const auto& default_ld_paths = g_default_namespace.get_default_library_paths(); size_t required_size = 0; for (const auto& path : default_ld_paths) { required_size += path.size() + 1; } if (buffer_size < required_size) { async_safe_fatal("android_get_LD_LIBRARY_PATH failed, buffer too small: " "buffer len %zu, required len %zu", buffer_size, required_size); } char* end = buffer; for (size_t i = 0; i < default_ld_paths.size(); ++i) { if (i > 0) *end++ = ':'; end = stpcpy(end, default_ld_paths[i].c_str()); }}Copy the code

Do_android_get_LD_LIBRARY_PATH buffer is assigned to end, and then paths from default_LD_PATHS are iterated. So look at g_default_namespaces. Get_default_library_paths ()

bionic/linker/linker_namespaces.h

const std::vector<std::string>& get_default_library_paths() const {
  return default_library_paths_;
}
void set_default_library_paths(std::vector<std::string>&& library_paths) {
  default_library_paths_ = std::move(library_paths);
}
void set_default_library_paths(const std::vector<std::string>& library_paths) {
  default_library_paths_ = library_paths;
}
Copy the code

Here’s a get set method, so let’s see where set_default_LIBRARY_paths is called

bionic/linker/linker.cpp static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan) { g_default_namespace.set_isolated(false); auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths; char real_path[PATH_MAX]; std::vector<std::string> ld_default_paths; for (size_t i = 0; default_ld_paths[i] ! = nullptr; ++i) { if (realpath(default_ld_paths[i], real_path) ! = nullptr) { ld_default_paths.push_back(real_path); } else { ld_default_paths.push_back(default_ld_paths[i]); } } g_default_namespace.set_default_library_paths(std::move(ld_default_paths));Copy the code

In init_default_namespaces, the init_default_namespace_no_config method is called. For detecting memory errors in native code, Android supports plain ASANS and hardware-accelerated ASANS, so default_LD_PATHS = kAsanDefaultLdPaths

bionic/linker/linker.cpp

static const char* const kAsanDefaultLdPaths[] = {
  kAsanSystemLibDir,
  kSystemLibDir,
  kAsanOdmLibDir,
  kOdmLibDir,
  kAsanVendorLibDir,
  kVendorLibDir,
  nullptr
};
Copy the code
bionic/linker/linker.cpp

#if defined(__LP64__)
static const char* const kSystemLibDir        = "/system/lib64";
static const char* const kOdmLibDir           = "/odm/lib64";
static const char* const kVendorLibDir        = "/vendor/lib64";
#else
static const char* const kSystemLibDir        = "/system/lib";
static const char* const kOdmLibDir           = "/odm/lib";
static const char* const kVendorLibDir        = "/vendor/lib";
#endif
Copy the code

In DexPathList, findLibrary will first search from the directory corresponding to the app according to filename with prefix and suffix. If it does not find filename, it will search in the system lib directory corresponding to android64-bit platform. /system/lib64/, /vendor/lib64/

/system/lib/, /vendor/lib/, /vendor/lib/, /system/lib/, /vendor/lib/, /vendor/lib/ How does the system choose whether the APP is a 32-bit or 64-bit process

2. How does the system determine whether the APP should run 32-bit or 64-bit processes

As we all know, zygote Folk starts an infinite loop waiting for AMS to send requests to create app processes after it exits the SystemServer process

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java if (startSystemServer) { Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r ! = null} in the // child (system_server) process. if (r ! = null) { r.run(); return; } } Log.i(TAG, "Accepting command socket connections"); // The select loop returns early in the child process after a fork and // loops forever in the zygote. caller = zygoteServer.runSelectLoop(abiList);Copy the code

Override ABI (Application Binary Interface) Defines a set of rules that allow compiled binary object code to run unchanged on all operating systems compatible with the ABI

Currently, the ABI is supported on android10 as follows:

darwin:/ # getprop | grep "ro.product.cpu.abilist"
[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]
[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist64]: [arm64-v8a]
Copy the code
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord, boolean allowWhileBooting, boolean isolated, boolean keepIfLarge) { // Smt: {@ if (processName ! = mLaunchingProcessName) { trimSystemMemoryIfNeeded(processName, info); mLaunchingProcessName = processName; } // @} return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingRecord, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge, null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */, null /* crashHandler */); }Copy the code
frameworks/base/services/core/java/com/android/server/am/ProcessList.java String requiredAbi = (abiOverride ! = null) ? abiOverride : app.info.primaryCpuAbi; if (requiredAbi == null) { requiredAbi = Build.SUPPORTED_ABIS[0]; }Copy the code

Further down the line, after a number of overloaded constructor calls in ProcessList, startProcessLocked will make a judgment based on the abiOverride passed because null was passed earlier, So requiredAbi = app.info.primaryCpuAbi take a look at which side app.info.primaryCpuAbi comes from

This value is recorded in data/system/packages. XML after apK installation, such as bluetooth app, where you can see primaryCpuAbi=” arm64-v8A “, so Bluetoh. Apk is 64bit by default

<package name="com.android.bluetooth" codePath="/system/app/Bluetooth" nativeLibraryPath="/system/app/Bluetooth/lib" primaryCpuAbi="arm64-v8a" publicFlags="541638213" privateFlags="0" ft="11e8f7d4c00"       it="11e8f7d4c00" ut="11e8f7d4c00" version="29" sharedUserId="1002" isOrphaned="true">  
Copy the code

When Android starts up, the PMS constructor calls scanDirTracedLI to scan all system apps

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java // Collect ordinary system packages.  final File systemAppDir = new File(Environment.getRootDirectory(), "app"); scanDirTracedLI(systemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM, 0);Copy the code

Continue calling scanPackageChildLI

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

try {
    scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
            currentTime, null);
} catch (PackageManagerException e) {
    errorCode = e.error;
    Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
Copy the code

You end up with scanPackageOnlyLI

Frameworks/base/services/core/Java/com/android/server/PM/PackageManagerService. Java / / if the system app is not the first time start Boolean or upgrade needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) ! = 0; if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final boolean extractNativeLibs = ! pkg.isLibrary(); derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Some system apps still use directory structure for native libraries // in which case we might end up not detecting abi solely based on apk // structure. Try to detect abi based on directory structure. if (isSystemApp(pkg) && ! pkg.isUpdatedSystemApp() && pkg.applicationInfo.primaryCpuAbi == null) { setBundledAppAbisAndRoots(pkg, pkgSetting); setNativeLibraryPaths(pkg, sAppLib32InstallDir); }Copy the code

If the System App is not started for the first time or upgraded, the derivePackageAbi is called

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
        boolean extractLibs)
                throws PackageManagerException {
    // Give ourselves some initial paths; we'll come back for another
    // pass once we've determined ABI below.
    setNativeLibraryPaths(pkg, sAppLib32InstallDir);

    // We shouldn't attempt to extract libs from system app when it was not updated.
    if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
        extractLibs = false;
    }

    final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
    final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;

    NativeLibraryHelper.Handle handle = null;
    try {
        handle = NativeLibraryHelper.Handle.create(pkg);
        // TODO(multiArch): This can be null for apps that didn't go through the
        // usual installation process. We can calculate it again, like we
        // do during install time.
        //
        // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
        // unnecessary.
        final File nativeLibraryRoot = new File(nativeLibraryRootStr);

        // Null out the abis so that they can be recalculated.
        pkg.applicationInfo.primaryCpuAbi = null;
        pkg.applicationInfo.secondaryCpuAbi = null;
        //如果app支持多种abi架构
        if (isMultiArch(pkg.applicationInfo)) {
            // Warn if we've set an abiOverride for multi-lib packages..
            // By definition, we need to copy both 32 and 64 bit libraries for
            // such packages.
            if (pkg.cpuAbiOverride != null
                    && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
                Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
            }

            int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
            int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
            //如果通过getprop拿到的"ro.product.cpu.abilist32"的abi存在
            if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
                if (extractLibs) {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
            //调用copyNativeBinariesForSupportedAbi查询nativeLibraryPath下面支持的abi,并进行so拷贝
                    abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                            nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
                            useIsaSpecificSubdirs);
                } else {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                    abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
                }
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }

            // Shared library native code should be in the APK zip aligned
            if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Shared library native lib extraction not supported");
            }

            maybeThrowExceptionForMultiArchCopy(
                    "Error unpackaging 32 bit native libs for multiarch app.", abi32);
            //如果通过getprop拿到的"ro.product.cpu.abilist64"的abi存在
            if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
                if (extractLibs) {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
                    //调用copyNativeBinariesForSupportedAbi查询nativeLibraryPath下面支持的abi,并进行so拷贝
                    abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                            nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
                            useIsaSpecificSubdirs);
                } else {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                    abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
                }
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }

            maybeThrowExceptionForMultiArchCopy(
                    "Error unpackaging 64 bit native libs for multiarch app.", abi64);

            if (abi64 >= 0) {
                // Shared library native libs should be in the APK zip aligned
                if (extractLibs && pkg.isLibrary()) {
                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                            "Shared library native lib extraction not supported");
                }
                //如果支持64位so,则primaryCpuAbi被赋值为prop中对应的abi属性
                pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
            }

            if (abi32 >= 0) {
                final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                if (abi64 >= 0) {
                    if (pkg.use32bitAbi) {
                        //如果同时支持32bit和64bit的so,并且当前的app使用的是32bit so
                        //则将上面的64abi作为secondaryCpuAbi,32abi作为首要的CpuAbi
                        pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
                        pkg.applicationInfo.primaryCpuAbi = abi;
                    } else {
                        pkg.applicationInfo.secondaryCpuAbi = abi;
                    }
                } else {
                    pkg.applicationInfo.primaryCpuAbi = abi;
                }
            }
        } else {
            //如果app不支持多种abi架构,adbList被赋值为当前系统支持的ABIS
            String[] abiList = (cpuAbiOverride != null) ?
                    new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;

            // Enable gross and lame hacks for apps that are built with old
            // SDK tools. We must scan their APKs for renderscript bitcode and
            // not launch them if it's present. Don't bother checking on devices
            // that don't have 64 bit support.
            boolean needsRenderScriptOverride = false;
            if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
                    NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
                abiList = Build.SUPPORTED_32_BIT_ABIS;
                needsRenderScriptOverride = true;
            }

            final int copyRet;
            if (extractLibs) {
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
                copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                        nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
            } else {
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
            }
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

            if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Error unpackaging native libs for app, errorCode=" + copyRet);
            }

            if (copyRet >= 0) {
                // Shared libraries that have native libs must be multi-architecture
                if (pkg.isLibrary()) {
                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                            "Shared library with native libs must be multiarch");
                }
                //如果nativeLib下面存在abi,则将对应的abi赋值
                pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
            } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
                //如果没有native lib,但是cpuAbiOverride不为null,则赋值cpuAbiOverride
                pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
            } else if (needsRenderScriptOverride) {
                //否则将abi list中第一项赋值
                pkg.applicationInfo.primaryCpuAbi = abiList[0];
            }
        }
    } catch (IOException ioe) {
        Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
    } finally {
        IoUtils.closeQuietly(handle);
    }

    // Now that we've calculated the ABIs and determined if it's an internal app,
    // we will go ahead and populate the nativeLibraryPath.
    setNativeLibraryPaths(pkg, sAppLib32InstallDir);
}
Copy the code

As you can see from the code above, the derivePackageAbi rules are as follows:

  1. If the app supports multi-abi architecture, if 64-bit so is supported, then the corresponding abi is assigned to primaryCpuAbi, if only 32-bit so is supported, then the corresponding abi is assigned to primaryCpuAbi. If both 64-bit so and 32-bit so are supported, If 32bit so is currently used, primaryCpuAbi is assigned 32abi and secondaryCpuAbi is assigned 64abi

  2. If the app does not support multi-abi architecture, the current abi is searched under the current nativeLibraryPath. If the abi exists, the corresponding abi in abiList is assigned to primaryCpuAbi. If the abi does not exist, If cpuAbiOverride is not null, the value of cpuAbiOverride is assigned to primaryCpuAbi. Otherwise, primaryCpuAbi is the first existing abi in abiList

If it is a system app, and through the above don’t get the corresponding primaryCpuAbi derivePackageAbi method, by this time, the PMS to call setBundledAppAbisAndRoots method

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private static void setBundledAppAbisAndRoots(PackageParser.Package pkg,
                                       PackageSetting pkgSetting) {
    final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());

    // If "/system/lib64/apkname" exists, assume that is the per-package
    // native library directory to use; otherwise use "/system/lib/apkname".
    final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
    setBundledAppAbi(pkg, apkRoot, apkName);
    // pkgSetting might be null during rescan following uninstall of updates
    // to a bundled app, so accommodate that possibility.  The settings in
    // that case will be established later from the parsed package.
    //
    // If the settings aren't null, sync them up with what we've just derived.
    // note that apkRoot isn't stored in the package settings.
    if (pkgSetting != null) {
        pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
        pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
    }
}
Copy the code
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { final File codeFile = new File(pkg.codePath); final boolean has64BitLibs; final boolean has32BitLibs; If (isApkFile(codeFile)) {// Monolithic Install // check whether the 32bit and 64bit lib exists in the current apK directory has64BitLibs = (new File(apkRoot,  new File(LIB64_DIR_NAME, apkName).getPath())).exists(); has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists(); } else { // Cluster install final File rootDir = new File(codeFile, LIB_DIR_NAME); if (! ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS) && ! TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) { final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]); has64BitLibs = (new File(rootDir, isa)).exists(); } else { has64BitLibs = false; } if (! ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS) && ! TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) { final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); has32BitLibs = (new File(rootDir, isa)).exists(); } else { has32BitLibs = false; } } if (has64BitLibs && ! has32BitLibs) { // The package has 64 bit libs, but not 32 bit libs. Its primary // ABI should be 64 bit. We can safely assume here that the bundled // native libraries // Correspond to the most preferred ABI in the list. // If it is supported by 64bit lib and not supported by 32bit lib, PrimaryCpuAbi as the current 64 - bit systems support the lib list first PKG. ApplicationInfo. PrimaryCpuAbi = Build. SUPPORTED_64_BIT_ABIS [0]. pkg.applicationInfo.secondaryCpuAbi = null; } else if (has32BitLibs && ! Has64BitLibs) {// The package has 32bit libs but not 64 bit libs. Its primary // ABI should be 32bit 64-bit lib does not support 64-bit lib. PrimaryCpuAbi as current system support 32 bit lib list first PKG. ApplicationInfo. PrimaryCpuAbi = Build. SUPPORTED_32_BIT_ABIS [0]. pkg.applicationInfo.secondaryCpuAbi = null; } else if (has32BitLibs && has64BitLibs) {// The application has both 64 and 32 bits bundled libraries. We check // here that the app declares multiArch support, and warn if it doesn't. // // We will be lenient here and record both ABIs. The primary will be the // ABI that's higher  on the list, i.e, a device that's configured to prefer // 64 bit apps will see a 64 bit primary ABI, if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) { Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch."); } the if (VMRuntime is64BitInstructionSet (getPreferredInstructionSet ())) {/ / if the current operation environment of the virtual machine is 64 bit, primaryCpuAbi as the current 64 - bit systems support The first ABI in the lib list, SecondaryCpuAbi support the current system of 32 bit lib list first PKG. ApplicationInfo. PrimaryCpuAbi = Build. SUPPORTED_64_BIT_ABIS [0]. pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; } else {// If the vm is running in a 32-bit environment, primaryCpuAbi is the first ABI in the IDE32bit lib list supported by the system. SecondaryCpuAbi support the current system of 64 - bit lib list first PKG. ApplicationInfo. PrimaryCpuAbi = Build. SUPPORTED_32_BIT_ABIS [0]. pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; } } else { pkg.applicationInfo.primaryCpuAbi = null; pkg.applicationInfo.secondaryCpuAbi = null; }}Copy the code

According to the code analysis above, the rules of setBundledAppAbi are as follows:

  1. If the system app supports 64-bit lib but does not support 32-bit lib, primaryCpuAbi is the first in the list of 64-bit abi supported by the system
  2. If the system app does not support 64-bit lib but supports 32-bit lib, primaryCpuAbi is the first in the list of 32-bit abi supported by the system
  3. If the system app supports both 64-bit and 32bit lib, check whether the vm running environment is 64-bit

PrimaryCpuAbi indicates the first byte in the 64-bit ABI list supported by the system, and secondaryCpuAbi indicates the first byte in the 32-bit ABI list supported by the system. If the 32-bit ABI is supported, PrimaryCpuAbi is the first byte in the 32-bit ABI list supported by the system, and secondaryCpuAbi is the first byte in the 64-bit ABI list supported by the system.

At this point, we have a pretty good idea of how app processes run in 32-bit or 64-bit processes at startup

Going back to the problem we had at the beginning, how do we integrate if the vendor can only provide 32bit SO

Although the 64bit APP is downward compatible with 32bit, the system cannot load both 32bit and 64bit SO at the same time when loading SO. If all the SO that the APP needs to load is 32bit, it will use 32bit to load SO. If one of the so that needs to be loaded is 64bit SO, So must be loaded in 64bit mode. Therefore, based on the above analysis, we can only choose to run the app in 32 processes

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libbytelink
LOCAL_SRC_FILES := libs/armeabi-v7a/libbytelink.so
LOCAL_32_BIT_ONLY := true
include $(BUILD_PREBUILT)
Copy the code
LOCAL_32_BIT_ONLY := true // Will be confused to disable LOCAL_PROGUARD_ENABLED:= disabled LOCAL_JNI_SHARED_LIBRARIES := libbytelink LOCAL_REQUIRED_MODULES := libbytelink include $(BUILD_PACKAGE)Copy the code

For the pre-compiled so, we need to put the corresponding so file under libs/armeabi-v7a/ directory, and then add LOCAL_32_BIT_ONLY := true, This will make sure that so will be compiled in 32bit and we also need to specify 32bit compilation for our final app

darwin:/ # ps -A | grep zygote root 730 1 5509808 185960 poll_schedule_timeout 0 S zygote64 root 731 1 1821052 166792 poll_schedule_timeout 0 S zygote webview_zygote 2753 731 1822560 85128 poll_schedule_timeout 0 S webview_zygote darwin:/  # ps -A | grep screenmirrorclient system 3655 731 1418008 98028 ep_poll 0 S com.bytedance.screenmirrorclientCopy the code

As you can see, now com. Bytedance. Screenmirrorclient process from 731 the zygote folk, at this point, the app can use normal load 32 bit of third party so provides.