Loading services/core/java/com/android/server/pm/PackageInstallerService.java +44 −9 Original line number Original line Diff line number Diff line Loading @@ -135,6 +135,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50; private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50; /** Upper bound on number of historical sessions for a UID */ /** Upper bound on number of historical sessions for a UID */ private static final long MAX_HISTORICAL_SESSIONS = 1048576; private static final long MAX_HISTORICAL_SESSIONS = 1048576; /** Destroy sessions older than this on storage free request */ private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS; /** /** * Allow verification-skipping if it's a development app installed through ADB with * Allow verification-skipping if it's a development app installed through ADB with Loading Loading @@ -334,22 +336,28 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @GuardedBy("mSessions") @GuardedBy("mSessions") private void reconcileStagesLocked(String volumeUuid) { private void reconcileStagesLocked(String volumeUuid) { final File stagingDir = getTmpSessionDir(volumeUuid); final ArraySet<File> unclaimedStages = getStagingDirsOnVolume(volumeUuid); final ArraySet<File> unclaimedStages = newArraySet( stagingDir.listFiles(sStageFilter)); // We also need to clean up orphaned staging directory for staged sessions final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles())); // Ignore stages claimed by active sessions // Ignore stages claimed by active sessions for (int i = 0; i < mSessions.size(); i++) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); final PackageInstallerSession session = mSessions.valueAt(i); unclaimedStages.remove(session.stageDir); unclaimedStages.remove(session.stageDir); } } removeStagingDirs(unclaimedStages); } private ArraySet<File> getStagingDirsOnVolume(String volumeUuid) { final File stagingDir = getTmpSessionDir(volumeUuid); final ArraySet<File> stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter)); // We also need to clean up orphaned staging directory for staged sessions final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles())); return stagingDirs; } private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) { // Clean up orphaned staging directories // Clean up orphaned staging directories for (File stage : unclaimedStages) { for (File stage : stagingDirsToRemove) { Slog.w(TAG, "Deleting orphan stage " + stage); Slog.w(TAG, "Deleting orphan stage " + stage); synchronized (mPm.mInstallLock) { synchronized (mPm.mInstallLock) { mPm.removeCodePathLI(stage); mPm.removeCodePathLI(stage); Loading @@ -363,6 +371,33 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } } } /** * Called to free up some storage space from obsolete installation files */ public void freeStageDirs(String volumeUuid) { final ArraySet<File> unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid); final long currentTimeMillis = System.currentTimeMillis(); synchronized (mSessions) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) { // Only handles sessions stored on the target volume continue; } final long age = currentTimeMillis - session.createdMillis; if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) { // Aggressively close old sessions because we are running low on storage // Their staging dirs will be removed too session.abandon(); } else { // Session is new enough, so it deserves to be kept even on low storage unclaimedStagingDirsOnVolume.remove(session.stageDir); } } } removeStagingDirs(unclaimedStagingDirsOnVolume); } public static boolean isStageName(String name) { public static boolean isStageName(String name) { final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -673,7 +673,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final Runnable r; final Runnable r; synchronized (mLock) { synchronized (mLock) { assertNotChildLocked("StagedSession#abandon"); assertNotChildLocked("StagedSession#abandon"); assertCallerIsOwnerOrRoot(); assertCallerIsOwnerOrRootOrSystem(); if (isInTerminalState()) { if (isInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be // We keep the session in the database if it's in a finalized state. It will be // removed by PackageInstallerService when the last update time is old enough. // removed by PackageInstallerService when the last update time is old enough. Loading Loading @@ -3704,7 +3704,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void abandonNonStaged() { private void abandonNonStaged() { synchronized (mLock) { synchronized (mLock) { assertNotChildLocked("abandonNonStaged"); assertNotChildLocked("abandonNonStaged"); assertCallerIsOwnerOrRoot(); assertCallerIsOwnerOrRootOrSystem(); if (mRelinquished) { if (mRelinquished) { if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control"); if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control"); return; return; Loading services/core/java/com/android/server/pm/PackageManagerService.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -9296,6 +9296,10 @@ public class PackageManagerService extends IPackageManager.Stub if (freeBytesRequired > 0) { if (freeBytesRequired > 0) { smInternal.freeCache(volumeUuid, freeBytesRequired); smInternal.freeCache(volumeUuid, freeBytesRequired); } } // 12. Clear temp install session files mInstallerService.freeStageDirs(volumeUuid); if (file.getUsableSpace() >= bytes) return; if (file.getUsableSpace() >= bytes) return; } else { } else { try { try { Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +44 −9 Original line number Original line Diff line number Diff line Loading @@ -135,6 +135,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50; private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50; /** Upper bound on number of historical sessions for a UID */ /** Upper bound on number of historical sessions for a UID */ private static final long MAX_HISTORICAL_SESSIONS = 1048576; private static final long MAX_HISTORICAL_SESSIONS = 1048576; /** Destroy sessions older than this on storage free request */ private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS; /** /** * Allow verification-skipping if it's a development app installed through ADB with * Allow verification-skipping if it's a development app installed through ADB with Loading Loading @@ -334,22 +336,28 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @GuardedBy("mSessions") @GuardedBy("mSessions") private void reconcileStagesLocked(String volumeUuid) { private void reconcileStagesLocked(String volumeUuid) { final File stagingDir = getTmpSessionDir(volumeUuid); final ArraySet<File> unclaimedStages = getStagingDirsOnVolume(volumeUuid); final ArraySet<File> unclaimedStages = newArraySet( stagingDir.listFiles(sStageFilter)); // We also need to clean up orphaned staging directory for staged sessions final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles())); // Ignore stages claimed by active sessions // Ignore stages claimed by active sessions for (int i = 0; i < mSessions.size(); i++) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); final PackageInstallerSession session = mSessions.valueAt(i); unclaimedStages.remove(session.stageDir); unclaimedStages.remove(session.stageDir); } } removeStagingDirs(unclaimedStages); } private ArraySet<File> getStagingDirsOnVolume(String volumeUuid) { final File stagingDir = getTmpSessionDir(volumeUuid); final ArraySet<File> stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter)); // We also need to clean up orphaned staging directory for staged sessions final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles())); return stagingDirs; } private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) { // Clean up orphaned staging directories // Clean up orphaned staging directories for (File stage : unclaimedStages) { for (File stage : stagingDirsToRemove) { Slog.w(TAG, "Deleting orphan stage " + stage); Slog.w(TAG, "Deleting orphan stage " + stage); synchronized (mPm.mInstallLock) { synchronized (mPm.mInstallLock) { mPm.removeCodePathLI(stage); mPm.removeCodePathLI(stage); Loading @@ -363,6 +371,33 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } } } /** * Called to free up some storage space from obsolete installation files */ public void freeStageDirs(String volumeUuid) { final ArraySet<File> unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid); final long currentTimeMillis = System.currentTimeMillis(); synchronized (mSessions) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) { // Only handles sessions stored on the target volume continue; } final long age = currentTimeMillis - session.createdMillis; if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) { // Aggressively close old sessions because we are running low on storage // Their staging dirs will be removed too session.abandon(); } else { // Session is new enough, so it deserves to be kept even on low storage unclaimedStagingDirsOnVolume.remove(session.stageDir); } } } removeStagingDirs(unclaimedStagingDirsOnVolume); } public static boolean isStageName(String name) { public static boolean isStageName(String name) { final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -673,7 +673,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final Runnable r; final Runnable r; synchronized (mLock) { synchronized (mLock) { assertNotChildLocked("StagedSession#abandon"); assertNotChildLocked("StagedSession#abandon"); assertCallerIsOwnerOrRoot(); assertCallerIsOwnerOrRootOrSystem(); if (isInTerminalState()) { if (isInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be // We keep the session in the database if it's in a finalized state. It will be // removed by PackageInstallerService when the last update time is old enough. // removed by PackageInstallerService when the last update time is old enough. Loading Loading @@ -3704,7 +3704,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void abandonNonStaged() { private void abandonNonStaged() { synchronized (mLock) { synchronized (mLock) { assertNotChildLocked("abandonNonStaged"); assertNotChildLocked("abandonNonStaged"); assertCallerIsOwnerOrRoot(); assertCallerIsOwnerOrRootOrSystem(); if (mRelinquished) { if (mRelinquished) { if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control"); if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control"); return; return; Loading
services/core/java/com/android/server/pm/PackageManagerService.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -9296,6 +9296,10 @@ public class PackageManagerService extends IPackageManager.Stub if (freeBytesRequired > 0) { if (freeBytesRequired > 0) { smInternal.freeCache(volumeUuid, freeBytesRequired); smInternal.freeCache(volumeUuid, freeBytesRequired); } } // 12. Clear temp install session files mInstallerService.freeStageDirs(volumeUuid); if (file.getUsableSpace() >= bytes) return; if (file.getUsableSpace() >= bytes) return; } else { } else { try { try {