Merge "Incremental native lib extraction"
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 21434a2..9d98b3b 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -103,4 +103,9 @@
* 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 0024ac7..ba38268 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -229,16 +229,41 @@
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 c4b843b..5df44ff 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -416,4 +416,24 @@
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 a0de51d..0ccc45e 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_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.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.File;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.List;
/**
@@ -481,8 +488,54 @@
*/
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 0941831..b2c316a 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 @@
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 8a09977..51d7de3 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -28,11 +28,11 @@
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 @@
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 dbd97cf..3b51377 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -60,6 +60,9 @@
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 @@
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 @@
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 dec9f64..2444dde 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -126,7 +126,8 @@
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)