diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index a3803447083a3..47be7e66db9e5 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -100,6 +100,8 @@ public class Installer extends SystemService { public static final int FLAG_FREE_CACHE_V2 = IInstalld.FLAG_FREE_CACHE_V2; public static final int FLAG_FREE_CACHE_V2_DEFY_QUOTA = IInstalld.FLAG_FREE_CACHE_V2_DEFY_QUOTA; public static final int FLAG_FREE_CACHE_NOOP = IInstalld.FLAG_FREE_CACHE_NOOP; + public static final int FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES = + IInstalld.FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES; public static final int FLAG_USE_QUOTA = IInstalld.FLAG_USE_QUOTA; public static final int FLAG_FORCE = IInstalld.FLAG_FORCE; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e491ec5be70ab..f3fe2c07d3b4e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2779,6 +2779,23 @@ public class PackageManagerService extends IPackageManager.Stub }); } + /** + * Blocking call to clear all cached app data above quota. + */ + public void freeAllAppCacheAboveQuota(String volumeUuid) throws IOException { + synchronized (mInstallLock) { + // To avoid refactoring Installer.freeCache() and InstalldNativeService.freeCache(), + // Long.MAX_VALUE is passed as an argument which is used in neither of two methods + // when FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES is set + try { + mInstaller.freeCache(volumeUuid, Long.MAX_VALUE, Installer.FLAG_FREE_CACHE_V2 + | Installer.FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES); + } catch (InstallerException ignored) { + } + } + return; + } + /** * Blocking call to clear various types of cached data across the system * until the requested bytes are available. diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java index da56824723184..3093509428d3e 100644 --- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java +++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java @@ -75,10 +75,12 @@ public class DeviceStorageMonitorService extends SystemService { */ public static final String EXTRA_SEQUENCE = "seq"; - private static final int MSG_CHECK = 1; + private static final int MSG_CHECK_LOW = 1; + private static final int MSG_CHECK_HIGH = 2; private static final long DEFAULT_LOG_DELTA_BYTES = DataUnit.MEBIBYTES.toBytes(64); - private static final long DEFAULT_CHECK_INTERVAL = DateUtils.MINUTE_IN_MILLIS; + private static final long LOW_CHECK_INTERVAL = DateUtils.MINUTE_IN_MILLIS; + private static final long HIGH_CHECK_INTERVAL = 10 * DateUtils.HOUR_IN_MILLIS; // com.android.internal.R.string.low_internal_storage_view_text_no_boot // hard codes 250MB in the message as the storage space required for the @@ -165,12 +167,12 @@ public class DeviceStorageMonitorService extends SystemService { } /** - * Core logic that checks the storage state of every mounted private volume. - * Since this can do heavy I/O, callers should invoke indirectly using - * {@link #MSG_CHECK}. + * Core logic that checks the storage state of every mounted private volume and clears cache + * under low storage state. Since this can do heavy I/O, callers should invoke indirectly using + * {@link #MSG_CHECK_LOW}. */ @WorkerThread - private void check() { + private void checkLow() { final StorageManager storage = getContext().getSystemService(StorageManager.class); final int seq = mSeq.get(); @@ -234,9 +236,45 @@ public class DeviceStorageMonitorService extends SystemService { // Loop around to check again in future; we don't remove messages since // there might be an immediate request pending. - if (!mHandler.hasMessages(MSG_CHECK)) { - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK), - DEFAULT_CHECK_INTERVAL); + if (!mHandler.hasMessages(MSG_CHECK_LOW)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_LOW), + LOW_CHECK_INTERVAL); + } + if (!mHandler.hasMessages(MSG_CHECK_HIGH)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_HIGH), + HIGH_CHECK_INTERVAL); + } + } + + /** + * Core logic that checks the storage state of every mounted private volume and clears cache if + * free space is under 20% of total space. Since this can do heavy I/O, callers should invoke + * indirectly using {@link #MSG_CHECK_HIGH}. + */ + @WorkerThread + private void checkHigh() { + final StorageManager storage = getContext().getSystemService(StorageManager.class); + // Check every mounted private volume to see if they're under the high storage threshold + // which is StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space + for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { + final File file = vol.getPath(); + if (file.getUsableSpace() < file.getTotalSpace() + * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) { + final PackageManagerService pms = (PackageManagerService) ServiceManager + .getService("package"); + try { + pms.freeAllAppCacheAboveQuota(vol.getFsUuid()); + } catch (IOException e) { + Slog.w(TAG, e); + } + } + } + + // Loop around to check again in future; we don't remove messages since + // there might be an immediate request pending + if (!mHandler.hasMessages(MSG_CHECK_HIGH)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_HIGH), + HIGH_CHECK_INTERVAL); } } @@ -250,8 +288,11 @@ public class DeviceStorageMonitorService extends SystemService { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_CHECK: - check(); + case MSG_CHECK_LOW: + checkLow(); + return; + case MSG_CHECK_HIGH: + checkHigh(); return; } } @@ -282,16 +323,16 @@ public class DeviceStorageMonitorService extends SystemService { publishLocalService(DeviceStorageMonitorInternal.class, mLocalService); // Kick off pass to examine storage state - mHandler.removeMessages(MSG_CHECK); - mHandler.obtainMessage(MSG_CHECK).sendToTarget(); + mHandler.removeMessages(MSG_CHECK_LOW); + mHandler.obtainMessage(MSG_CHECK_LOW).sendToTarget(); } private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() { @Override public void checkMemory() { // Kick off pass to examine storage state - mHandler.removeMessages(MSG_CHECK); - mHandler.obtainMessage(MSG_CHECK).sendToTarget(); + mHandler.removeMessages(MSG_CHECK_LOW); + mHandler.obtainMessage(MSG_CHECK_LOW).sendToTarget(); } @Override @@ -360,8 +401,8 @@ public class DeviceStorageMonitorService extends SystemService { mForceLevel = State.LEVEL_LOW; int seq = mSeq.incrementAndGet(); if ((opts & OPTION_FORCE_UPDATE) != 0) { - mHandler.removeMessages(MSG_CHECK); - mHandler.obtainMessage(MSG_CHECK).sendToTarget(); + mHandler.removeMessages(MSG_CHECK_LOW); + mHandler.obtainMessage(MSG_CHECK_LOW).sendToTarget(); pw.println(seq); } } break; @@ -372,8 +413,8 @@ public class DeviceStorageMonitorService extends SystemService { mForceLevel = State.LEVEL_NORMAL; int seq = mSeq.incrementAndGet(); if ((opts & OPTION_FORCE_UPDATE) != 0) { - mHandler.removeMessages(MSG_CHECK); - mHandler.obtainMessage(MSG_CHECK).sendToTarget(); + mHandler.removeMessages(MSG_CHECK_LOW); + mHandler.obtainMessage(MSG_CHECK_LOW).sendToTarget(); pw.println(seq); } } break; @@ -384,8 +425,8 @@ public class DeviceStorageMonitorService extends SystemService { mForceLevel = State.LEVEL_UNKNOWN; int seq = mSeq.incrementAndGet(); if ((opts & OPTION_FORCE_UPDATE) != 0) { - mHandler.removeMessages(MSG_CHECK); - mHandler.obtainMessage(MSG_CHECK).sendToTarget(); + mHandler.removeMessages(MSG_CHECK_LOW); + mHandler.obtainMessage(MSG_CHECK_LOW).sendToTarget(); pw.println(seq); } } break;