Loading core/java/android/os/incremental/IncrementalFileStorages.java +88 −67 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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; Loading @@ -61,22 +67,68 @@ 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<InstallationFile> 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; if (TextUtils.isEmpty(mDefaultDir)) { throw new IOException("Failed to create storage: incrementalPath is empty"); } mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); } else { mDefaultDir = getTempDir(); if (mDefaultDir == null) { return; throw new IOException("Failed to create storage: tempDir is empty"); } mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, dataLoaderParams, Loading @@ -84,18 +136,8 @@ public final class IncrementalFileStorages { | 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); } else { throw new IOException("Unknown file type: " + file.getFileType()); throw new IOException("Failed to create storage"); } } Loading @@ -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() { } /** Loading @@ -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) { 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) { Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +13 −34 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -1040,10 +1028,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } if (mIncrementalFileStorages != null) { mIncrementalFileStorages.finishSetUp(); } dispatchStreamValidateAndCommit(); } Loading @@ -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()) { Loading Loading @@ -2476,17 +2455,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } if (mIncrementalFileStorages != null) { for (InstallationFile file : addedFiles) { // TODO(b/136132412): update with new APIs if (isIncrementalInstallation()) { 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); } } mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, params.dataLoaderParams, addedFiles); return true; } catch (IOException e) { throw new PackageManagerException(e); } } final DataLoaderManager dataLoaderManager = mContext.getSystemService( Loading Loading @@ -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) { Loading @@ -2783,6 +2761,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } else { if (mIncrementalFileStorages != null) { mIncrementalFileStorages.cleanUp(); mIncrementalFileStorages = null; } try { mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); Loading Loading
core/java/android/os/incremental/IncrementalFileStorages.java +88 −67 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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; Loading @@ -61,22 +67,68 @@ 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<InstallationFile> 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; if (TextUtils.isEmpty(mDefaultDir)) { throw new IOException("Failed to create storage: incrementalPath is empty"); } mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); } else { mDefaultDir = getTempDir(); if (mDefaultDir == null) { return; throw new IOException("Failed to create storage: tempDir is empty"); } mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, dataLoaderParams, Loading @@ -84,18 +136,8 @@ public final class IncrementalFileStorages { | 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); } else { throw new IOException("Unknown file type: " + file.getFileType()); throw new IOException("Failed to create storage"); } } Loading @@ -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() { } /** Loading @@ -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) { 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) { Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +13 −34 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -1040,10 +1028,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } if (mIncrementalFileStorages != null) { mIncrementalFileStorages.finishSetUp(); } dispatchStreamValidateAndCommit(); } Loading @@ -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()) { Loading Loading @@ -2476,17 +2455,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } if (mIncrementalFileStorages != null) { for (InstallationFile file : addedFiles) { // TODO(b/136132412): update with new APIs if (isIncrementalInstallation()) { 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); } } mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, params.dataLoaderParams, addedFiles); return true; } catch (IOException e) { throw new PackageManagerException(e); } } final DataLoaderManager dataLoaderManager = mContext.getSystemService( Loading Loading @@ -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) { Loading @@ -2783,6 +2761,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } else { if (mIncrementalFileStorages != null) { mIncrementalFileStorages.cleanUp(); mIncrementalFileStorages = null; } try { mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); Loading