FileDocCategorySizeDatePackage
DeviceStorageMonitorService.javaAPI DocAndroid 1.5 API13924Wed May 06 22:42:00 BST 2009com.android.server

DeviceStorageMonitorService

public class DeviceStorageMonitorService extends android.os.Binder
This class implements a service to monitor the amount of disk storage space on the device. If the free storage on device is less than a tunable threshold value (default is 10%. this value is a gservices parameter) a low memory notification is displayed to alert the user. If the user clicks on the low memory notification the Application Manager application gets launched to let the user free storage space. Event log events: A low memory event with the free storage on device in bytes is logged to the event log when the device goes low on storage space. The amount of free storage on the device is periodically logged to the event log. The log interval is a gservices parameter with a default value of 12 hours When the free storage differential goes below a threshold(again a gservices parameter with a default value of 2MB), the free memory is logged to the event log

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final boolean
localLOGV
private static final int
DEVICE_MEMORY_WHAT
private static final int
MONITOR_INTERVAL
private static final int
LOW_MEMORY_NOTIFICATION_ID
private static final int
DEFAULT_THRESHOLD_PERCENTAGE
private static final int
DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES
private static final int
EVENT_LOG_STORAGE_BELOW_THRESHOLD
private static final int
EVENT_LOG_LOW_STORAGE_NOTIFICATION
private static final int
EVENT_LOG_FREE_STORAGE_LEFT
private static final long
DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD
private static final long
DEFAULT_CHECK_INTERVAL
private long
mFreeMem
private long
mLastReportedFreeMem
private long
mLastReportedFreeMemTime
private boolean
mLowMemFlag
private android.content.Context
mContext
private android.content.ContentResolver
mContentResolver
int
mBlkSize
long
mTotalMemory
android.os.StatFs
mFileStats
private static final String
DATA_PATH
long
mThreadStartTime
boolean
mClearSucceeded
boolean
mClearingCache
private android.content.Intent
mStorageLowIntent
private android.content.Intent
mStorageOkIntent
private CachePackageDataObserver
mClearCacheObserver
private static final int
_TRUE
private static final int
_FALSE
static final String
SERVICE
This string is used for ServiceManager access to this class.
android.os.Handler
mHandler
Handler that checks the amount of disk space on the device and sends a notification if the device runs low on disk space
Constructors Summary
public DeviceStorageMonitorService(android.content.Context context)
Constructor to run service. initializes the disk space threshold value and posts an empty message to kickstart the process.

        mLastReportedFreeMemTime = 0;
        mContext = context;
        mContentResolver = mContext.getContentResolver();
        //create StatFs object
        mFileStats = new StatFs(DATA_PATH);
        //initialize block size
        mBlkSize = mFileStats.getBlockSize();
        //initialize total storage on device
        mTotalMemory = (mFileStats.getBlockCount()*mBlkSize)/100;
        mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
        mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
        checkMemory(true);
    
Methods Summary
private final voidcancelNotification()
Cancels low storage notification and sends OK intent.

        if(localLOGV) Log.i(TAG, "Canceling low memory notification");
        NotificationManager mNotificationMgr =
                (NotificationManager)mContext.getSystemService(
                        Context.NOTIFICATION_SERVICE);
        //cancel notification since memory has been freed
        mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID);

        mContext.removeStickyBroadcast(mStorageLowIntent);
        mContext.sendBroadcast(mStorageOkIntent);
    
private final voidcheckMemory(boolean checkCache)

        //if the thread that was started to clear cache is still running do nothing till its 
        //finished clearing cache. Ideally this flag could be modified by clearCache 
        // and should be accessed via a lock but even if it does this test will fail now and
        //hopefully the next time this flag will be set to the correct value.
        if(mClearingCache) {
            if(localLOGV) Log.i(TAG, "Thread already running just skip");
            //make sure the thread is not hung for too long
            long diffTime = System.currentTimeMillis() - mThreadStartTime;
            if(diffTime > (10*60*1000)) {
                Log.w(TAG, "Thread that clears cache file seems to run for ever");
            } 
        } else {
            restatDataDir();
            if (localLOGV)  Log.v(TAG, "freeMemory="+mFreeMem);
            
            //post intent to NotificationManager to display icon if necessary
            long memThreshold = getMemThreshold();
            if (mFreeMem < memThreshold) {
                if (!mLowMemFlag) {
                    if (checkCache) {
                        // See if clearing cache helps
                        // Note that clearing cache is asynchronous and so we do a
                        // memory check again once the cache has been cleared.
                        mThreadStartTime = System.currentTimeMillis();
                        mClearSucceeded = false;
                        clearCache();
                    } else {
                        Log.i(TAG, "Running low on memory. Sending notification");
                        sendNotification();
                        mLowMemFlag = true;
                    }
                } else {
                    if (localLOGV) Log.v(TAG, "Running low on memory " +
                            "notification already sent. do nothing");
                }
            } else {
                if (mLowMemFlag) {
                    Log.i(TAG, "Memory available. Cancelling notification");
                    cancelNotification();
                    mLowMemFlag = false;
                }
            }
        }
        if(localLOGV) Log.i(TAG, "Posting Message again");
        //keep posting messages to itself periodically
        postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
    
private final voidclearCache()

        if (mClearCacheObserver == null) {
            // Lazy instantiation
            mClearCacheObserver = new CachePackageDataObserver();
        }
        mClearingCache = true;
        try {
            if (localLOGV) Log.i(TAG, "Clearing cache");
            IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
                    freeStorageAndNotify(getMemThreshold(), mClearCacheObserver);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
            mClearingCache = false;
            mClearSucceeded = false;
        }
    
private longgetMemThreshold()

        int value = Settings.Gservices.getInt(
                              mContentResolver, 
                              Settings.Gservices.SYS_STORAGE_THRESHOLD_PERCENTAGE, 
                              DEFAULT_THRESHOLD_PERCENTAGE);
        if(localLOGV) Log.v(TAG, "Threshold Percentage="+value);
        //evaluate threshold value
        return mTotalMemory*value;
    
private voidpostCheckMemoryMsg(boolean clearCache, long delay)

        // Remove queued messages
        mHandler.removeMessages(DEVICE_MEMORY_WHAT);
        mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
                clearCache ?_TRUE : _FALSE, 0),
                delay);
    
private final voidrestatDataDir()

        mFileStats.restat(DATA_PATH);
        mFreeMem = mFileStats.getAvailableBlocks()*mBlkSize;
        // Allow freemem to be overridden by debug.freemem for testing
        String debugFreeMem = SystemProperties.get("debug.freemem");
        if (!"".equals(debugFreeMem)) {
            mFreeMem = Long.parseLong(debugFreeMem);
        }
        // Read the log interval from Gservices
        long freeMemLogInterval = Gservices.getLong(mContentResolver,
                Gservices.SYS_FREE_STORAGE_LOG_INTERVAL,
                DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
        //log the amount of free memory in event log
        long currTime = SystemClock.elapsedRealtime();
        if((mLastReportedFreeMemTime == 0) || 
                (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
            mLastReportedFreeMemTime = currTime;
            EventLog.writeEvent(EVENT_LOG_FREE_STORAGE_LEFT, mFreeMem);
        }
        // Read the reporting threshold from Gservices
        long threshold = Gservices.getLong(mContentResolver,
                Gservices.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
                DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
        // If mFree changed significantly log the new value
        long delta = mFreeMem - mLastReportedFreeMem;
        if (delta > threshold || delta < -threshold) {
            mLastReportedFreeMem = mFreeMem;
            EventLog.writeEvent(EVENT_LOG_STORAGE_BELOW_THRESHOLD, mFreeMem);
        }
    
private final voidsendNotification()
This method sends a notification to NotificationManager to display an error dialog indicating low disk space and launch the Installer application

        if(localLOGV) Log.i(TAG, "Sending low memory notification");
        //log the event to event log with the amount of free storage(in bytes) left on the device
        EventLog.writeEvent(EVENT_LOG_LOW_STORAGE_NOTIFICATION, mFreeMem);
        //  Pack up the values and broadcast them to everyone
        Intent lowMemIntent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
        lowMemIntent.putExtra("memory", mFreeMem);
        lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        NotificationManager mNotificationMgr = 
                (NotificationManager)mContext.getSystemService(
                        Context.NOTIFICATION_SERVICE);
        CharSequence title = mContext.getText(
                com.android.internal.R.string.low_internal_storage_view_title);
        CharSequence details = mContext.getText(
                com.android.internal.R.string.low_internal_storage_view_text);
        PendingIntent intent = PendingIntent.getActivity(mContext, 0,  lowMemIntent, 0);
        Notification notification = new Notification();
        notification.icon = com.android.internal.R.drawable.stat_notify_disk_full;
        notification.tickerText = title;
        notification.flags |= Notification.FLAG_NO_CLEAR;
        notification.setLatestEventInfo(mContext, title, details, intent);
        mNotificationMgr.notify(LOW_MEMORY_NOTIFICATION_ID, notification);
        mContext.sendStickyBroadcast(mStorageLowIntent);
    
public voidupdateMemory()

        int callingUid = getCallingUid();
        if(callingUid != Process.SYSTEM_UID) {
            return;
        }
        // force an early check
        postCheckMemoryMsg(true, 0);