diff options
20 files changed, 497 insertions, 111 deletions
diff --git a/core/java/android/os/incremental/IIncrementalManager.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl index b415bc02fbcc..be83aaedd773 100644 --- a/core/java/android/os/incremental/IIncrementalManager.aidl +++ b/core/java/android/os/incremental/IIncrementalManager.aidl @@ -33,7 +33,4 @@ interface IIncrementalManager { boolean startDataLoader(int mountId); void showHealthBlockedUI(int mountId); void destroyDataLoader(int mountId); - - // fileId is a 16 byte long identifier. - void newFileForDataLoader(int mountId, in byte[] fileId, in byte[] metadata); } diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index 9d98b3b7819b..2dbaea860e2a 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -17,6 +17,7 @@ package android.os.incremental; import android.content.pm.DataLoaderParamsParcel; +import android.content.pm.IDataLoaderStatusListener; import android.os.incremental.IncrementalNewFileParams; /** @hide */ @@ -33,7 +34,7 @@ interface IIncrementalService { * Opens or creates a storage given a target path and data loader params. Returns the storage ID. */ int openStorage(in @utf8InCpp String path); - int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode); + int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, in IDataLoaderStatusListener listener, int createMode); int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode); /** diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 25fb3e0ed907..3f8c0fedc7a6 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -35,6 +35,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.DataLoaderParams; +import android.content.pm.IDataLoaderStatusListener; import android.content.pm.InstallationFile; import android.text.TextUtils; import android.util.Slog; @@ -74,6 +75,7 @@ public final class IncrementalFileStorages { public static IncrementalFileStorages initialize(Context context, @NonNull File stageDir, @NonNull DataLoaderParams dataLoaderParams, + @Nullable IDataLoaderStatusListener dataLoaderStatusListener, List<InstallationFile> addedFiles) throws IOException { // TODO(b/136132412): sanity check if session should not be incremental IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( @@ -85,7 +87,13 @@ public final class IncrementalFileStorages { IncrementalFileStorages result = null; try { - result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams); + result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams, + dataLoaderStatusListener); + + if (!addedFiles.isEmpty()) { + result.mDefaultStorage.bind(stageDir.getAbsolutePath()); + } + for (InstallationFile file : addedFiles) { if (file.getLocation() == LOCATION_DATA_APP) { try { @@ -93,14 +101,15 @@ public final class IncrementalFileStorages { } catch (IOException e) { // TODO(b/146080380): add incremental-specific error code throw new IOException( - "Failed to add and configure Incremental File: " + file.getName(), - e); + "Failed to add file to IncFS: " + file.getName() + ", reason: " + + e.getMessage(), e.getCause()); } } else { throw new IOException("Unknown file location: " + file.getLocation()); } } + // TODO(b/146080380): remove 5 secs wait in startLoading if (!result.mDefaultStorage.startLoading()) { // TODO(b/146080380): add incremental-specific error code throw new IOException("Failed to start loading data for Incremental installation."); @@ -117,7 +126,8 @@ public final class IncrementalFileStorages { private IncrementalFileStorages(@NonNull File stageDir, @NonNull IncrementalManager incrementalManager, - @NonNull DataLoaderParams dataLoaderParams) throws IOException { + @NonNull DataLoaderParams dataLoaderParams, + @Nullable IDataLoaderStatusListener dataLoaderStatusListener) throws IOException { mStageDir = stageDir; mIncrementalManager = incrementalManager; if (dataLoaderParams.getComponentName().getPackageName().equals("local")) { @@ -134,6 +144,7 @@ public final class IncrementalFileStorages { } mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, dataLoaderParams, + dataLoaderStatusListener, IncrementalManager.CREATE_MODE_CREATE | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); } @@ -144,10 +155,8 @@ public final class IncrementalFileStorages { } private void addApkFile(@NonNull InstallationFile apk) throws IOException { - final String stageDirPath = mStageDir.getAbsolutePath(); - mDefaultStorage.bind(stageDirPath); - String apkName = apk.getName(); - File targetFile = Paths.get(stageDirPath, apkName).toFile(); + final String apkName = apk.getName(); + final File targetFile = new File(mStageDir, apkName); if (!targetFile.exists()) { mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(), apk.getSignature()); diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index d2d8f85b1b35..74a4215b804f 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; import android.content.pm.DataLoaderParams; +import android.content.pm.IDataLoaderStatusListener; import android.os.RemoteException; import android.util.SparseArray; @@ -103,10 +104,11 @@ public final class IncrementalManager { */ @Nullable public IncrementalStorage createStorage(@NonNull String path, - @NonNull DataLoaderParams params, @CreateMode int createMode, + @NonNull DataLoaderParams params, @Nullable IDataLoaderStatusListener listener, + @CreateMode int createMode, boolean autoStartDataLoader) { try { - final int id = mService.createStorage(path, params.getData(), createMode); + final int id = mService.createStorage(path, params.getData(), listener, createMode); if (id < 0) { return null; } diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java index b9700b2b80db..41900015d6a5 100644 --- a/core/java/android/service/dataloader/DataLoaderService.java +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -240,7 +240,7 @@ public abstract class DataLoaderService extends Service { private native boolean nativeDestroyDataLoader(int storageId); private native boolean nativePrepareImage(int storageId, - Collection<InstallationFile> addedFiles, Collection<String> removedFiles); + List<InstallationFile> addedFiles, List<String> removedFiles); private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd); diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp index b307a73ed05b..ed0d381c67bb 100644 --- a/core/jni/android_service_DataLoaderService.cpp +++ b/core/jni/android_service_DataLoaderService.cpp @@ -75,8 +75,9 @@ static const JNINativeMethod dlc_method_table[] = { {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader}, {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader}, {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader}, - {"nativePrepareImage", "(ILjava/util/Collection;Ljava/util/Collection;)Z", (void*)nativePrepareImage}, - {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", (void*)nativeWriteData}, + {"nativePrepareImage", "(ILjava/util/List;Ljava/util/List;)Z", (void*)nativePrepareImage}, + {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", + (void*)nativeWriteData}, }; } // namespace diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp index 01150b773557..6bd5a69fe98b 100644 --- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp +++ b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp @@ -261,6 +261,11 @@ private: mReadLogFd.reset(); } + // Installation callback + bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) final { + return true; + } + // IFS callbacks. void onPendingReads(const android::dataloader::PendingReads& pendingReads) final { std::lock_guard lock{mMapsMutex}; @@ -520,6 +525,6 @@ private: int JNI_OnLoad(JavaVM* jvm, void* /* reserved */) { android::dataloader::DataLoader::initialize( - [](auto) { return std::make_unique<AdbDataLoader>(); }); + [](auto, auto) { return std::make_unique<AdbDataLoader>(); }); return JNI_VERSION_1_6; } diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java index 64f25dd6f9fd..a8121cc6924f 100644 --- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java +++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java @@ -142,16 +142,6 @@ public class IncrementalManagerService extends IIncrementalManager.Stub { } } - // TODO: remove this - @Override - public void newFileForDataLoader(int mountId, byte[] fileId, byte[] metadata) { - IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId); - if (dataLoader == null) { - Slog.e(TAG, "Failed to retrieve data loader for ID=" + mountId); - return; - } - } - @Override public void showHealthBlockedUI(int mountId) { // TODO(b/136132412): implement this diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 97defcdd3bc7..4ed4de70264a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2450,17 +2450,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - // TODO(b/136132412): update with new APIs - if (isIncrementalInstallation()) { - try { - mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, - stageDir, params.dataLoaderParams, addedFiles); - return true; - } catch (IOException e) { - throw new PackageManagerException(e); - } - } - final DataLoaderManager dataLoaderManager = mContext.getSystemService( DataLoaderManager.class); if (dataLoaderManager == null) { @@ -2468,6 +2457,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Failed to find data loader manager service"); } + final boolean manualStartAndDestroy = !isIncrementalInstallation(); IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() { @Override public void onStatusChanged(int dataLoaderId, int status) { @@ -2487,7 +2477,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { switch (status) { case IDataLoaderStatusListener.DATA_LOADER_CREATED: { - dataLoader.start(); + if (manualStartAndDestroy) { + // IncrementalFileStorages will call start after all files are + // created in IncFS. + dataLoader.start(); + } break; } case IDataLoaderStatusListener.DATA_LOADER_STARTED: { @@ -2502,7 +2496,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } else { dispatchStreamValidateAndCommit(); } - dataLoader.destroy(); + if (manualStartAndDestroy) { + dataLoader.destroy(); + } break; } case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: { @@ -2510,7 +2506,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { onSessionVerificationFailure( new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Failed to prepare image.")); - dataLoader.destroy(); + if (manualStartAndDestroy) { + dataLoader.destroy(); + } break; } } @@ -2524,6 +2522,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; + if (!manualStartAndDestroy) { + try { + mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, + stageDir, params.dataLoaderParams, listener, addedFiles); + return false; + } catch (IOException e) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), + e.getCause()); + } + } + final FileSystemConnector connector = new FileSystemConnector(addedFiles); final FileSystemControlParcel control = new FileSystemControlParcel(); control.callback = connector; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index c69a62d406aa..3909fdff21f0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -181,6 +181,8 @@ class PackageManagerShellCommand extends ShellCommand { return runInstall(); case "install-streaming": return runStreamingInstall(); + case "install-incremental": + return runIncrementalInstall(); case "install-abandon": case "install-destroy": return runInstallAbandon(); @@ -1168,6 +1170,15 @@ class PackageManagerShellCommand extends ShellCommand { return doRunInstall(params); } + private int runIncrementalInstall() throws RemoteException { + final InstallParams params = makeInstallParams(); + if (params.sessionParams.dataLoaderParams == null) { + params.sessionParams.setDataLoaderParams( + PackageManagerShellCommandDataLoader.getIncrementalDataLoaderParams(this)); + } + return doRunInstall(params); + } + private int runInstall() throws RemoteException { return doRunInstall(makeInstallParams()); } @@ -3001,17 +3012,21 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } - session.addFile(LOCATION_DATA_APP, name, sizeBytes, STDIN_PATH_BYTES, null); + // Incremental requires unique metadatas, let's add a name to the dash. + session.addFile(LOCATION_DATA_APP, name, sizeBytes, + ("-" + name).getBytes(StandardCharsets.UTF_8), null); continue; } // 3. Local file. final String inPath = arg; - String name = new File(inPath).getName(); + final File file = new File(inPath); + final String name = file.getName(); + final long size = file.length(); byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8); - session.addFile(LOCATION_DATA_APP, name, -1, metadata, null); + session.addFile(LOCATION_DATA_APP, name, size, metadata, null); } return 0; } finally { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java index 4170be4a913d..8f30e7da3204 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.pm.DataLoaderParams; import android.content.pm.InstallationFile; +import android.content.pm.PackageInstaller; import android.os.ParcelFileDescriptor; import android.os.ShellCommand; import android.service.dataloader.DataLoaderService; @@ -55,6 +56,8 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { private static final String STDIN_PATH = "-"; private static String getDataLoaderParamsArgs(ShellCommand shellCommand) { + nativeInitialize(); + int commandId; synchronized (sShellCommands) { // Clean up old references. @@ -86,6 +89,11 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { getDataLoaderParamsArgs(shellCommand)); } + static DataLoaderParams getIncrementalDataLoaderParams(ShellCommand shellCommand) { + return DataLoaderParams.forIncremental(new ComponentName(PACKAGE, CLASS), + getDataLoaderParamsArgs(shellCommand), null); + } + private static int extractShellCommandId(String args) { int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX); if (sessionIdIdx < 0) { @@ -106,7 +114,7 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } } - static class DataLoader implements DataLoaderService.DataLoader { + private static class DataLoader implements DataLoaderService.DataLoader { private DataLoaderParams mParams = null; private FileSystemConnector mConnector = null; @@ -121,17 +129,7 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { @Override public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles, @NonNull Collection<String> removedFiles) { - final int commandId = extractShellCommandId(mParams.getArguments()); - if (commandId == INVALID_SHELL_COMMAND_ID) { - return false; - } - - final WeakReference<ShellCommand> shellCommandRef; - synchronized (sShellCommands) { - shellCommandRef = sShellCommands.get(commandId, null); - } - final ShellCommand shellCommand = - shellCommandRef != null ? shellCommandRef.get() : null; + ShellCommand shellCommand = lookupShellCommand(mParams.getArguments()); if (shellCommand == null) { Slog.e(TAG, "Missing shell command."); return false; @@ -139,14 +137,13 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { try { for (InstallationFile file : addedFiles) { String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8); - if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) { - final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup( - shellCommand.getInFileDescriptor()); + if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) { + final ParcelFileDescriptor inFd = getStdInPFD(shellCommand); mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd); } else { ParcelFileDescriptor incomingFd = null; try { - incomingFd = shellCommand.openFileForSystem(filePath, "r"); + incomingFd = getLocalFile(shellCommand, filePath); mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(), incomingFd); } finally { @@ -162,9 +159,45 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } } + static ShellCommand lookupShellCommand(String args) { + final int commandId = extractShellCommandId(args); + if (commandId == INVALID_SHELL_COMMAND_ID) { + return null; + } + + final WeakReference<ShellCommand> shellCommandRef; + synchronized (sShellCommands) { + shellCommandRef = sShellCommands.get(commandId, null); + } + final ShellCommand shellCommand = + shellCommandRef != null ? shellCommandRef.get() : null; + + return shellCommand; + } + + static ParcelFileDescriptor getStdInPFD(ShellCommand shellCommand) { + try { + return ParcelFileDescriptor.dup(shellCommand.getInFileDescriptor()); + } catch (IOException e) { + Slog.e(TAG, "Exception while obtaining STDIN fd", e); + return null; + } + } + + static ParcelFileDescriptor getLocalFile(ShellCommand shellCommand, String filePath) { + return shellCommand.openFileForSystem(filePath, "r"); + } + @Override public DataLoaderService.DataLoader onCreateDataLoader( @NonNull DataLoaderParams dataLoaderParams) { - return new DataLoader(); + if (dataLoaderParams.getType() == PackageInstaller.DATA_LOADER_TYPE_STREAMING) { + // This DataLoader only supports streaming installations. + return new DataLoader(); + } + return null; } + + /* Native methods */ + private static native void nativeInitialize(); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 49c7e0a3b242..e888f2a4bae5 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -2,6 +2,7 @@ cc_library_static { name: "libservices.core", defaults: ["libservices.core-libs"], + cpp_std: "c++2a", cflags: [ "-Wall", "-Werror", @@ -55,6 +56,7 @@ cc_library_static { "com_android_server_am_CachedAppOptimizer.cpp", "com_android_server_am_LowMemDetector.cpp", "com_android_server_incremental_IncrementalManagerService.cpp", + "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp", "onload.cpp", ":lib_networkStatsFactory_native", ], @@ -125,6 +127,8 @@ cc_defaults { "libnetdbpf", "libnetdutils", "libpsi", + "libdataloader", + "libincfs", "android.hardware.audio.common@2.0", "android.hardware.broadcastradio@1.0", "android.hardware.broadcastradio@1.1", diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp new file mode 100644 index 000000000000..d803c1dc9397 --- /dev/null +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_ADB +#define LOG_TAG "PackageManagerShellCommandDataLoader-jni" +#include <android-base/logging.h> + +#include <android-base/unique_fd.h> +#include <nativehelper/JNIHelp.h> + +#include <core_jni_helpers.h> + +#include "dataloader.h" + +#include <chrono> +#include <thread> + +namespace android { + +namespace { + +using android::base::unique_fd; + +static constexpr int BUFFER_SIZE = 256 * 1024; +static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE; + +struct JniIds { + jclass packageManagerShellCommandDataLoader; + jmethodID pmscdLookupShellCommand; + jmethodID pmscdGetStdInPFD; + jmethodID pmscdGetLocalFile; + + jmethodID parcelFileDescriptorGetFileDescriptor; + + jclass ioUtils; + jmethodID ioUtilsCloseQuietly; + + JniIds(JNIEnv* env) { + packageManagerShellCommandDataLoader = (jclass)env->NewGlobalRef( + FindClassOrDie(env, "com/android/server/pm/PackageManagerShellCommandDataLoader")); + pmscdLookupShellCommand = + GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, + "lookupShellCommand", + "(Ljava/lang/String;)Landroid/os/ShellCommand;"); + pmscdGetStdInPFD = + GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getStdInPFD", + "(Landroid/os/ShellCommand;)Landroid/os/" + "ParcelFileDescriptor;"); + pmscdGetLocalFile = + GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getLocalFile", + "(Landroid/os/ShellCommand;Ljava/lang/String;)Landroid/os/" + "ParcelFileDescriptor;"); + + auto parcelFileDescriptor = FindClassOrDie(env, "android/os/ParcelFileDescriptor"); + parcelFileDescriptorGetFileDescriptor = + GetMethodIDOrDie(env, parcelFileDescriptor, "getFileDescriptor", + "()Ljava/io/FileDescriptor;"); + + ioUtils = (jclass)env->NewGlobalRef(FindClassOrDie(env, "libcore/io/IoUtils")); + ioUtilsCloseQuietly = GetStaticMethodIDOrDie(env, ioUtils, "closeQuietly", + "(Ljava/lang/AutoCloseable;)V"); + } +}; + +const JniIds& jniIds(JNIEnv* env) { + static const JniIds ids(env); + return ids; +} + +static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) { + if (!pfd) { + ALOGE("Missing In ParcelFileDescriptor."); + return {}; + } + auto managedFd = env->CallObjectMethod(pfd, jni.parcelFileDescriptorGetFileDescriptor); + if (!pfd) { + ALOGE("Missing In FileDescriptor."); + return {}; + } + return unique_fd{dup(jniGetFDFromFileDescriptor(env, managedFd))}; +} + +static inline std::pair<unique_fd, bool> openIncomingFile(JNIEnv* env, const JniIds& jni, + jobject shellCommand, + IncFsSpan metadata) { + jobject pfd = nullptr; + const bool stdin = (metadata.size == 0 || *metadata.data == '-'); + if (stdin) { + // stdin + pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, + jni.pmscdGetStdInPFD, shellCommand); + } else { + // file + const std::string filePath(metadata.data, metadata.size); + pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, + jni.pmscdGetLocalFile, shellCommand, + env->NewStringUTF(filePath.c_str())); + } + + auto result = convertPfdToFdAndDup(env, jni, pfd); + if (pfd) { + // Can be closed after dup. + env->CallStaticVoidMethod(jni.ioUtils, jni.ioUtilsCloseQuietly, pfd); + } + + return {std::move(result), stdin}; +} + +static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; +} + +static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) { + JNIEnv* env = GetJNIEnvironment(jvm); + if (!env) { + int result = jvm->AttachCurrentThread(&env, nullptr); + CHECK_EQ(result, JNI_OK) << "thread attach failed"; + struct VmDetacher { + VmDetacher(JavaVM* vm) : mVm(vm) {} + ~VmDetacher() { mVm->DetachCurrentThread(); } + + private: + JavaVM* const mVm; + }; + static thread_local VmDetacher detacher(jvm); + } + return env; +} + +class PackageManagerShellCommandDataLoaderDataLoader : public android::dataloader::DataLoader { +public: + PackageManagerShellCommandDataLoaderDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); } + +private: + // Lifecycle. + bool onCreate(const android::dataloader::DataLoaderParams& params, + android::dataloader::FilesystemConnectorPtr ifs, + android::dataloader::StatusListenerPtr statusListener, + android::dataloader::ServiceConnectorPtr, + android::dataloader::ServiceParamsPtr) final { + mArgs = params.arguments(); + mIfs = ifs; + return true; + } + bool onStart() final { return true; } + void onStop() final {} + void onDestroy() final {} + + // IFS callbacks. + void onPendingReads(const dataloader::PendingReads& pendingReads) final {} + void onPageReads(const dataloader::PageReads& pageReads) final {} + + // FS callbacks. + bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final { + JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); + const auto& jni = jniIds(env); + + jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, + jni.pmscdLookupShellCommand, + env->NewStringUTF(mArgs.c_str())); + if (!shellCommand) { + ALOGE("Missing shell command."); + return false; + } + + std::vector<char> buffer; + buffer.reserve(BUFFER_SIZE); + + std::vector<IncFsDataBlock> blocks; + blocks.reserve(BLOCKS_COUNT); + + for (auto&& file : addedFiles) { + auto [incomingFd, stdin] = openIncomingFile(env, jni, shellCommand, file.metadata); + if (incomingFd < 0) { + ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. " + "Error %d", + int(file.metadata.size), file.metadata.data, file.name, errno); + return false; + } + + const auto fileId = IncFs_FileIdFromMetadata(file.metadata); + + const auto incfsFd(mIfs->openWrite(fileId)); + if (incfsFd < 0) { + ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. " + "Error %d", + int(file.metadata.size), file.metadata.data, file.name, errno); + return false; + } + + IncFsSize size = file.size; + IncFsSize remaining = size; + IncFsSize totalSize = 0; + IncFsBlockIndex blockIdx = 0; + while (remaining > 0) { + constexpr auto capacity = BUFFER_SIZE; + auto size = buffer.size(); + if (capacity - size < INCFS_DATA_FILE_BLOCK_SIZE) { + if (!flashToIncFs(incfsFd, false, &blocks, &blockIdx, &buffer)) { + return false; + } + continue; + } + + auto toRead = std::min<IncFsSize>(remaining, capacity - size); + buffer.resize(size + toRead); + auto read = ::read(incomingFd, buffer.data() + size, toRead); + if (read == 0) { + if (stdin) { + // eof of stdin, waiting... + ALOGE("eof of stdin, waiting...: %d, remaining: %d, block: %d, read: %d", + int(totalSize), int(remaining), int(blockIdx), int(read)); + using namespace std::chrono_literals; + std::this_thread::sleep_for(10ms); + continue; + } + break; + } + if (read < 0) { + ALOGE("Underlying file read error: %.*s: %d", int(file.metadata.size), + file.metadata.data, int(read)); + return false; + } + + buffer.resize(size + read); + remaining -= read; + totalSize += read; + } + if (!buffer.empty() && !flashToIncFs(incfsFd, true, &blocks, &blockIdx, &buffer)) { + return false; + } + } + + ALOGE("All done."); + return true; + } + + bool flashToIncFs(int incfsFd, bool eof, std::vector<IncFsDataBlock>* blocks, + IncFsBlockIndex* blockIdx, std::vector<char>* buffer) { + int consumed = 0; + const auto fullBlocks = buffer->size() / INCFS_DATA_FILE_BLOCK_SIZE; + for (int i = 0; i < fullBlocks; ++i) { + const auto inst = IncFsDataBlock{ + .fileFd = incfsFd, + .pageIndex = (*blockIdx)++, + .compression = INCFS_COMPRESSION_KIND_NONE, + .kind = INCFS_BLOCK_KIND_DATA, + .dataSize = INCFS_DATA_FILE_BLOCK_SIZE, + .data = buffer->data() + consumed, + }; + blocks->push_back(inst); + consumed += INCFS_DATA_FILE_BLOCK_SIZE; + } + const auto remain = buffer->size() - fullBlocks * INCFS_DATA_FILE_BLOCK_SIZE; + if (remain && eof) { + const auto inst = IncFsDataBlock{ + .fileFd = incfsFd, + .pageIndex = (*blockIdx)++, + .compression = INCFS_COMPRESSION_KIND_NONE, + .kind = INCFS_BLOCK_KIND_DATA, + .dataSize = static_cast<uint16_t>(remain), + .data = buffer->data() + consumed, + }; + blocks->push_back(inst); + consumed += remain; + } + + auto res = mIfs->writeBlocks({blocks->data(), blocks->data() + blocks->size()}); + + blocks->clear(); + buffer->erase(buffer->begin(), buffer->begin() + consumed); + + if (res < 0) { + ALOGE("Failed to write block to IncFS: %d", int(res)); + return false; + } + return true; + } + + JavaVM* const mJvm; + std::string mArgs; + android::dataloader::FilesystemConnectorPtr mIfs; +}; + +static void nativeInitialize(JNIEnv* env, jclass klass) { + jniIds(env); +} + +static const JNINativeMethod method_table[] = { + {"nativeInitialize", "()V", (void*)nativeInitialize}, +}; + +} // namespace + +int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader( + JNIEnv* env) { + android::dataloader::DataLoader::initialize( + [](auto jvm, const auto& params) -> android::dataloader::DataLoaderPtr { + if (params.type() == DATA_LOADER_TYPE_INCREMENTAL) { + // This DataLoader only supports incremental installations. + return std::make_unique<PackageManagerShellCommandDataLoaderDataLoader>(jvm); + } + return {}; + }); + return jniRegisterNativeMethods(env, + "com/android/server/pm/PackageManagerShellCommandDataLoader", + method_table, NELEM(method_table)); +} + +} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index c1864945f921..e57543203634 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -59,6 +59,7 @@ int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( JNIEnv* env); int register_android_server_incremental_IncrementalManagerService(JNIEnv* env); +int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env); int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); }; @@ -112,6 +113,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( env); register_android_server_incremental_IncrementalManagerService(env); + register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env); register_android_server_stats_pull_StatsPullAtomService(env); return JNI_VERSION_1_4; } diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index b2c316a25e7f..3fcb57a83cf5 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -117,10 +117,9 @@ binder::Status BinderIncrementalService::openStorage(const std::string& path, binder::Status BinderIncrementalService::createStorage(const std::string& path, const DataLoaderParamsParcel& params, + const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, int32_t createMode, int32_t* _aidl_return) { - *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), - android::incremental::IncrementalService::CreateOptions( - createMode)); + *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), listener, android::incremental::IncrementalService::CreateOptions(createMode)); return ok(); } diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 51d7de3d9adf..4075da6d8159 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -38,9 +38,7 @@ public: void onInvalidStorage(int mountId); binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final; - binder::Status createStorage(const std::string& path, - const ::android::content::pm::DataLoaderParamsParcel& params, - int32_t createMode, int32_t* _aidl_return) final; + binder::Status createStorage(const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params, const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, int32_t createMode, int32_t* _aidl_return) final; binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId, int32_t createMode, int32_t* _aidl_return) final; binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath, diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 3b513774b615..ebadf565706d 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -30,7 +30,6 @@ #include <binder/BinderService.h> #include <binder/ParcelFileDescriptor.h> #include <binder/Status.h> -#include <openssl/sha.h> #include <sys/stat.h> #include <uuid/uuid.h> #include <zlib.h> @@ -242,20 +241,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v } FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) { - incfs::FileId id = {}; - if (size_t(metadata.size()) <= sizeof(id)) { - memcpy(&id, metadata.data(), metadata.size()); - } else { - uint8_t buffer[SHA_DIGEST_LENGTH]; - static_assert(sizeof(buffer) >= sizeof(id)); - - SHA_CTX ctx; - SHA1_Init(&ctx); - SHA1_Update(&ctx, metadata.data(), metadata.size()); - SHA1_Final(buffer, &ctx); - memcpy(&id, buffer, sizeof(id)); - } - return id; + return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()}); } IncrementalService::~IncrementalService() = default; @@ -344,7 +330,7 @@ std::optional<std::future<void>> IncrementalService::onSystemReady() { std::thread([this, mounts = std::move(mounts)]() { std::vector<IfsMountPtr> failedLoaderMounts; for (auto&& ifs : mounts) { - if (prepareDataLoader(*ifs, nullptr)) { + if (prepareDataLoader(*ifs)) { LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId; } else { LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId; @@ -377,6 +363,7 @@ auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator { StorageId IncrementalService::createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams, + const DataLoaderStatusListener& dataLoaderStatusListener, CreateOptions options) { LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options); if (!path::isAbsolute(mountPoint)) { @@ -509,7 +496,7 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint, // Done here as well, all data structures are in good state. secondCleanupOnFailure.release(); - if (!prepareDataLoader(*ifs, &dataLoaderParams)) { + if (!prepareDataLoader(*ifs, &dataLoaderParams, &dataLoaderStatusListener)) { LOG(ERROR) << "prepareDataLoader() failed"; deleteStorageLocked(*ifs, std::move(l)); return kInvalidStorageId; @@ -767,7 +754,6 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m if (params.metadata.data && params.metadata.size > 0) { metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size); } - mIncrementalManager->newFileForDataLoader(ifs->mountId, id, metadataBytes); return 0; } return -EINVAL; @@ -1074,7 +1060,8 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v } bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, - DataLoaderParamsParcel* params) { + DataLoaderParamsParcel* params, + const DataLoaderStatusListener* externalListener) { if (!mSystemReady.load(std::memory_order_relaxed)) { std::unique_lock l(ifs.lock); if (params) { @@ -1117,7 +1104,7 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, fsControlParcel.incremental->pendingReads.reset( base::unique_fd(::dup(ifs.control.pendingReads))); fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs))); - sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this); + sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this, *externalListener); bool created = false; auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp, listener, &created); @@ -1247,6 +1234,11 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId, int newStatus) { + if (externalListener) { + // Give an external listener a chance to act before we destroy something. + externalListener->onStatusChanged(mountId, newStatus); + } + std::unique_lock l(incrementalService.mLock); const auto& ifs = incrementalService.getIfsLocked(mountId); if (!ifs) { @@ -1288,6 +1280,12 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange case IDataLoaderStatusListener::DATA_LOADER_STOPPED: { break; } + case IDataLoaderStatusListener::DATA_LOADER_IMAGE_READY: { + break; + } + case IDataLoaderStatusListener::DATA_LOADER_IMAGE_NOT_READY: { + break; + } default: { LOG(WARNING) << "Unknown data loader status: " << newStatus << " for mount: " << mountId; diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 2e7ced37e91d..75d066b9444a 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -59,6 +59,8 @@ using Clock = std::chrono::steady_clock; using TimePoint = std::chrono::time_point<Clock>; using Seconds = std::chrono::seconds; +using DataLoaderStatusListener = ::android::sp<::android::content::pm::IDataLoaderStatusListener>; + class IncrementalService final { public: explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir); @@ -94,7 +96,9 @@ public: std::optional<std::future<void>> onSystemReady(); - StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams, + StorageId createStorage(std::string_view mountPoint, + DataLoaderParamsParcel&& dataLoaderParams, + const DataLoaderStatusListener& dataLoaderStatusListener, CreateOptions options = CreateOptions::Default); StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage, CreateOptions options = CreateOptions::Default); @@ -129,13 +133,14 @@ public: std::string_view libDirRelativePath, std::string_view abi); class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener { public: - IncrementalDataLoaderListener(IncrementalService& incrementalService) - : incrementalService(incrementalService) {} + IncrementalDataLoaderListener(IncrementalService& incrementalService, DataLoaderStatusListener externalListener) + : incrementalService(incrementalService), externalListener(externalListener) {} // Callbacks interface binder::Status onStatusChanged(MountId mount, int newStatus) override; private: IncrementalService& incrementalService; + DataLoaderStatusListener externalListener; }; private: @@ -201,7 +206,7 @@ private: std::string&& source, std::string&& target, BindKind kind, std::unique_lock<std::mutex>& mainLock); - bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params); + bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr, const DataLoaderStatusListener* externalListener = nullptr); BindPathMap::const_iterator findStorageLocked(std::string_view path) const; StorageId findStorageId(std::string_view path) const; diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index f0b56729e8d7..642158322c7c 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -60,8 +60,6 @@ public: bool* _aidl_return) const = 0; virtual binder::Status startDataLoader(MountId mountId, bool* _aidl_return) const = 0; virtual binder::Status destroyDataLoader(MountId mountId) const = 0; - virtual binder::Status newFileForDataLoader(MountId mountId, FileId fileid, - const std::vector<uint8_t>& metadata) const = 0; virtual binder::Status showHealthBlockedUI(MountId mountId) const = 0; }; @@ -128,13 +126,6 @@ public: binder::Status destroyDataLoader(MountId mountId) const override { return mInterface->destroyDataLoader(mountId); } - binder::Status newFileForDataLoader(MountId mountId, FileId fileid, - const std::vector<uint8_t>& metadata) const override { - return mInterface->newFileForDataLoader(mountId, - {(const uint8_t*)fileid.data, - (const uint8_t*)fileid.data + sizeof(fileid.data)}, - metadata); - } binder::Status showHealthBlockedUI(MountId mountId) const override { return mInterface->showHealthBlockedUI(mountId); } diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 9cdc83e75055..f7598f769b2b 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -293,7 +293,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) { EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -303,7 +303,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel) EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -316,7 +316,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) { EXPECT_CALL(*mVold, unmountIncFs(_)); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -330,7 +330,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) { EXPECT_CALL(*mVold, unmountIncFs(_)); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -344,7 +344,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -358,7 +358,7 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mIncrementalService->deleteStorage(storageId); @@ -373,7 +373,7 @@ TEST_F(IncrementalServiceTest, testOnStatusNotReady) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mIncrementalManager->setDataLoaderStatusNotReady(); @@ -389,7 +389,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderSuccess) { EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mIncrementalManager->setDataLoaderStatusReady(); @@ -404,7 +404,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) { mIncrementalManager->startDataLoaderSuccess(); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); std::string dir_path("test"); @@ -428,7 +428,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) { mIncrementalManager->startDataLoaderSuccess(); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); auto first = "first"sv; auto second = "second"sv; |