Android извлекает локальную библиотеку Apk

краткое описание

  В процессе установки Apk будет задействовано извлечение файлов локальной библиотеки. В каких условиях и как он добывается, нам предстоит разобраться. В то же время в этом процессе первичному ABI и вторичному ABI, необходимым для анализа пакета, также будут присвоены значения.
  Этот связанный код находится в методе DeerePackageAbi() класса PackageAbiHelperImpl. Давайте углубимся в код и посмотрим.

PackageAbiHelperImpl DerivatePackageAbi()

  Код DeerePackageAbi() довольно длинный, давайте рассмотрим его по частям, первый абзац:

    public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg,
            boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)
            throws PackageManagerException {
    
    
        // Give ourselves some initial paths; we'll come back for another
        // pass once we've determined ABI below.
        String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(pkg);
        String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg);
        final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths(
                new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
                appLib32InstallDir, pkg.getPath(),
                pkg.getBaseApkPath(), pkg.isSystem(),
                isUpdatedSystemApp);

        final boolean extractLibs = shouldExtractLibs(pkg, isUpdatedSystemApp);

        final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
        final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
        final boolean onIncremental = isIncrementalPath(pkg.getPath());

        String primaryCpuAbi = null;
        String secondaryCpuAbi = null;

        NativeLibraryHelper.Handle handle = null;
        try {
    
    
            handle = AndroidPackageUtils.createNativeLibraryHandle(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);

  Сначала получите исходный ABI основного ЦП и ABI вторичного ЦП анализируемого пакета. ABI — это двоичный интерфейс приложения, связанный с архитектурой ЦП. Различные архитектуры ЦП поддерживают разные наборы команд.
  Посмотрите на соответствующую связь между ABI и архитектурой инструкций в Android. Соответствующая связь находится в статической переменной-члене ABI_TO_INSTRUCTION_SET_MAP класса VMRuntime. Ключ — ABI, а значение — соответствующий набор инструкций.

    private static final Map<String, String> ABI_TO_INSTRUCTION_SET_MAP
            = new HashMap<String, String>(16);
    static {
    
    
        ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm");
        ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm");
        ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips");
        ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64");
        ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86");
        ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64");
        ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64");
        ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a-hwasan", "arm64");
    }

  Затем вызовите метод DerivNativeLibraryPaths(), чтобы сгенерировать объект NativeLibraryPaths InitialLibraryPaths. Посмотрите на это имя класса, чтобы узнать, что это путь к собственной библиотеке.

вывестиNativeLibraryPaths()

  Взгляните на код его метода:

    private static NativeLibraryPaths deriveNativeLibraryPaths(final Abis abis,
            final File appLib32InstallDir, final String codePath, final String sourceDir,
            final boolean isSystemApp, final boolean isUpdatedSystemApp) {
    
    
        final File codeFile = new File(codePath);
        final boolean bundledApp = isSystemApp && !isUpdatedSystemApp;

        final String nativeLibraryRootDir;
        final boolean nativeLibraryRootRequiresIsa;
        final String nativeLibraryDir;
        final String secondaryNativeLibraryDir;

        if (isApkFile(codeFile)) {
    
    
            // Monolithic install
            if (bundledApp) {
    
    
                // 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(sourceDir);
                final boolean is64Bit = VMRuntime.is64BitInstructionSet(
                        getPrimaryInstructionSet(abis));

                // This is a bundled system app so choose the path based on the ABI.
                // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
                // is just the default path.
                final String apkName = deriveCodePathName(codePath);
                final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
                nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
                        apkName).getAbsolutePath();

                if (abis.secondary != null) {
    
    
                    final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
                    secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
                            secondaryLibDir, apkName).getAbsolutePath();
                } else {
    
    
                    secondaryNativeLibraryDir = null;
                }
            } else {
    
    
                final String apkName = deriveCodePathName(codePath);
                nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
                        .getAbsolutePath();
                secondaryNativeLibraryDir = null;
            }

            nativeLibraryRootRequiresIsa = false;
            nativeLibraryDir = nativeLibraryRootDir;
        } else {
    
    
            // Cluster install
            nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
            nativeLibraryRootRequiresIsa = true;

            nativeLibraryDir = new File(nativeLibraryRootDir,
                    getPrimaryInstructionSet(abis)).getAbsolutePath();

            if (abis.secondary != null) {
    
    
                secondaryNativeLibraryDir = new File(nativeLibraryRootDir,
                        VMRuntime.getInstructionSet(abis.secondary)).getAbsolutePath();
            } else {
    
    
                secondaryNativeLibraryDir = null;
            }
        }
        return new NativeLibraryPaths(nativeLibraryRootDir, nativeLibraryRootRequiresIsa,
                nativeLibraryDir, secondaryNativeLibraryDir);
    }

  Видно, что его значение связано со способом установки Apk. Один из них — монолитная установка (установка всего пакета), а другой — кластерная установка (кластерная установка).
  Результаты getPath() анализируемого пакета, полученные двумя методами установки, различны.При установке монолитной установки getPath() — это путь к файлу Apk; для установки кластера getPath() — это каталог установки АПК-файл.
  1. Монолитная установка.
  1. BundleApp имеет значение true, что означает, что это системное приложение, а не находящееся в состоянии обновления.
  apkroot — это корневой каталог, полученный в соответствии с расположением apk.
  VMRuntime.is64BitInstructionSet(getPrimaryInstructionSet(abis)) Указывает, являются ли поддерживаемые инструкции 64-разрядными. Сначала взгляните на getPrimaryInstructionSet(abis), который находится в классе InstructionSets:

    public static String getPrimaryInstructionSet(PackageAbiHelper.Abis abis) {
    
    
        if (abis.primary == null) {
    
    
            return getPreferredInstructionSet();
        }

        return VMRuntime.getInstructionSet(abis.primary);
    }

    private static final String PREFERRED_INSTRUCTION_SET =
            VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);    
    public static String getPreferredInstructionSet() {
    
    
        return PREFERRED_INSTRUCTION_SET;
    }

  Вы можете видеть, что если abis.primary имеет значение null, abi не настроен. Он получит метод getPreferredInstructionSet(), который извлекает инструкции, поддерживаемые системой. Массив Build.SUPPORTED_ABIS — это ABI, поддерживаемый текущей системой, возьмите первый, а затем преобразуйте его в поддерживаемую инструкцию с помощью метода VMRuntime.getInstructionSet(). Его преобразование осуществляется с помощью ABI_TO_INSTRUCTION_SET_MAP, который мы представили ранее.
  По названию также можно догадаться: чем выше последовательность массива Build.SUPPORTED_ABIS, тем больше ABI предпочитает система.
  Если значение abis.primary не равно нулю, получите поддерживаемые инструкции непосредственно с помощью метода VMRuntime.getInstructionSet().
  Посмотрите, как реализован VMRuntime.is64BitInstructionSet().

    public static boolean is64BitInstructionSet(String instructionSet) {
    
    
        return "arm64".equals(instructionSet) ||
                "x86_64".equals(instructionSet) ||
                "mips64".equals(instructionSet);
    }

  Он равен любому из «arm64», «x86_64» и «mips64», что означает, что поддерживаются 64-битные инструкции.
  Затем при установке Monolithic получите apkName. Имя apkName здесь без суффикса «.apk».
  Затем установите значение libDir в зависимости от того, является ли это 64-битной инструкцией. libDir — это «lib64», если 64-разрядная версия, в противном случае — «lib».
  Затем создайте ownLibraryRootDir. Например, если путь к установленному файлу APK — «/system/app/exam.apk», значение ownLibraryRootDir после создания будет следующим: поддержка 64-битных инструкций, «/system/lib64/exam»; не поддержка 64-битных инструкций. Инструкция, "/system/lib/exam".
  Затем перейдите к оценке: abis. Secondary имеет значение NULL, SecondaryNativeLibraryDir имеет значение NULL. Если оно не равно нулю, его каталог соответствует NativeLibraryRootDir, то есть NativeLibraryRootDir — это lib64, а SecondaryNativeLibraryDir — это lib; то есть NativeLibraryRootDir — это lib, а SecondaryNativeLibraryDir — это lib64.
  2. Если это не системный пакет или системный пакет, но находится в состоянии обновления,
  то если это Монолитная установка, то это не системный пакет или системный пакет, а находится в состоянии обновления, что противоположно ( системное приложение, не находящееся в состоянии обновления). nativeLibraryRootDir — это «/data/app-lib» + имя файла Apk. вторичныйNativeLibraryDir = null.
  В двух приведенных выше случаях ownLibraryRootRequiresIsa = false означает, что при извлечении библиотеки не используется подкаталог,ativeLibraryDir =nativeLibraryRootDir. Установите каталог локальной библиотеки таким же, как корневой каталог библиотеки.

  2. Монолитная установка.
  Если приведенное выше описание представляет собой монолитную установку, а если это кластерная установка, посмотрите вниз:
  здесь codeFile — это каталог, в котором находится установленный Apk. nativeLibraryRootDir — это codeFile + «/lib». nativeLibraryRootRequiresIsa = true. nativeLibraryRootRequiresIsa означает, следует ли использовать подкаталоги при извлечении библиотек.
  getPrimaryInstructionSet(abis) также упоминался выше, его значением могут быть все наборы инструкций, здесь это может быть один из Arm, Arm64, x86_64, x86, mips64, mips. Таким образом, также появляется значение ownLibraryDir, которое представляет собой ownLibraryRootDir + имя набора инструкций.
  Аналогично, если abis. Secondary имеет значение NULL, SecondaryNativeLibraryDir имеет значение NULL. В противном случае, SecondaryNativeLibraryDir получается в соответствии с набором инструкций, соответствующим abis. Secondary, то есть NativeLibraryRootDir + имя вторичного набора инструкций.
  Например, при установке кластера местоположение файла APK – "/data/app/com.example/exam.apk", abis.primary — "arm64-v8a", а ownLibraryRootDir — "/data/app/com.example/lib". , ownLibraryDir "/data/app/com.example/lib/arm64".
  Наконец, создается и возвращается объект NativeLibraryPaths.
  Видно, что функция DerivNativeLibraryPaths() в основном предназначена для получения местоположения основного каталога и местоположения вторичного каталога локальной библиотеки и инкапсуляции их в NativeLibraryPaths.
  На этом работа метода DeerNativeLibraryPaths() завершается.

  Вернитесь к DeerePackageAbi(), а затем посмотрите,
  mustExtractLibs(), чтобы узнать, нужно ли извлекать собственный код. взглянем:

    private boolean shouldExtractLibs(AndroidPackage pkg, boolean isUpdatedSystemApp) {
    
    
        // We shouldn't extract libs if the package is a library or if extractNativeLibs=false
        boolean extractLibs = !AndroidPackageUtils.isLibrary(pkg) && pkg.isExtractNativeLibs();
        // We shouldn't attempt to extract libs from system app when it was not updated.
        if (pkg.isSystem() && !isUpdatedSystemApp) {
    
    
            extractLibs = false;
        }
        return extractLibs;
    }

  Если анализируемый пакет является библиотекой, его невозможно извлечь. Это не библиотека, и можно извлечь экстрактNativeLibs=true пакета синтаксического анализа. Однако пакет синтаксического анализа является системным пакетом, и его нельзя извлечь, если он не находится в состоянии обновления.
  Что такое библиотека, так это то, что тег «static-library» или «library» (той же глубины, что и тег активности) настроен в файле манифеста. ExtractNativeLibs пакета синтаксического анализа предназначен для настройки атрибута метки «приложение» ExtractNativeLibs в файле манифеста. Если конфигурация отсутствует, значение по умолчанию — true.
  Вернитесь к DeerePackageAbi() и посмотрите на него, и
  получите значения локальных переменных ownLibraryRootStr и useIsaSpecificSubdirs через полученные ранее InitialLibraryPaths. Здесь обратите внимание, что основные четыре значения переменных были получены через DerivateNativeLibraryPaths(), а теперь используются только две переменные. nativeLibraryRootStr — имя корневого каталога собственной библиотеки. Он также используется при извлечении файла локальной библиотеки ниже.
  onIncremental указывает, находится ли он в инкрементной файловой системе.
  Вызовите AndroidPackageUtils.createNativeLibraryHandle(pkg), чтобы создать дескриптор объекта NativeLibraryHelper.Handle.

    public static NativeLibraryHelper.Handle createNativeLibraryHandle(AndroidPackage pkg)
            throws IOException {
    
    
        return NativeLibraryHelper.Handle.create(
                AndroidPackageUtils.getAllCodePaths(pkg),
                pkg.isMultiArch(),
                pkg.isExtractNativeLibs(),
                pkg.isDebuggable()
        );
    }

  AndroidPackageUtils.getAllCodePaths(pkg) получает все местоположения Apk приложения. Большинство приложений представляют собой Apk, но метод разделения apk будет иметь и другие разделенные APK, помимо основного Apk. Итак, здесь будут все местоположения APK.
  Значение pkg.isMultiArch() берется из атрибута multiArch тега application в файле манифеста. Если не настроено, значение по умолчанию — false.
  Значение pkg.isDebuggable() берется из атрибута debuggable тега "application" в файле манифеста. Если не настроено, значение по умолчанию — false.
  Взгляните еще раз на функцию create() Handle:

        public static Handle create(List<String> codePaths, boolean multiArch,
                boolean extractNativeLibs, boolean debuggable) throws IOException {
    
    
            final int size = codePaths.size();
            final String[] apkPaths = new String[size];
            final long[] apkHandles = new long[size];
            for (int i = 0; i < size; i++) {
    
    
                final String path = codePaths.get(i);
                apkPaths[i] = path;
                apkHandles[i] = nativeOpenApk(path);
                if (apkHandles[i] == 0) {
    
    
                    // Unwind everything we've opened so far
                    for (int j = 0; j < i; j++) {
    
    
                        nativeClose(apkHandles[j]);
                    }
                    throw new IOException("Unable to open APK: " + path);
                }
            }

            return new Handle(apkPaths, apkHandles, multiArch, extractNativeLibs, debuggable);
        }

  Прокрутите местоположение файла Apk в codePaths, вызовите ownOpenApk(path), чтобы получить значение длинного типа. Затем инкапсулируется в объект Handle.
  Длинное значение, полученное методом ownOpenApk(path), на самом деле является адресом объекта ZipFileRO на уровне C++. Объект ZipFileRO имеет переменную-член ZipArchiveHandle mHandle, которая указывает на открытый файл apk, и операция с файлом apk также выполняется через mHandle.
  Возвращаясь к методу DeriverPackageAbi(), NativeLibraryRoot — это файл, созданный функцией ownLibraryRootStr.

Затем посмотрите на второй код DerivatePackageAbi():

            // Null out the abis so that they can be recalculated.
            primaryCpuAbi = null;
            secondaryCpuAbi = null;
            if (pkg.isMultiArch()) {
    
    
                int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
                int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
                if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
    
    
                    if (extractLibs) {
    
    
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
                        abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                                nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
                                useIsaSpecificSubdirs, onIncremental);
                    } 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 && AndroidPackageUtils.isLibrary(pkg) && 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);

                if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
    
    
                    if (extractLibs) {
    
    
                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
                        abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                                nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
                                useIsaSpecificSubdirs, onIncremental);
                    } 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 && AndroidPackageUtils.isLibrary(pkg)) {
    
    
                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                                "Shared library native lib extraction not supported");
                    }
                    primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
                }

                if (abi32 >= 0) {
    
    
                    final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                    if (abi64 >= 0) {
    
    
                        if (pkg.isUse32BitAbi()) {
    
    
                            secondaryCpuAbi = primaryCpuAbi;
                            primaryCpuAbi = abi;
                        } else {
    
    
                            secondaryCpuAbi = abi;
                        }
                    } else {
    
    
                        primaryCpuAbi = abi;
                    }
                }

  Некоторые пакеты синтаксического анализа поддерживают несколько архитектур, а другие — нет. Далее необходимо проанализировать, поддерживает или не поддерживает пакет анализа мультиархитектурную обработку. Этот второй фрагмент кода предназначен для работы с несколькими архитектурами.

Пакет анализа поддерживает несколько архитектур

  Build.SUPPORTED_32_BIT_ABIS.length > 0 означает, что система поддерживает 32-битный ABI. В случае, если пакет синтаксического анализа может извлечь библиотеку, вызовите NativeLibraryHelper.copyNativeBinariesForSupportedAbi(), если нет, вызовите NativeLibraryHelper.findSupportedAbi().
  Что возвращает abi32 — это последовательность массива Build.SUPPORTED_32_BIT_ABIS. Это последовательность при условии, что ABI в массиве соответствует ABI файла «.so» в файле Apk. При этом чем меньше индекс последовательности массива Build.SUPPORTED_32_BIT_ABIS, тем выше приоритет.
  В то же время, когда Build.SUPPORTED_64_BIT_ABIS.length > 0, логика та же, что и 32. Итак, когда мы понимаем одно, мы понимаем и другое.
  Что делает NativeLibraryHelper.copyNativeBinariesForSupportedAbi()? Здесь он извлекает все файлы собственной библиотеки, соответствующие последовательности массива ABI. Подробности смотрите в анализе ниже.
  Если abi64>0, установите PrimaryCpuAbi в качестве поддержки системы, а ABI
  pkg.isUse32BitAbi(), который также существует в Apk, также взят из атрибута тега «приложение» use32bitAbi в файле манифеста конфигурации пакета синтаксического анализа. Если нет конфигурации, значение по умолчанию — false.
  Таким образом, пакет синтаксического анализа настраивается с помощью pkg.isUse32BitAbi(), затем он должен установить для основного ABI PrimaryCpuAbi значение 32-битного ABI, а для вторичного ABI SecondaryCpuAbi — его исходный основной ABI.
  Если обнаружен только 32-битный ABI, на этом этапе установите для основного ABI PrimaryCpuAbi значение 32-битного ABI, а для вторичного ABI SecondaryCpuAbi не задано.
  Давайте посмотрим на код NativeLibraryHelper.copyNativeBinariesForSupportedAbi(), как он извлекает файл локальной библиотеки.

Извлеките репозиторий двоичного кода

    public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
            String[] abiList, boolean useIsaSubdir, boolean isIncremental) throws IOException {
    
    
        /*
         * If this is an internal application or our nativeLibraryPath points to
         * the app-lib directory, unpack the libraries if necessary.
         */
        int abi = findSupportedAbi(handle, abiList);
        if (abi < 0) {
    
    
            return abi;
        }

        /*
         * If we have a matching instruction set, construct a subdir under the native
         * library root that corresponds to this instruction set.
         */
        final String supportedAbi = abiList[abi];
        final String instructionSet = VMRuntime.getInstructionSet(supportedAbi);
        final File subDir;
        if (useIsaSubdir) {
    
    
            subDir = new File(libraryRoot, instructionSet);
        } else {
    
    
            subDir = libraryRoot;
        }

        if (isIncremental) {
    
    
            int res =
                    incrementalConfigureNativeBinariesForSupportedAbi(handle, subDir, supportedAbi);
            if (res != PackageManager.INSTALL_SUCCEEDED) {
    
    
                // TODO(b/133435829): the caller of this function expects that we return the index
                // to the supported ABI. However, any non-negative integer can be a valid index.
                // We should fix this function and make sure it doesn't accidentally return an error
                // code that can also be a valid index.
                return res;
            }
            return abi;
        }

        // For non-incremental, use regular extraction and copy
        createNativeLibrarySubdir(libraryRoot);
        if (subDir != libraryRoot) {
    
    
            createNativeLibrarySubdir(subDir);
        }

        // Even if extractNativeLibs is false, we still need to check if the native libs in the APK
        // are valid. This is done in the native code.
        int copyRet = copyNativeBinaries(handle, subDir, supportedAbi);
        if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
    
    
            return copyRet;
        }

        return abi;
    }

  Сначала вызовите findSupportedAbi(), чтобы получить последовательность, подходящую для массива ABI abiList в файле apk. Взгляните на ее код:

найтиПоддерживаемыйAbi()

    public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
    
    
        int finalRes = NO_NATIVE_LIBRARIES;
        for (long apkHandle : handle.apkHandles) {
    
    
            final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
            if (res == NO_NATIVE_LIBRARIES) {
    
    
                // No native code, keep looking through all APKs.
            } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
    
    
                // Found some native code, but no ABI match; update our final
                // result if we haven't found other valid code.
                if (finalRes < 0) {
    
    
                    finalRes = INSTALL_FAILED_NO_MATCHING_ABIS;
                }
            } else if (res >= 0) {
    
    
                // Found valid native code, track the best ABI match
                if (finalRes < 0 || res < finalRes) {
    
    
                    finalRes = res;
                }
            } else {
    
    
                // Unexpected error; bail
                return res;
            }
        }
        return finalRes;
    }

  Во всех файлах дескриптора выполните метод ownFindSupportedAbi(), где параметр apkHandle соответствует объекту ZipFileRO уровня C++, supportAbis — это ABI, поддерживаемый системой, а также возможность отладки handle.debuggable.
  nativeFindSupportedAbi() реализован в файле Platform\frameworks\base\core\jni\com_android_internal_content_NativeLibraryHelper.cpp, а соответствующий метод — это метод com_android_internal_content_NativeLibraryHelper_findSupportedAbi():

static jint
com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,
        jlong apkHandle, jobjectArray javaCpuAbisToSearch, jboolean debuggable)
{
    
    
    return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch, debuggable);
}

static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray,
        jboolean debuggable) {
    
    
    const int numAbis = env->GetArrayLength(supportedAbisArray);
    Vector<ScopedUtfChars*> supportedAbis;

    for (int i = 0; i < numAbis; ++i) {
    
    
        supportedAbis.add(new ScopedUtfChars(env,
            (jstring) env->GetObjectArrayElement(supportedAbisArray, i)));
    }

    ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
    if (zipFile == NULL) {
    
    
        return INSTALL_FAILED_INVALID_APK;
    }

    std::unique_ptr<NativeLibrariesIterator> it(
            NativeLibrariesIterator::create(zipFile, debuggable));
    if (it.get() == NULL) {
    
    
        return INSTALL_FAILED_INVALID_APK;
    }

    ZipEntryRO entry = NULL;
    int status = NO_NATIVE_LIBRARIES;
    while ((entry = it->next()) != NULL) {
    
    
        // We're currently in the lib/ directory of the APK, so it does have some native
        // code. We should return INSTALL_FAILED_NO_MATCHING_ABIS if none of the
        // libraries match.
        if (status == NO_NATIVE_LIBRARIES) {
    
    
            status = INSTALL_FAILED_NO_MATCHING_ABIS;
        }

        const char* fileName = it->currentEntry();
        const char* lastSlash = it->lastSlash();

        // Check to see if this CPU ABI matches what we are looking for.
        const char* abiOffset = fileName + APK_LIB_LEN;
        const size_t abiSize = lastSlash - abiOffset;
        for (int i = 0; i < numAbis; i++) {
    
    
            const ScopedUtfChars* abi = supportedAbis[i];
            if (abi->size() == abiSize && !strncmp(abiOffset, abi->c_str(), abiSize)) {
    
    
                // The entry that comes in first (i.e. with a lower index) has the higher priority.
                if (((i < status) && (status >= 0)) || (status < 0) ) {
    
    
                    status = i;
                }
            }
        }
    }

    for (int i = 0; i < numAbis; ++i) {
    
    
        delete supportedAbis[i];
    }

    return status;
}

  Функция findSupportedAbi(), реализованная на C++, в основном предназначена для сравнения ABI, поддерживаемого системой, с именем пакета в папке lib в файле Apk и получения последовательности системного ABI, поддерживаемого Apk. И в качестве результата возьмем последовательность с наименьшим.
  Взгляните на findSupportedAbi(), реализованный на C++. Массив ABI, передаваемый через уровень Java, помещается в переменную supportAbis, а apkHandle, передаваемый уровнем Java, преобразуется в указатель объекта ZipFileRO zipFile. Затем через zipFile вызовите NativeLibrariesIterator::create(zipFile, debuggable)) для создания указателя объекта NativeLibrariesIterator.
  Мы знаем, что файл Apk представляет собой Zip-файл, состоящий из множества файлов. В этом фрагменте для представления сжатого файла используется ZipFileRO, а отдельный файл внутри представлен ZipEntryRO. NativeLibrariesIterator::create(zipFile, debuggable)) в основном предназначен для поиска первого _ZipEntryRO, начинающегося с "lib/", через startIteration() из ZipFileRO. Позже, выполнив цикл метода next() объекта NativeLibrariesIterator, найдите один сжатый элемент, который начинается с «lib/» и заканчивается «.so».
  Среди них LastSlash — это позиция последнего «/» имени файла, чтобы можно было перехватить имя файла Abi за папкой lib, а затем сравнить его с одним из поддерживаемых массивов Abis, если они равны, оно находится . Затем запишите порядковый номер найденного поддерживаемого массива Abis, и если найдено более одного, будет выбрана наименьшая последовательность.
  Это логика findSupportedAbi(), реализованная на C++. Если вы не понимаете, вы можете посмотреть код еще раз.
  Давайте вернемся к методу findSupportedAbi() уровня Java. Если существует несколько Apks (разделенных apks), это также цикл для поиска того, который имеет наименьшую поддерживаемую последовательность массива Abis.
  Таким образом, мы можем вернуться к NativeLibraryHelper.copyNativeBinariesForSupportedAbi().
  Затем, если abi, возвращаемый функцией findSupportedAbi(), меньше 0, он будет считаться не найденным и будет выполнен непосредственный возврат.
  Если abi правильный, получите соответствующий поддерживаемый ABIAbi, а затем получите набор инструкций InstructionSet.
  Если используется подкаталог, каталог с именем набора команд будет добавлен в корневой каталог.
  Если он находится в инкрементной файловой системе, вам необходимо вызвать службу инкрементной файловой системы для обработки создания соответствующего файла и записи данных. Затем верните результат.
  Если это не инкрементная файловая система, создается каталог LibraryRoot. Если subDir и LibraryRoot различаются, также создается SubDir.
  Затем вызовите метод copyNativeBinaries(), чтобы скопировать библиотеку в каталог subDir.

Скопируйте файлы библиотеки в каталог

  Его реализация находится в методе copyNativeBinaries(), посмотрите:

    public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
    
    
        for (long apkHandle : handle.apkHandles) {
    
    
            int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
                    handle.extractNativeLibs, handle.debuggable);
            if (res != INSTALL_SUCCEEDED) {
    
    
                return res;
            }
        }
        return INSTALL_SUCCEEDED;
    }

  Вызывается цикл для переноса всех файлов Apk в каталогsharedLibraryDir согласно параметру handle.extractNativeLibs через ownCopyNativeBinaries().
  Взгляните на метод ownCopyNativeBinaries(), который реализован в файле Platform\frameworks\base\core\jni\com_android_internal_content_NativeLibraryHelper.cpp, и соответствующий метод — это метод com_android_internal_content_NativeLibraryHelper_copyNativeBinaries():

static jint
com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
        jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
        jboolean extractNativeLibs, jboolean debuggable)
{
    
    
    void* args[] = {
    
     &javaNativeLibPath, &extractNativeLibs };
    return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
            copyFileIfChanged, reinterpret_cast<void*>(args));
}

  Видно, что он вызывает метод iterateOverNativeFiles():

iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi,
                       jboolean debuggable, iterFunc callFunc, void* callArg) {
    
    
    ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
    if (zipFile == NULL) {
    
    
        return INSTALL_FAILED_INVALID_APK;
    }

    std::unique_ptr<NativeLibrariesIterator> it(
            NativeLibrariesIterator::create(zipFile, debuggable));
    if (it.get() == NULL) {
    
    
        return INSTALL_FAILED_INVALID_APK;
    }

    const ScopedUtfChars cpuAbi(env, javaCpuAbi);
    if (cpuAbi.c_str() == NULL) {
    
    
        // This would've thrown, so this return code isn't observable by
        // Java.
        return INSTALL_FAILED_INVALID_APK;
    }
    ZipEntryRO entry = NULL;
    while ((entry = it->next()) != NULL) {
    
    
        const char* fileName = it->currentEntry();
        const char* lastSlash = it->lastSlash();

        // Check to make sure the CPU ABI of this file is one we support.
        const char* cpuAbiOffset = fileName + APK_LIB_LEN;
        const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset;

        if (cpuAbi.size() == cpuAbiRegionSize && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
    
    
            install_status_t ret = callFunc(env, callArg, zipFile, entry, lastSlash + 1);

            if (ret != INSTALL_SUCCEEDED) {
    
    
                ALOGV("Failure for entry %s", lastSlash + 1);
                return ret;
            }
        }
    }

    return INSTALL_SUCCEEDED;
}

  В методе iterateOverNativeFiles() сначала найдите объект ZipFileRO через apkHandle. Затем создайте указатель объекта NativeLibrariesIterator, о котором упоминалось ранее. Далее необходимо просмотреть все файлы, начиная с «lib/» и заканчивая «.so», а затем сравнить их с параметром ABI javaCpuAbi и вызвать функцию callFunc, если они равны. Вот это copyFileIfChanged(), взгляните на него:

static install_status_t
copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
{
    
    
	……
    if (!extractNativeLibs) {
    
    
        // check if library is uncompressed and page-aligned
        if (method != ZipFileRO::kCompressStored) {
    
    
            ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
                fileName);
            return INSTALL_FAILED_INVALID_APK;
        }
		……
        return INSTALL_SUCCEEDED;
    }
    // Build local file path
    const size_t fileNameLen = strlen(fileName);
    char localFileName[nativeLibPath.size() + fileNameLen + 2];
    ……
    // Only copy out the native file if it's different.
    struct tm t;
    ZipUtils::zipTimeToTimespec(when, &t);
    const time_t modTime = mktime(&t);
    struct stat64 st;
    if (!isFileDifferent(localFileName, uncompLen, modTime, crc, &st)) {
    
    
        return INSTALL_SUCCEEDED;
    }
    ……
    int fd = mkstemp(localTmpFileName);	
    ……
    if (!zipFile->uncompressEntry(zipEntry, fd)) {
    
    
        ALOGE("Failed uncompressing %s to %s\n", fileName, localTmpFileName);
        close(fd);
        unlink(localTmpFileName);
        return INSTALL_FAILED_CONTAINER_ERROR;
    }
    ……
    // Finally, rename it to the final name.
    if (rename(localTmpFileName, localFileName) < 0) {
    
    
        ALOGE("Couldn't rename %s to %s: %s\n", localTmpFileName, localFileName, strerror(errno));
        unlink(localTmpFileName);
        return INSTALL_FAILED_CONTAINER_ERROR;
    } 
    return INSTALL_SUCCEEDED;
}       

  copyFileIfChanged() — здесь выполняется фактическое копирование библиотеки. Код довольно длинный, поэтому давайте выберем важный код.
  ExtractNativeLibs указывает, следует ли извлечь библиотеку. Если нет, он проверит, сжата ли библиотека. Если она сжата, он сообщит код ошибки INSTALL_FAILED_INVALID_APK.
  localFileName — имя файла извлеченной библиотеки. Также он сравнит файлы в localFileName и Apk с isFileDifferent(localFileName, uncompLen, modTime, crc, &st).Если они одинаковые, то значит нет необходимости выполнять копирование и они уже существуют. Но если он другой, его нужно скопировать.
  Когда мы видим, что он извлечен, он сначала извлекается во временный файл fd. Затем метод извлечения — zipFile->uncompressEntry(zipEntry, fd). Последний шаг — изменить имя извлеченного временного файла на имя целевого файла, таким образом извлекая библиотеку.
  Таким образом, copyNativeBinariesForSupportedAbi() наконец завершается.
  Теперь вернитесь к DeerPackageAbi() и продолжите последующий процесс.

  Третий фрагмент кода в DerivPackageAbi():

            } else {
    
    
                String[] abiList = (cpuAbiOverride != null)
                        ? new String[]{
    
    cpuAbiOverride} : Build.SUPPORTED_ABIS;

                // If an app that contains RenderScript has target API level < 21, it needs to run
                // with 32-bit ABI, and its APK file will contain a ".bc" file.
                // If an app that contains RenderScript has target API level >= 21, it can run with
                // either 32-bit or 64-bit ABI, and its APK file will not contain a ".bc" file.
                // Therefore, on a device that supports both 32-bit and 64-bit ABIs, we scan the app
                // APK to see if it has a ".bc" file. If so, we will run it with 32-bit ABI.
                // However, if the device only supports 64-bit ABI but does not support 32-bit ABI,
                // we will fail the installation for such an app because it won't be able to run.
                boolean needsRenderScriptOverride = false;
                // No need to check if the device only supports 32-bit
                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
                        && NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
    
    
                    if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
    
    
                        abiList = Build.SUPPORTED_32_BIT_ABIS;
                        needsRenderScriptOverride = true;
                    } else {
    
    
                        throw new PackageManagerException(
                                INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
                                "Apps that contain RenderScript with target API level < 21 are not "
                                        + "supported on 64-bit only platforms");
                    }
                }

                final int copyRet;
                if (extractLibs) {
    
    
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
                    copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                            nativeLibraryRoot, abiList, useIsaSpecificSubdirs, onIncremental);
                } 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 (AndroidPackageUtils.isLibrary(pkg)) {
    
    
                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                                "Shared library with native libs must be multiarch");
                    }
                    primaryCpuAbi = abiList[copyRet];
                } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES
                        && cpuAbiOverride != null) {
    
    
                    primaryCpuAbi = cpuAbiOverride;
                } else if (needsRenderScriptOverride) {
    
    
                    primaryCpuAbi = abiList[0];
                }
            }
        } catch (IOException ioe) {
    
    
            Slog.e(PackageManagerService.TAG, "Unable to get canonical file " + ioe.toString());
        } finally {
    
    
            IoUtils.closeQuietly(handle);
        }

  Этот третий фрагмент кода предназначен для обработки случая, когда пакет синтаксического анализа не поддерживает несколько архитектур.

Пакет синтаксического анализа не поддерживает несколько архитектур.

  Взгляните на DeriverPackageAbi(), который не поддерживает мультиархитектуру.
  Если параметр cpuAbiOverride не равен нулю, задайте для него массив abiList; если параметр cpuAbiOverride имеет значение NULL, задайте для abiList значение Build.SUPPORTED_ABIS — массив ABI, поддерживаемый системой.
  Далее следует разобраться с ситуацией с RenderScript. Если пакет анализа Apk имеет файл «.bc», он поддерживает RenderScript, но может работать только на 32-битном ABI. Итак, в этом случае abiList может выбрать только Build.SUPPORTED_32_BIT_ABIS. Если длина Build.SUPPORTED_32_BIT_ABIS меньше или равна 0, будет сообщено об исключении PackageManagerException.
  Затем, если вам нужно извлечь файл локальной библиотеки, вы также можете использовать метод NativeLibraryHelper.copyNativeBinariesForSupportedAbi(), см. выше . Если не извлекаете, перейдите сразу к NativeLibraryHelper.findSupportedAbi(handle, abiList), см. выше .
  Если результат copyRet >= 0, установите PrimaryCpuAbi = abiList[copyRet].
  В дополнение к предыдущему случаю, если copyRet == PackageManager.NO_NATIVE_LIBRARIES, пакет lib не существует в файле Apk. В настоящее время, если установлен cpuAbiOverride, установите PrimaryCpuAbi = cpuAbiOverride.
  В дополнение к двум вышеуказанным случаям, если приложение содержит RenderScript. Просто установите PrimaryCpuAbi = abiList[0].
  Мы обнаружили, что если несколько архитектур не поддерживаются, устанавливается только основнойCpuAbi, а дополнительный ABI-вторичныйCpuAbi не устанавливается.
  Дополнение кода для нескольких архитектур не поддерживается.

  В это время будет взят последний фрагмент кода DerivatePackageAbi():

        final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
        return new Pair<>(abis,
                deriveNativeLibraryPaths(abis, appLib32InstallDir,
                        pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(),
                        isUpdatedSystemApp));

  Главное — вызвать DerivNativeLibraryPaths() и инкапсулировать его в объект NativeLibraryPaths.
  Есть ли разница между этим звонком и первым звонком? Основная причина заключается в том, что ABI основного и вторичного процессора в abis различаются.
  В начале пакета синтаксического анализа все ABI основного и вторичного процессора пусты, теперь, после предыдущего поиска, значения определены. Теперь определите каталог основной библиотеки и каталог вторичной библиотеки.
Наконец, он инкапсулируется в объект Pair и возвращается.

Подведем итог

  1. Теперь мы знаем, при каких обстоятельствах файлы локальной библиотеки будут извлечены?
  Анализируемый пакет не является библиотекой, и анализируемый пакет имеет ExtractNativeLibs=true.
  При указанных выше условиях следует исключить еще одну ситуацию: пакет синтаксического анализа является системным пакетом и не может быть извлечен, если он не находится в состоянии обновления.
  2. Как извлечь файл локальной библиотеки?
  В основном это реализуется путем вызова copyNativeBinariesForSupportedAbi(). Общий смысл состоит в том, чтобы найти файл, начинающийся с «lib/», из сжатого пакета, а затем сравнить его с ABI, поддерживаемым системой, чтобы найти наиболее подходящий ABI, затем определить путь извлечения и, наконец, найти соответствующий ABI в APK-файл через соответствующий пакет ABI, извлеките его.

Supongo que te gusta

Origin blog.csdn.net/q1165328963/article/details/132391414
Recomendado
Clasificación