diff options
8 files changed, 256 insertions, 21 deletions
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index 21434a2aecba..9d98b3b7819b 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -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); } diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 0024ac7a6b2e..ba38268949b5 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -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); } /** diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index c4b843b6ce33..5df44ff49059 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -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; + } + } } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index a0de51d4d211..0ccc45e000e6 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -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,8 +488,54 @@ public class NativeLibraryHelper { */ private static int incrementalConfigureNativeBinariesForSupportedAbi(Handle handle, File libSubDir, String abi) { - // TODO(b/136132412): implement this - return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + 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. diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index 0941831f5299..b2c316a25e7f 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -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() { diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 8a099776b54b..51d7de3d9adf 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -28,11 +28,11 @@ namespace android::os::incremental { class BinderIncrementalService : public BnIncrementalService, public BinderService<BinderIncrementalService> { public: - BinderIncrementalService(const sp<IServiceManager> &sm); + BinderIncrementalService(const sp<IServiceManager>& sm); - static BinderIncrementalService *start(); - static const char16_t *getServiceName() { return u"incremental_service"; } - status_t dump(int fd, const Vector<String16> &args) final; + static BinderIncrementalService* start(); + static const char16_t* getServiceName() { return u"incremental_service"; } + status_t dump(int fd, const Vector<String16>& args) final; void onSystemReady(); void onInvalidStorage(int mountId); @@ -70,6 +70,9 @@ public: std::vector<uint8_t>* _aidl_return) final; binder::Status startLoading(int32_t storageId, bool* _aidl_return) final; binder::Status deleteStorage(int32_t storageId) final; + binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath, + const std::string& libDirRelativePath, + const std::string& abi, bool* _aidl_return) final; private: android::incremental::IncrementalService mImpl; diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index dbd97cfa039a..3b513774b615 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -60,6 +60,9 @@ struct Constants { static constexpr auto storagePrefix = "st"sv; static constexpr auto mountpointMdPrefix = ".mountpoint."sv; static constexpr auto infoMdName = ".info"sv; + static constexpr auto libDir = "lib"sv; + static constexpr auto libSuffix = ".so"sv; + static constexpr auto blockSize = 4096; }; static const Constants& constants() { @@ -259,16 +262,18 @@ IncrementalService::~IncrementalService() = default; inline const char* toString(TimePoint t) { using SystemClock = std::chrono::system_clock; - time_t time = SystemClock::to_time_t(SystemClock::now() + std::chrono::duration_cast<SystemClock::duration>(t - Clock::now())); + time_t time = SystemClock::to_time_t( + SystemClock::now() + + std::chrono::duration_cast<SystemClock::duration>(t - Clock::now())); return std::ctime(&time); } inline const char* toString(IncrementalService::BindKind kind) { switch (kind) { - case IncrementalService::BindKind::Temporary: - return "Temporary"; - case IncrementalService::BindKind::Permanent: - return "Permanent"; + case IncrementalService::BindKind::Temporary: + return "Temporary"; + case IncrementalService::BindKind::Permanent: + return "Permanent"; } } @@ -1124,6 +1129,122 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, return true; } +// Extract lib filse from zip, create new files in incfs and write data to them +bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath, + std::string_view libDirRelativePath, + std::string_view abi) { + const auto ifs = getIfs(storage); + // First prepare target directories if they don't exist yet + if (auto res = makeDirs(storage, libDirRelativePath, 0755)) { + LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath + << " errno: " << res; + return false; + } + + std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(apkFullPath.data())); + if (!zipFile) { + LOG(ERROR) << "Failed to open zip file at " << apkFullPath; + return false; + } + void* cookie = nullptr; + const auto libFilePrefix = path::join(constants().libDir, abi); + if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */, + constants().libSuffix.data() /* suffix */)) { + LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath; + return false; + } + ZipEntryRO entry = nullptr; + bool success = true; + while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) { + char fileName[PATH_MAX]; + if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) { + continue; + } + const auto libName = path::basename(fileName); + const auto targetLibPath = path::join(libDirRelativePath, libName); + const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath); + // If the extract file already exists, skip + struct stat st; + if (stat(targetLibPathAbsolute.c_str(), &st) == 0) { + LOG(INFO) << "Native lib file already exists: " << targetLibPath + << "; skipping extraction"; + continue; + } + + uint32_t uncompressedLen; + if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr, + nullptr, nullptr)) { + LOG(ERROR) << "Failed to read native lib entry: " << fileName; + success = false; + break; + } + + // Create new lib file without signature info + incfs::NewFileParams libFileParams; + libFileParams.size = uncompressedLen; + libFileParams.verification.hashAlgorithm = INCFS_HASH_NONE; + // Metadata of the new lib file is its relative path + IncFsSpan libFileMetadata; + libFileMetadata.data = targetLibPath.c_str(); + libFileMetadata.size = targetLibPath.size(); + libFileParams.metadata = libFileMetadata; + incfs::FileId libFileId = idFromMetadata(targetLibPath); + if (auto res = makeFile(storage, targetLibPath, 0777, libFileId, libFileParams)) { + LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res; + success = false; + // If one lib file fails to be created, abort others as well + break; + } + + // Write extracted data to new file + std::vector<uint8_t> libData(uncompressedLen); + if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) { + LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName; + success = false; + break; + } + android::base::unique_fd writeFd(mIncFs->openWrite(ifs->control, libFileId)); + if (writeFd < 0) { + LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd; + success = false; + break; + } + const int numBlocks = uncompressedLen / constants().blockSize + 1; + std::vector<IncFsDataBlock> instructions; + auto remainingData = std::span(libData); + for (int i = 0; i < numBlocks - 1; i++) { + auto inst = IncFsDataBlock{ + .fileFd = writeFd, + .pageIndex = static_cast<IncFsBlockIndex>(i), + .compression = INCFS_COMPRESSION_KIND_NONE, + .kind = INCFS_BLOCK_KIND_DATA, + .dataSize = static_cast<uint16_t>(constants().blockSize), + .data = reinterpret_cast<const char*>(remainingData.data()), + }; + instructions.push_back(inst); + remainingData = remainingData.subspan(constants().blockSize); + } + // Last block + auto inst = IncFsDataBlock{ + .fileFd = writeFd, + .pageIndex = static_cast<IncFsBlockIndex>(numBlocks - 1), + .compression = INCFS_COMPRESSION_KIND_NONE, + .kind = INCFS_BLOCK_KIND_DATA, + .dataSize = static_cast<uint16_t>(remainingData.size()), + .data = reinterpret_cast<const char*>(remainingData.data()), + }; + instructions.push_back(inst); + size_t res = mIncFs->writeBlocks(instructions); + if (res != instructions.size()) { + LOG(ERROR) << "Failed to write data into: " << targetLibPath; + success = false; + } + instructions.clear(); + } + zipFile.get()->endIteration(cookie); + return success; +} + binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId, int newStatus) { std::unique_lock l(incrementalService.mLock); diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index dec9f64f2084..2444ddecdb80 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -126,7 +126,8 @@ public: std::vector<std::string> listFiles(StorageId storage) const; bool startLoading(StorageId storage) const; - + bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath, + std::string_view libDirRelativePath, std::string_view abi); class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener { public: IncrementalDataLoaderListener(IncrementalService& incrementalService) |