diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 4a668791aeb92..987a53e337a06 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -17,24 +17,24 @@ package android.os.incremental; /** - * Set up files and directories used in an installation session. - * Currently only used by Incremental Installation. - * For Incremental installation, the expected outcome of this function is: - * 0) All the files are in defaultStorage - * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the - * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage. - * 2) All lib files are in the sub directories as their names suggest, and in the same parent - * directory as the APK files. The files are linked from mApkStorage to defaultStorage. - * 3) OBB files are in another directory that is different from APK files and lib files, bound - * to mObbStorage. The files are linked from mObbStorage to defaultStorage. + * Set up files and directories used in an installation session. Currently only used by Incremental + * Installation. For Incremental installation, the expected outcome of this function is: 0) All the + * files are in defaultStorage 1) All APK files are in the same directory, bound to mApkStorage, and + * bound to the InstallerSession's stage dir. The files are linked from mApkStorage to + * defaultStorage. 2) All lib files are in the sub directories as their names suggest, and in the + * same parent directory as the APK files. The files are linked from mApkStorage to defaultStorage. + * 3) OBB files are in another directory that is different from APK files and lib files, bound to + * mObbStorage. The files are linked from mObbStorage to defaultStorage. * * @throws IllegalStateException the session is not an Incremental installation session. */ import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.content.pm.DataLoaderParams; import android.content.pm.InstallationFile; +import android.text.TextUtils; import android.util.Slog; import java.io.File; @@ -42,6 +42,8 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; +import java.util.Objects; import java.util.Random; /** @@ -50,6 +52,10 @@ import java.util.Random; */ public final class IncrementalFileStorages { private static final String TAG = "IncrementalFileStorages"; + + private static final String TMP_DIR_ROOT = "/data/incremental/tmp"; + private static final Random TMP_DIR_RANDOM = new Random(); + private @Nullable IncrementalStorage mDefaultStorage; private @Nullable String mDefaultDir; private @NonNull IncrementalManager mIncrementalManager; @@ -61,41 +67,77 @@ public final class IncrementalFileStorages { * TODO(b/133435829): code clean up * * @throws IllegalStateException the session is not an Incremental installation session. + * @throws IOException if fails to setup files or directories. */ - public IncrementalFileStorages(@NonNull String packageName, + public static IncrementalFileStorages initialize(Context context, @NonNull File stageDir, + @NonNull DataLoaderParams dataLoaderParams, + List addedFiles) throws IOException { + // TODO(b/136132412): sanity check if session should not be incremental + IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( + Context.INCREMENTAL_SERVICE); + if (incrementalManager == null) { + // TODO(b/146080380): add incremental-specific error code + throw new IOException("Failed to obtain incrementalManager."); + } + + IncrementalFileStorages result = null; + try { + result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams); + for (InstallationFile file : addedFiles) { + if (file.getFileType() == InstallationFile.FILE_TYPE_APK) { + try { + result.addApkFile(file); + } catch (IOException e) { + // TODO(b/146080380): add incremental-specific error code + throw new IOException( + "Failed to add and configure Incremental File: " + file.getName(), + e); + } + } else { + throw new IOException("Unknown file type: " + file.getFileType()); + } + } + + if (!result.mDefaultStorage.startLoading()) { + // TODO(b/146080380): add incremental-specific error code + throw new IOException("Failed to start loading data for Incremental installation."); + } + + return result; + } catch (IOException e) { + if (result != null) { + result.cleanUp(); + } + throw e; + } + } + + private IncrementalFileStorages(@NonNull File stageDir, @NonNull IncrementalManager incrementalManager, - @NonNull DataLoaderParams dataLoaderParams) { + @NonNull DataLoaderParams dataLoaderParams) throws IOException { mStageDir = stageDir; mIncrementalManager = incrementalManager; if (dataLoaderParams.getComponentName().getPackageName().equals("local")) { final String incrementalPath = dataLoaderParams.getArguments(); - mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); mDefaultDir = incrementalPath; - return; - } - mDefaultDir = getTempDir(); - if (mDefaultDir == null) { - return; - } - mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, - dataLoaderParams, - IncrementalManager.CREATE_MODE_CREATE - | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); - } - - /** - * Adds a file into the installation session. Makes sure it will be placed inside - * a proper storage instance, based on its file type. - */ - public void addFile(@NonNull InstallationFile file) throws IOException { - if (mDefaultStorage == null) { - throw new IOException("Cannot add file because default storage does not exist"); - } - if (file.getFileType() == InstallationFile.FILE_TYPE_APK) { - addApkFile(file); + if (TextUtils.isEmpty(mDefaultDir)) { + throw new IOException("Failed to create storage: incrementalPath is empty"); + } + mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); } else { - throw new IOException("Unknown file type: " + file.getFileType()); + mDefaultDir = getTempDir(); + if (mDefaultDir == null) { + throw new IOException("Failed to create storage: tempDir is empty"); + } + mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, + dataLoaderParams, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); + } + + if (mDefaultStorage == null) { + throw new IOException("Failed to create storage"); } } @@ -108,26 +150,6 @@ public final class IncrementalFileStorages { mDefaultStorage.makeFile(apkName, apk.getSize(), null, apk.getMetadata(), 0, null, null, null); } - if (targetFile.exists()) { - Slog.i(TAG, "!!! created: " + targetFile.getAbsolutePath()); - } - } - - /** - * Starts loading data for default storage. - * TODO(b/136132412): update the implementation with latest API design. - */ - public boolean startLoading() { - if (mDefaultStorage == null) { - return false; - } - return mDefaultStorage.startLoading(); - } - - /** - * Sets up obb storage directory and create bindings. - */ - public void finishSetUp() { } /** @@ -135,22 +157,21 @@ public final class IncrementalFileStorages { * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept */ public void cleanUp() { - if (mDefaultStorage != null && mDefaultDir != null) { - try { - mDefaultStorage.unBind(mDefaultDir); - mDefaultStorage.unBind(mStageDir.getAbsolutePath()); - } catch (IOException ignored) { - } - mDefaultDir = null; - mDefaultStorage = null; + Objects.requireNonNull(mDefaultStorage); + + try { + mDefaultStorage.unBind(mDefaultDir); + mDefaultStorage.unBind(mStageDir.getAbsolutePath()); + } catch (IOException ignored) { } + + mDefaultDir = null; + mDefaultStorage = null; } - private String getTempDir() { - final String tmpDirRoot = "/data/incremental/tmp"; - final Random random = new Random(); - final Path tmpDir = - Paths.get(tmpDirRoot, String.valueOf(random.nextInt(Integer.MAX_VALUE - 1))); + private static String getTempDir() { + final Path tmpDir = Paths.get(TMP_DIR_ROOT, + String.valueOf(TMP_DIR_RANDOM.nextInt(Integer.MAX_VALUE - 1))); try { Files.createDirectories(tmpDir); } catch (Exception ex) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 38da8ab269620..bf7bebd10a136 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -101,7 +101,6 @@ import android.os.RevocableFileDescriptor; import android.os.SystemProperties; import android.os.UserHandle; import android.os.incremental.IncrementalFileStorages; -import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.provider.Settings.Secure; import android.stats.devicepolicy.DevicePolicyEnums; @@ -559,17 +558,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionErrorMessage = stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; - // TODO(b/136132412): sanity check if session should not be incremental - if (!params.isStaged && isIncrementalInstallation()) { - IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService( - Context.INCREMENTAL_SERVICE); - if (incrementalManager != null) { - mIncrementalFileStorages = - new IncrementalFileStorages(mPackageName, stageDir, incrementalManager, - params.dataLoaderParams); - } - } - if (isStreamingInstallation() && this.params.dataLoaderParams.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE) { @@ -1040,10 +1028,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - if (mIncrementalFileStorages != null) { - mIncrementalFileStorages.finishSetUp(); - } - dispatchStreamValidateAndCommit(); } @@ -1052,11 +1036,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private void handleStreamValidateAndCommit() { - // TODO(b/136132412): update with new APIs - if (mIncrementalFileStorages != null) { - mIncrementalFileStorages.startLoading(); - } - boolean success = streamValidateAndCommit(); if (isMultiPackage()) { @@ -2476,17 +2455,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - if (mIncrementalFileStorages != null) { - for (InstallationFile file : addedFiles) { - try { - mIncrementalFileStorages.addFile(file); - } catch (IOException ex) { - // TODO(b/146080380): add incremental-specific error code - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Failed to add and configure Incremental File: " + file.getName(), ex); - } + // 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); } - return true; } final DataLoaderManager dataLoaderManager = mContext.getSystemService( @@ -2761,13 +2738,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { bridge.forceClose(); } } + if (mIncrementalFileStorages != null) { + mIncrementalFileStorages.cleanUp(); + mIncrementalFileStorages = null; + } // For staged sessions, we don't delete the directory where the packages have been copied, // since these packages are supposed to be read on reboot. // Those dirs are deleted when the staged session has reached a final state. if (stageDir != null && !params.isStaged) { - if (mIncrementalFileStorages != null) { - mIncrementalFileStorages.cleanUp(); - } try { mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); } catch (InstallerException ignored) { @@ -2783,6 +2761,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } else { if (mIncrementalFileStorages != null) { mIncrementalFileStorages.cleanUp(); + mIncrementalFileStorages = null; } try { mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());