blob: 62cf7076d1b9ed7b803bd20555de8938410cc375 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import com.android.server.am.ActivityManagerService;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.IPackageDataObserver;
27import android.content.pm.IPackageManager;
28import android.os.Binder;
29import android.os.Handler;
30import android.os.Message;
31import android.os.Process;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.os.StatFs;
35import android.os.SystemClock;
36import android.os.SystemProperties;
Doug Zongker43866e02010-01-07 12:09:54 -080037import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.util.Config;
39import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080040import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.provider.Settings;
42
43/**
Doug Zongker43866e02010-01-07 12:09:54 -080044 * This class implements a service to monitor the amount of disk
45 * storage space on the device. If the free storage on device is less
46 * than a tunable threshold value (a secure settings parameter;
47 * default 10%) a low memory notification is displayed to alert the
48 * user. If the user clicks on the low memory notification the
49 * Application Manager application gets launched to let the user free
50 * storage space.
51 *
52 * Event log events: A low memory event with the free storage on
53 * device in bytes is logged to the event log when the device goes low
54 * on storage space. The amount of free storage on the device is
55 * periodically logged to the event log. The log interval is a secure
56 * settings parameter with a default value of 12 hours. When the free
57 * storage differential goes below a threshold (again a secure
58 * settings parameter with a default value of 2MB), the free memory is
59 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 */
61class DeviceStorageMonitorService extends Binder {
62 private static final String TAG = "DeviceStorageMonitorService";
63 private static final boolean DEBUG = false;
64 private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
65 private static final int DEVICE_MEMORY_WHAT = 1;
66 private static final int MONITOR_INTERVAL = 1; //in minutes
67 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
68 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
69 private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
71 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Doug Zongker3161795b2009-10-07 15:14:03 -070072 private long mFreeMem; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 private long mLastReportedFreeMem;
74 private long mLastReportedFreeMemTime;
75 private boolean mLowMemFlag=false;
76 private Context mContext;
77 private ContentResolver mContentResolver;
Doug Zongker3161795b2009-10-07 15:14:03 -070078 private long mTotalMemory; // on /data
79 private StatFs mDataFileStats;
80 private StatFs mSystemFileStats;
81 private StatFs mCacheFileStats;
82 private static final String DATA_PATH = "/data";
83 private static final String SYSTEM_PATH = "/system";
84 private static final String CACHE_PATH = "/cache";
85 private long mThreadStartTime = -1;
86 private boolean mClearSucceeded = false;
87 private boolean mClearingCache;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 private Intent mStorageLowIntent;
89 private Intent mStorageOkIntent;
90 private CachePackageDataObserver mClearCacheObserver;
91 private static final int _TRUE = 1;
92 private static final int _FALSE = 0;
Doug Zongker3161795b2009-10-07 15:14:03 -070093
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 /**
95 * This string is used for ServiceManager access to this class.
96 */
97 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -070098
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700100 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 * notification if the device runs low on disk space
102 */
103 Handler mHandler = new Handler() {
104 @Override
105 public void handleMessage(Message msg) {
106 //dont handle an invalid message
107 if (msg.what != DEVICE_MEMORY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800108 Slog.e(TAG, "Will not process invalid message");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 return;
110 }
111 checkMemory(msg.arg1 == _TRUE);
112 }
113 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 class CachePackageDataObserver extends IPackageDataObserver.Stub {
116 public void onRemoveCompleted(String packageName, boolean succeeded) {
117 mClearSucceeded = succeeded;
118 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800119 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 +", mClearingCache:"+mClearingCache+" Forcing memory check");
121 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700122 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 private final void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700126 try {
127 mDataFileStats.restat(DATA_PATH);
128 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
129 mDataFileStats.getBlockSize();
130 } catch (IllegalArgumentException e) {
131 // use the old value of mFreeMem
132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 // Allow freemem to be overridden by debug.freemem for testing
134 String debugFreeMem = SystemProperties.get("debug.freemem");
135 if (!"".equals(debugFreeMem)) {
136 mFreeMem = Long.parseLong(debugFreeMem);
137 }
Doug Zongker43866e02010-01-07 12:09:54 -0800138 // Read the log interval from secure settings
139 long freeMemLogInterval = Settings.Secure.getLong(mContentResolver,
140 Settings.Secure.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
142 //log the amount of free memory in event log
143 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700144 if((mLastReportedFreeMemTime == 0) ||
145 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700147 long mFreeSystem = -1, mFreeCache = -1;
148 try {
149 mSystemFileStats.restat(SYSTEM_PATH);
150 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
151 mSystemFileStats.getBlockSize();
152 } catch (IllegalArgumentException e) {
153 // ignore; report -1
154 }
155 try {
156 mCacheFileStats.restat(CACHE_PATH);
157 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
158 mCacheFileStats.getBlockSize();
159 } catch (IllegalArgumentException e) {
160 // ignore; report -1
161 }
162 mCacheFileStats.restat(CACHE_PATH);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800163 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700164 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 }
Doug Zongker43866e02010-01-07 12:09:54 -0800166 // Read the reporting threshold from secure settings
167 long threshold = Settings.Secure.getLong(mContentResolver,
168 Settings.Secure.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
170 // If mFree changed significantly log the new value
171 long delta = mFreeMem - mLastReportedFreeMem;
172 if (delta > threshold || delta < -threshold) {
173 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800174 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 }
176 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 private final void clearCache() {
179 if (mClearCacheObserver == null) {
180 // Lazy instantiation
181 mClearCacheObserver = new CachePackageDataObserver();
182 }
183 mClearingCache = true;
184 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800185 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
187 freeStorageAndNotify(getMemThreshold(), mClearCacheObserver);
188 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800189 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 mClearingCache = false;
191 mClearSucceeded = false;
192 }
193 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 private final void checkMemory(boolean checkCache) {
Doug Zongker3161795b2009-10-07 15:14:03 -0700196 //if the thread that was started to clear cache is still running do nothing till its
197 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 // and should be accessed via a lock but even if it does this test will fail now and
199 //hopefully the next time this flag will be set to the correct value.
200 if(mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800201 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 //make sure the thread is not hung for too long
203 long diffTime = System.currentTimeMillis() - mThreadStartTime;
204 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800205 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700206 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 } else {
208 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800209 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 //post intent to NotificationManager to display icon if necessary
212 long memThreshold = getMemThreshold();
213 if (mFreeMem < memThreshold) {
214 if (!mLowMemFlag) {
215 if (checkCache) {
216 // See if clearing cache helps
217 // Note that clearing cache is asynchronous and so we do a
218 // memory check again once the cache has been cleared.
219 mThreadStartTime = System.currentTimeMillis();
220 mClearSucceeded = false;
221 clearCache();
222 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800223 Slog.i(TAG, "Running low on memory. Sending notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 sendNotification();
225 mLowMemFlag = true;
226 }
227 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800228 if (localLOGV) Slog.v(TAG, "Running low on memory " +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 "notification already sent. do nothing");
230 }
231 } else {
232 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800233 Slog.i(TAG, "Memory available. Cancelling notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 cancelNotification();
235 mLowMemFlag = false;
236 }
237 }
238 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800239 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 //keep posting messages to itself periodically
241 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
242 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 private void postCheckMemoryMsg(boolean clearCache, long delay) {
245 // Remove queued messages
246 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
247 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
248 clearCache ?_TRUE : _FALSE, 0),
249 delay);
250 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700251
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 /*
Doug Zongker3161795b2009-10-07 15:14:03 -0700253 * just query settings to retrieve the memory threshold.
Doug Zongker43866e02010-01-07 12:09:54 -0800254 * Preferred this over using a ContentObserver since Settings.Secure caches the value
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 * any way
256 */
257 private long getMemThreshold() {
Doug Zongker43866e02010-01-07 12:09:54 -0800258 int value = Settings.Secure.getInt(
Doug Zongker3161795b2009-10-07 15:14:03 -0700259 mContentResolver,
Doug Zongker43866e02010-01-07 12:09:54 -0800260 Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 DEFAULT_THRESHOLD_PERCENTAGE);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800262 if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 //evaluate threshold value
264 return mTotalMemory*value;
265 }
266
267 /**
268 * Constructor to run service. initializes the disk space threshold value
269 * and posts an empty message to kickstart the process.
270 */
271 public DeviceStorageMonitorService(Context context) {
272 mLastReportedFreeMemTime = 0;
273 mContext = context;
274 mContentResolver = mContext.getContentResolver();
275 //create StatFs object
Doug Zongker3161795b2009-10-07 15:14:03 -0700276 mDataFileStats = new StatFs(DATA_PATH);
277 mSystemFileStats = new StatFs(SYSTEM_PATH);
278 mCacheFileStats = new StatFs(CACHE_PATH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 //initialize total storage on device
Doug Zongker3161795b2009-10-07 15:14:03 -0700280 mTotalMemory = ((long)mDataFileStats.getBlockCount() *
281 mDataFileStats.getBlockSize())/100L;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
283 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
284 checkMemory(true);
285 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700286
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287
288 /**
289 * This method sends a notification to NotificationManager to display
290 * an error dialog indicating low disk space and launch the Installer
291 * application
292 */
293 private final void sendNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800294 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 //log the event to event log with the amount of free storage(in bytes) left on the device
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800296 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 // Pack up the values and broadcast them to everyone
298 Intent lowMemIntent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
299 lowMemIntent.putExtra("memory", mFreeMem);
300 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Doug Zongker3161795b2009-10-07 15:14:03 -0700301 NotificationManager mNotificationMgr =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 (NotificationManager)mContext.getSystemService(
303 Context.NOTIFICATION_SERVICE);
304 CharSequence title = mContext.getText(
305 com.android.internal.R.string.low_internal_storage_view_title);
306 CharSequence details = mContext.getText(
307 com.android.internal.R.string.low_internal_storage_view_text);
308 PendingIntent intent = PendingIntent.getActivity(mContext, 0, lowMemIntent, 0);
309 Notification notification = new Notification();
310 notification.icon = com.android.internal.R.drawable.stat_notify_disk_full;
311 notification.tickerText = title;
312 notification.flags |= Notification.FLAG_NO_CLEAR;
313 notification.setLatestEventInfo(mContext, title, details, intent);
314 mNotificationMgr.notify(LOW_MEMORY_NOTIFICATION_ID, notification);
315 mContext.sendStickyBroadcast(mStorageLowIntent);
316 }
317
318 /**
319 * Cancels low storage notification and sends OK intent.
320 */
321 private final void cancelNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800322 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 NotificationManager mNotificationMgr =
324 (NotificationManager)mContext.getSystemService(
325 Context.NOTIFICATION_SERVICE);
326 //cancel notification since memory has been freed
327 mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID);
328
329 mContext.removeStickyBroadcast(mStorageLowIntent);
330 mContext.sendBroadcast(mStorageOkIntent);
331 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700332
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 public void updateMemory() {
334 int callingUid = getCallingUid();
335 if(callingUid != Process.SYSTEM_UID) {
336 return;
337 }
338 // force an early check
339 postCheckMemoryMsg(true, 0);
340 }
341}