diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index a40fe7558cbae..734d43503e7b8 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -460,7 +460,8 @@ class ContextImpl extends Context { registerService(STORAGE_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { try { - return new StorageManager(ctx.mMainThread.getHandler().getLooper()); + return new StorageManager( + ctx.getContentResolver(), ctx.mMainThread.getHandler().getLooper()); } catch (RemoteException rex) { Log.e(TAG, "Failed to create StorageManager", rex); return null; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 862a95cb9d139..f5e728dc565c1 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -16,7 +16,9 @@ package android.os.storage; -import android.app.NotificationManager; +import static android.net.TrafficStats.MB_IN_BYTES; + +import android.content.ContentResolver; import android.content.Context; import android.os.Environment; import android.os.Handler; @@ -25,6 +27,7 @@ import android.os.Message; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.provider.Settings; import android.util.Log; import android.util.SparseArray; @@ -54,20 +57,20 @@ import java.util.concurrent.atomic.AtomicInteger; * {@link android.content.Context#getSystemService(java.lang.String)} with an * argument of {@link android.content.Context#STORAGE_SERVICE}. */ - -public class StorageManager -{ +public class StorageManager { private static final String TAG = "StorageManager"; + private final ContentResolver mResolver; + /* * Our internal MountService binder reference */ - final private IMountService mMountService; + private final IMountService mMountService; /* * The looper target for callbacks */ - Looper mTgtLooper; + private final Looper mTgtLooper; /* * Target listener for binder callbacks @@ -308,16 +311,16 @@ public class StorageManager * * @hide */ - public StorageManager(Looper tgtLooper) throws RemoteException { + public StorageManager(ContentResolver resolver, Looper tgtLooper) throws RemoteException { + mResolver = resolver; + mTgtLooper = tgtLooper; mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); if (mMountService == null) { Log.e(TAG, "Unable to connect to mount service! - is it running yet?"); return; } - mTgtLooper = tgtLooper; } - /** * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. * @@ -610,4 +613,36 @@ public class StorageManager Log.w(TAG, "No primary storage defined"); return null; } + + private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; + private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; + private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; + + /** + * Return the number of available bytes at which the given path is + * considered running low on storage. + * + * @hide + */ + public long getStorageLowBytes(File path) { + final long lowPercent = Settings.Global.getInt(mResolver, + Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); + final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; + + final long maxLowBytes = Settings.Global.getLong(mResolver, + Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); + + return Math.min(lowBytes, maxLowBytes); + } + + /** + * Return the number of available bytes at which the given path is + * considered full. + * + * @hide + */ + public long getStorageFullBytes(File path) { + return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, + DEFAULT_FULL_THRESHOLD_BYTES); + } } diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index 94a087a0c7564..016c561077dc1 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -16,9 +16,6 @@ package com.android.server; -import java.io.FileDescriptor; -import java.io.PrintWriter; - import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -40,12 +37,17 @@ import android.os.StatFs; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.provider.Settings; import android.text.format.Formatter; import android.util.EventLog; import android.util.Slog; import android.util.TimeUtils; +import java.io.File; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * This class implements a service to monitor the amount of disk * storage space on the device. If the free storage on device is less @@ -66,17 +68,18 @@ import android.util.TimeUtils; */ public class DeviceStorageMonitorService extends Binder { private static final String TAG = "DeviceStorageMonitorService"; + private static final boolean DEBUG = false; private static final boolean localLOGV = false; + private static final int DEVICE_MEMORY_WHAT = 1; private static final int MONITOR_INTERVAL = 1; //in minutes private static final int LOW_MEMORY_NOTIFICATION_ID = 1; - private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; - private static final int DEFAULT_THRESHOLD_MAX_BYTES = 500*1024*1024; // 500MB + private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000; - private static final int DEFAULT_FULL_THRESHOLD_BYTES = 1024*1024; // 1MB + private long mFreeMem; // on /data private long mFreeMemAfterLastCacheClear; // on /data private long mLastReportedFreeMem; @@ -84,14 +87,16 @@ public class DeviceStorageMonitorService extends Binder { private boolean mLowMemFlag=false; private boolean mMemFullFlag=false; private Context mContext; - private ContentResolver mContentResolver; + private ContentResolver mResolver; private long mTotalMemory; // on /data private StatFs mDataFileStats; private StatFs mSystemFileStats; private StatFs mCacheFileStats; - private static final String DATA_PATH = "/data"; - private static final String SYSTEM_PATH = "/system"; - private static final String CACHE_PATH = "/cache"; + + private static final File DATA_PATH = Environment.getDataDirectory(); + private static final File SYSTEM_PATH = Environment.getRootDirectory(); + private static final File CACHE_PATH = Environment.getDownloadCacheDirectory(); + private long mThreadStartTime = -1; private boolean mClearSucceeded = false; private boolean mClearingCache; @@ -116,7 +121,7 @@ public class DeviceStorageMonitorService extends Binder { // more files than absolutely needed, to reduce the frequency that // flushing takes place. private long mMemCacheTrimToThreshold; - private int mMemFullThreshold; + private long mMemFullThreshold; /** * This string is used for ServiceManager access to this class. @@ -151,7 +156,7 @@ public class DeviceStorageMonitorService extends Binder { private final void restatDataDir() { try { - mDataFileStats.restat(DATA_PATH); + mDataFileStats.restat(DATA_PATH.getAbsolutePath()); mFreeMem = (long) mDataFileStats.getAvailableBlocks() * mDataFileStats.getBlockSize(); } catch (IllegalArgumentException e) { @@ -163,7 +168,7 @@ public class DeviceStorageMonitorService extends Binder { mFreeMem = Long.parseLong(debugFreeMem); } // Read the log interval from secure settings - long freeMemLogInterval = Settings.Global.getLong(mContentResolver, + long freeMemLogInterval = Settings.Global.getLong(mResolver, Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL, DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000; //log the amount of free memory in event log @@ -173,14 +178,14 @@ public class DeviceStorageMonitorService extends Binder { mLastReportedFreeMemTime = currTime; long mFreeSystem = -1, mFreeCache = -1; try { - mSystemFileStats.restat(SYSTEM_PATH); + mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath()); mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() * mSystemFileStats.getBlockSize(); } catch (IllegalArgumentException e) { // ignore; report -1 } try { - mCacheFileStats.restat(CACHE_PATH); + mCacheFileStats.restat(CACHE_PATH.getAbsolutePath()); mFreeCache = (long) mCacheFileStats.getAvailableBlocks() * mCacheFileStats.getBlockSize(); } catch (IllegalArgumentException e) { @@ -190,7 +195,7 @@ public class DeviceStorageMonitorService extends Binder { mFreeMem, mFreeSystem, mFreeCache); } // Read the reporting threshold from secure settings - long threshold = Settings.Global.getLong(mContentResolver, + long threshold = Settings.Global.getLong(mResolver, Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD, DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD); // If mFree changed significantly log the new value @@ -303,40 +308,6 @@ public class DeviceStorageMonitorService extends Binder { delay); } - /* - * just query settings to retrieve the memory threshold. - * Preferred this over using a ContentObserver since Settings.Secure caches the value - * any way - */ - private long getMemThreshold() { - long value = Settings.Global.getInt( - mContentResolver, - Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, - DEFAULT_THRESHOLD_PERCENTAGE); - if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value); - value = (value*mTotalMemory)/100; - long maxValue = Settings.Global.getInt( - mContentResolver, - Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, - DEFAULT_THRESHOLD_MAX_BYTES); - //evaluate threshold value - return value < maxValue ? value : maxValue; - } - - /* - * just query settings to retrieve the memory full threshold. - * Preferred this over using a ContentObserver since Settings.Secure caches the value - * any way - */ - private int getMemFullThreshold() { - int value = Settings.Global.getInt( - mContentResolver, - Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, - DEFAULT_FULL_THRESHOLD_BYTES); - if(localLOGV) Slog.v(TAG, "Full Threshold Bytes="+value); - return value; - } - /** * Constructor to run service. initializes the disk space threshold value * and posts an empty message to kickstart the process. @@ -344,11 +315,11 @@ public class DeviceStorageMonitorService extends Binder { public DeviceStorageMonitorService(Context context) { mLastReportedFreeMemTime = 0; mContext = context; - mContentResolver = mContext.getContentResolver(); + mResolver = mContext.getContentResolver(); //create StatFs object - mDataFileStats = new StatFs(DATA_PATH); - mSystemFileStats = new StatFs(SYSTEM_PATH); - mCacheFileStats = new StatFs(CACHE_PATH); + mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath()); + mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath()); + mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath()); //initialize total storage on device mTotalMemory = (long)mDataFileStats.getBlockCount() * mDataFileStats.getBlockSize(); @@ -360,9 +331,12 @@ public class DeviceStorageMonitorService extends Binder { mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL); mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + // cache storage thresholds - mMemLowThreshold = getMemThreshold(); - mMemFullThreshold = getMemFullThreshold(); + final StorageManager sm = StorageManager.from(context); + mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH); + mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH); + mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4; mMemCacheTrimToThreshold = mMemLowThreshold + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2); @@ -373,7 +347,6 @@ public class DeviceStorageMonitorService extends Binder { mCacheFileDeletedObserver.startWatching(); } - /** * This method sends a notification to NotificationManager to display * an error dialog indicating low disk space and launch the Installer