From cf9d19e030830fd808d59f1c97edf65e66f675d6 Mon Sep 17 00:00:00 2001 From: Daniel Nishi Date: Mon, 23 Jan 2017 14:33:42 -0800 Subject: First pass at adding the cache quota suggestions. This currently integrates with installd, but not with any framework API to expose this information to apps. The first pass, as per the design doc, adds a service which polls for large changes in the file system free space. If enough spaces changes, it begins a recalculation of the cache quotas and pipes the information down to installd. This calculation is done in the updateable ExtServices. Further enhancements in later patches include integrating this to listen to package install and removal events, caching the last computed quota values into an XML file on disk to load on boot, and exposing the information to apps. Bug: 33965858 Test: ExtServices unit test Change-Id: Ie39f228b73532cb6ce2f98529f7c5df0839202ae --- .../android/server/usage/StorageStatsService.java | 74 ++++++++++++++++++++++ .../android/server/usage/UsageStatsService.java | 7 ++ 2 files changed, 81 insertions(+) (limited to 'services/usage/java') diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 68269751efc1..96907c33ca45 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -20,6 +20,7 @@ import android.app.AppOpsManager; import android.app.usage.ExternalStorageStats; import android.app.usage.IStorageStatsManager; import android.app.usage.StorageStats; +import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -28,25 +29,35 @@ import android.content.pm.PackageStats; import android.content.pm.UserInfo; import android.os.Binder; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.StatFs; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.text.format.DateUtils; import android.util.Slog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; +import com.android.server.IoThread; +import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; +import com.android.server.storage.CacheQuotaStrategy; public class StorageStatsService extends IStorageStatsManager.Stub { private static final String TAG = "StorageStatsService"; private static final String PROP_VERIFY_STORAGE = "fw.verify_storage"; + private static final long DELAY_IN_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS; + public static class Lifecycle extends SystemService { private StorageStatsService mService; @@ -68,6 +79,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { private final StorageManager mStorage; private final Installer mInstaller; + private final H mHandler; public StorageStatsService(Context context) { mContext = Preconditions.checkNotNull(context); @@ -80,6 +92,9 @@ public class StorageStatsService extends IStorageStatsManager.Stub { mInstaller.onStart(); invalidateMounts(); + mHandler = new H(IoThread.get().getLooper()); + mHandler.sendEmptyMessageDelayed(H.MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS); + mStorage.registerListener(new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { @@ -274,4 +289,63 @@ public class StorageStatsService extends IStorageStatsManager.Stub { res.cacheBytes = stats.cacheSize + stats.externalCacheSize; return res; } + + private class H extends Handler { + private static final int MSG_CHECK_STORAGE_DELTA = 100; + /** + * By only triggering a re-calculation after the storage has changed sizes, we can avoid + * recalculating quotas too often. Minimum change delta defines the percentage of change + * we need to see before we recalculate. + */ + private static final double MINIMUM_CHANGE_DELTA = 0.05; + private static final boolean DEBUG = false; + + private final StatFs mStats; + private long mPreviousBytes; + private double mMinimumThresholdBytes; + + public H(Looper looper) { + super(looper); + // TODO: Handle all private volumes. + mStats = new StatFs(Environment.getDataDirectory().getAbsolutePath()); + mPreviousBytes = mStats.getFreeBytes(); + mMinimumThresholdBytes = mStats.getTotalBytes() * MINIMUM_CHANGE_DELTA; + // TODO: Load cache quotas from a file to avoid re-doing work. + } + + public void handleMessage(Message msg) { + if (DEBUG) { + Slog.v(TAG, ">>> handling " + msg.what); + } + switch (msg.what) { + case MSG_CHECK_STORAGE_DELTA: { + long bytesDelta = Math.abs(mPreviousBytes - mStats.getFreeBytes()); + if (bytesDelta > mMinimumThresholdBytes) { + mPreviousBytes = mStats.getFreeBytes(); + recalculateQuotas(); + } + sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS); + break; + } + default: + if (DEBUG) { + Slog.v(TAG, ">>> default message case "); + } + return; + } + } + + private void recalculateQuotas() { + if (DEBUG) { + Slog.v(TAG, ">>> recalculating quotas "); + } + + UsageStatsManagerInternal usageStatsManager = + LocalServices.getService(UsageStatsManagerInternal.class); + CacheQuotaStrategy strategy = new CacheQuotaStrategy( + mContext, usageStatsManager, mInstaller); + // TODO: Save cache quotas to an XML file. + strategy.recalculateQuotas(); + } + } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 7a69803c8463..3c743b5cb355 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1603,5 +1603,12 @@ public class UsageStatsService extends SystemService implements userStats.applyRestoredPayload(key, payload); } } + + @Override + public List queryUsageStatsForUser( + int userId, int intervalType, long beginTime, long endTime) { + return UsageStatsService.this.queryUsageStats( + userId, intervalType, beginTime, endTime); + } } } -- cgit v1.2.3-59-g8ed1b