Merge "Better API for low disk space warnings."
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a40fe75..734d435 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -460,7 +460,8 @@
         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 862a95c..f5e728d 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.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 @@
  * {@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 @@
      *
      * @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 @@
         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 94a087a..016c561 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.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 @@
  */
 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 @@
     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 @@
     // 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 @@
 
     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 @@
             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 @@
             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 @@
                                 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 @@
                 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 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 @@
         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 @@
         mCacheFileDeletedObserver.startWatching();
     }
 
-
     /**
     * This method sends a notification to NotificationManager to display
     * an error dialog indicating low disk space and launch the Installer