DeviceStorageMonitorServicepublic 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 | SERVICEThis string is used for ServiceManager access to this class. | android.os.Handler | mHandlerHandler 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 void | cancelNotification()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 void | checkMemory(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 void | clearCache()
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 long | getMemThreshold()
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 void | postCheckMemoryMsg(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 void | restatDataDir()
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 void | sendNotification()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 void | updateMemory()
int callingUid = getCallingUid();
if(callingUid != Process.SYSTEM_UID) {
return;
}
// force an early check
postCheckMemoryMsg(true, 0);
|
|