Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f8b6fe9 authored by Songchun Fan's avatar Songchun Fan
Browse files

Incremental native lib extraction

Basically we configure all the lib files inside Incremental Service,
e.g., create lib dirs, make lib files, extract original
lib file data from zip and then write data to the lib files on incfs.

Test: manual with incremental installation
BUG: b/136132412 b/133435829
Change-Id: I7544d2e78bcf3bdd76ce4c0766ec31ff13fd2011
parent 207454b9
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -103,4 +103,9 @@ interface IIncrementalService {
     * Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
     */
    void deleteStorage(int storageId);

    /**
     * Setting up native library directories and extract native libs onto a storage.
     */
    boolean configureNativeBinaries(int storageId, in @utf8InCpp String apkFullPath, in @utf8InCpp String libDirRelativePath, in @utf8InCpp String abi);
}
+33 −8
Original line number Diff line number Diff line
@@ -229,16 +229,41 @@ public final class IncrementalManager {
        if (linkedApkStorage == null) {
            throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent);
        }
        linkedApkStorage.makeDirectory(afterCodePathName);
        File[] files = beforeCodeFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isFile()) {
                String fileName = files[i].getName();
                apkStorage.makeLink(
                        fileName, linkedApkStorage, afterCodePathName + "/" + fileName);
        linkFiles(apkStorage, beforeCodeFile, "", linkedApkStorage, afterCodePathName);
        apkStorage.unBind(beforeCodePath);
    }

    /**
     * Recursively set up directories and link all the files from source storage to target storage.
     *
     * @param sourceStorage The storage that has all the files and directories underneath.
     * @param sourceAbsolutePath The absolute path of the directory that holds all files and dirs.
     * @param sourceRelativePath The relative path on the source directory, e.g., "" or "lib".
     * @param targetStorage The target storage that will have the same files and directories.
     * @param targetRelativePath The relative path to the directory on the target storage that
     *                           should have all the files and dirs underneath,
     *                           e.g., "packageName-random".
     * @throws IOException When makeDirectory or makeLink fails on the Incremental File System.
     */
    private void linkFiles(IncrementalStorage sourceStorage, File sourceAbsolutePath,
            String sourceRelativePath, IncrementalStorage targetStorage,
            String targetRelativePath) throws IOException {
        targetStorage.makeDirectory(targetRelativePath);
        final File[] entryList = sourceAbsolutePath.listFiles();
        for (int i = 0; i < entryList.length; i++) {
            final File entry = entryList[i];
            final String entryName = entryList[i].getName();
            final String sourceEntryRelativePath =
                    sourceRelativePath.isEmpty() ? entryName : sourceRelativePath + "/" + entryName;
            final String targetEntryRelativePath = targetRelativePath + "/" + entryName;
            if (entry.isFile()) {
                sourceStorage.makeLink(
                        sourceEntryRelativePath, targetStorage, targetEntryRelativePath);
            } else if (entry.isDirectory()) {
                linkFiles(sourceStorage, entry, sourceEntryRelativePath, targetStorage,
                        targetEntryRelativePath);
            }
        }
        apkStorage.unBind(beforeCodePath);
    }

    /**
+20 −0
Original line number Diff line number Diff line
@@ -416,4 +416,24 @@ public final class IncrementalStorage {
            return false;
        }
    }

    /**
     * Configure all the lib files inside Incremental Service, e.g., create lib dirs, create new lib
     * files, extract original lib file data from zip and then write data to the lib files on the
     * Incremental File System.
     *
     * @param apkFullPath Source APK to extract native libs from.
     * @param libDirRelativePath Target dir to put lib files, e.g., "lib" or "lib/arm".
     * @param abi Target ABI of the native lib files. Only extract native libs of this ABI.
     * @return Success of not.
     */
    public boolean configureNativeBinaries(String apkFullPath, String libDirRelativePath,
            String abi) {
        try {
            return mService.configureNativeBinaries(mId, apkFullPath, libDirRelativePath, abi);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
            return false;
        }
    }
}
+55 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static android.system.OsConstants.S_IRWXU;
import static android.system.OsConstants.S_IXGRP;
import static android.system.OsConstants.S_IXOTH;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -32,7 +33,12 @@ import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
import android.os.IBinder;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.incremental.IIncrementalService;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;
@@ -44,6 +50,7 @@ import java.io.Closeable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;

/**
@@ -481,10 +488,56 @@ public class NativeLibraryHelper {
     */
    private static int incrementalConfigureNativeBinariesForSupportedAbi(Handle handle,
            File libSubDir, String abi) {
        // TODO(b/136132412): implement this
        final String[] apkPaths = handle.apkPaths;
        if (apkPaths == null || apkPaths.length == 0) {
            Slog.e(TAG, "No apks to extract native libraries from.");
            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
        }

        final IBinder incrementalService = ServiceManager.getService(Context.INCREMENTAL_SERVICE);
        if (incrementalService == null) {
            //TODO(b/133435829): add incremental specific error codes
            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
        }
        final IncrementalManager incrementalManager = new IncrementalManager(
                IIncrementalService.Stub.asInterface(incrementalService));
        final File apkParent = new File(apkPaths[0]).getParentFile();
        IncrementalStorage incrementalStorage =
                incrementalManager.openStorage(apkParent.getAbsolutePath());
        if (incrementalStorage == null) {
            Slog.e(TAG, "Failed to find incremental storage");
            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
        }

        String libRelativeDir = getRelativePath(apkParent, libSubDir);
        if (libRelativeDir == null) {
            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
        }

        for (int i = 0; i < apkPaths.length; i++) {
            if (!incrementalStorage.configureNativeBinaries(apkPaths[i], libRelativeDir, abi)) {
                return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            }
        }
        return PackageManager.INSTALL_SUCCEEDED;
    }

    private static String getRelativePath(File base, File target) {
        try {
            final Path basePath = base.toPath();
            final Path targetPath = target.toPath();
            final Path relativePath = basePath.relativize(targetPath);
            if (relativePath.toString().isEmpty()) {
                return "";
            }
            return relativePath.toString();
        } catch (IllegalArgumentException ex) {
            Slog.e(TAG, "Failed to find relative path between: " + base.getAbsolutePath()
                    + " and: " + target.getAbsolutePath());
            return null;
        }
    }

    // We don't care about the other return values for now.
    private static final int BITCODE_PRESENT = 1;

+8 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#include "BinderIncrementalService.h"

#include <android-base/logging.h>
#include <binder/IResultReceiver.h>
#include <binder/PermissionCache.h>
#include <incfs.h>
@@ -24,7 +25,6 @@
#include "jni.h"
#include "nativehelper/JNIHelp.h"
#include "path.h"
#include <android-base/logging.h>

using namespace std::literals;
using namespace android::incremental;
@@ -277,6 +277,13 @@ binder::Status BinderIncrementalService::startLoading(int32_t storageId, bool* _
    return ok();
}

binder::Status BinderIncrementalService::configureNativeBinaries(
        int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath,
        const std::string& abi, bool* _aidl_return) {
    *_aidl_return = mImpl.configureNativeBinaries(storageId, apkFullPath, libDirRelativePath, abi);
    return ok();
}

} // namespace android::os::incremental

jlong Incremental_IncrementalService_Start() {
Loading