blob: c919595ef8a143d7feae3e5a21faab0231630256 [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
Dianne Hackborn197a0c82012-07-12 14:46:04 -070019import java.io.FileDescriptor;
20import java.io.PrintWriter;
21
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.pm.IPackageDataObserver;
29import android.content.pm.IPackageManager;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070030import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.Binder;
Dianne Hackbornf882efa2012-04-11 16:04:12 -070032import android.os.Environment;
Jeff Sharkey4b496572012-04-19 14:17:03 -070033import android.os.FileObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.Handler;
35import android.os.Message;
36import android.os.Process;
37import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.StatFs;
40import android.os.SystemClock;
41import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070042import android.os.UserHandle;
Doug Zongker43866e02010-01-07 12:09:54 -080043import android.provider.Settings;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070044import android.text.format.Formatter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080046import android.util.Slog;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070047import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49/**
Doug Zongker43866e02010-01-07 12:09:54 -080050 * This class implements a service to monitor the amount of disk
51 * storage space on the device. If the free storage on device is less
52 * than a tunable threshold value (a secure settings parameter;
53 * default 10%) a low memory notification is displayed to alert the
54 * user. If the user clicks on the low memory notification the
55 * Application Manager application gets launched to let the user free
56 * storage space.
57 *
58 * Event log events: A low memory event with the free storage on
59 * device in bytes is logged to the event log when the device goes low
60 * on storage space. The amount of free storage on the device is
61 * periodically logged to the event log. The log interval is a secure
62 * settings parameter with a default value of 12 hours. When the free
63 * storage differential goes below a threshold (again a secure
64 * settings parameter with a default value of 2MB), the free memory is
65 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 */
Kenny Rootcf0b38c2011-03-22 14:17:59 -070067public class DeviceStorageMonitorService extends Binder {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 private static final String TAG = "DeviceStorageMonitorService";
69 private static final boolean DEBUG = false;
Joe Onorato43a17652011-04-06 19:22:23 -070070 private static final boolean localLOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 private static final int DEVICE_MEMORY_WHAT = 1;
72 private static final int MONITOR_INTERVAL = 1; //in minutes
73 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
74 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
Dianne Hackborn247fe742011-01-08 17:25:57 -080075 private static final int DEFAULT_THRESHOLD_MAX_BYTES = 500*1024*1024; // 500MB
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 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 -080077 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
78 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jake Hambybb371632010-08-23 18:16:48 -070079 private static final int DEFAULT_FULL_THRESHOLD_BYTES = 1024*1024; // 1MB
Doug Zongker3161795b2009-10-07 15:14:03 -070080 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -070081 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 private long mLastReportedFreeMem;
83 private long mLastReportedFreeMemTime;
84 private boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -070085 private boolean mMemFullFlag=false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 private Context mContext;
87 private ContentResolver mContentResolver;
Doug Zongker3161795b2009-10-07 15:14:03 -070088 private long mTotalMemory; // on /data
89 private StatFs mDataFileStats;
90 private StatFs mSystemFileStats;
91 private StatFs mCacheFileStats;
92 private static final String DATA_PATH = "/data";
93 private static final String SYSTEM_PATH = "/system";
94 private static final String CACHE_PATH = "/cache";
95 private long mThreadStartTime = -1;
96 private boolean mClearSucceeded = false;
97 private boolean mClearingCache;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 private Intent mStorageLowIntent;
99 private Intent mStorageOkIntent;
Jake Hambybb371632010-08-23 18:16:48 -0700100 private Intent mStorageFullIntent;
101 private Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 private CachePackageDataObserver mClearCacheObserver;
Jeff Sharkey4b496572012-04-19 14:17:03 -0700103 private final CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private static final int _TRUE = 1;
105 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700106 // This is the raw threshold that has been set at which we consider
107 // storage to be low.
Jake Hambybb371632010-08-23 18:16:48 -0700108 private long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700109 // This is the threshold at which we start trying to flush caches
110 // to get below the low threshold limit. It is less than the low
111 // threshold; we will allow storage to get a bit beyond the limit
112 // before flushing and checking if we are actually low.
113 private long mMemCacheStartTrimThreshold;
114 // This is the threshold that we try to get to when deleting cache
115 // files. This is greater than the low threshold so that we will flush
116 // more files than absolutely needed, to reduce the frequency that
117 // flushing takes place.
118 private long mMemCacheTrimToThreshold;
Jake Hambybb371632010-08-23 18:16:48 -0700119 private int mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 /**
122 * This string is used for ServiceManager access to this class.
123 */
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700124 public static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700127 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 * notification if the device runs low on disk space
129 */
130 Handler mHandler = new Handler() {
131 @Override
132 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700133 //don't handle an invalid message
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 if (msg.what != DEVICE_MEMORY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800135 Slog.e(TAG, "Will not process invalid message");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 return;
137 }
138 checkMemory(msg.arg1 == _TRUE);
139 }
140 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700141
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 class CachePackageDataObserver extends IPackageDataObserver.Stub {
143 public void onRemoveCompleted(String packageName, boolean succeeded) {
144 mClearSucceeded = succeeded;
145 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800146 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 +", mClearingCache:"+mClearingCache+" Forcing memory check");
148 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700149 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 private final void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700153 try {
154 mDataFileStats.restat(DATA_PATH);
155 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
156 mDataFileStats.getBlockSize();
157 } catch (IllegalArgumentException e) {
158 // use the old value of mFreeMem
159 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 // Allow freemem to be overridden by debug.freemem for testing
161 String debugFreeMem = SystemProperties.get("debug.freemem");
162 if (!"".equals(debugFreeMem)) {
163 mFreeMem = Long.parseLong(debugFreeMem);
164 }
Doug Zongker43866e02010-01-07 12:09:54 -0800165 // Read the log interval from secure settings
166 long freeMemLogInterval = Settings.Secure.getLong(mContentResolver,
167 Settings.Secure.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
169 //log the amount of free memory in event log
170 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700171 if((mLastReportedFreeMemTime == 0) ||
172 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700174 long mFreeSystem = -1, mFreeCache = -1;
175 try {
176 mSystemFileStats.restat(SYSTEM_PATH);
177 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
178 mSystemFileStats.getBlockSize();
179 } catch (IllegalArgumentException e) {
180 // ignore; report -1
181 }
182 try {
183 mCacheFileStats.restat(CACHE_PATH);
184 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
185 mCacheFileStats.getBlockSize();
186 } catch (IllegalArgumentException e) {
187 // ignore; report -1
188 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800189 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700190 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
Doug Zongker43866e02010-01-07 12:09:54 -0800192 // Read the reporting threshold from secure settings
193 long threshold = Settings.Secure.getLong(mContentResolver,
194 Settings.Secure.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
196 // If mFree changed significantly log the new value
197 long delta = mFreeMem - mLastReportedFreeMem;
198 if (delta > threshold || delta < -threshold) {
199 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800200 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 }
202 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 private final void clearCache() {
205 if (mClearCacheObserver == null) {
206 // Lazy instantiation
207 mClearCacheObserver = new CachePackageDataObserver();
208 }
209 mClearingCache = true;
210 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800211 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700213 freeStorageAndNotify(mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800215 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 mClearingCache = false;
217 mClearSucceeded = false;
218 }
219 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700220
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 private final void checkMemory(boolean checkCache) {
Doug Zongker3161795b2009-10-07 15:14:03 -0700222 //if the thread that was started to clear cache is still running do nothing till its
223 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 // and should be accessed via a lock but even if it does this test will fail now and
225 //hopefully the next time this flag will be set to the correct value.
226 if(mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800227 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 //make sure the thread is not hung for too long
229 long diffTime = System.currentTimeMillis() - mThreadStartTime;
230 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800231 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 } else {
234 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800235 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700236
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700238 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700239 if (checkCache) {
240 // We are allowed to clear cache files at this point to
241 // try to get down below the limit, because this is not
242 // the initial call after a cache clear has been attempted.
243 // In this case we will try a cache clear if our free
244 // space has gone below the cache clear limit.
245 if (mFreeMem < mMemCacheStartTrimThreshold) {
246 // We only clear the cache if the free storage has changed
247 // a significant amount since the last time.
248 if ((mFreeMemAfterLastCacheClear-mFreeMem)
249 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
250 // See if clearing cache helps
251 // Note that clearing cache is asynchronous and so we do a
252 // memory check again once the cache has been cleared.
253 mThreadStartTime = System.currentTimeMillis();
254 mClearSucceeded = false;
255 clearCache();
256 }
257 }
258 } else {
259 // This is a call from after clearing the cache. Note
260 // the amount of free storage at this point.
261 mFreeMemAfterLastCacheClear = mFreeMem;
262 if (!mLowMemFlag) {
263 // We tried to clear the cache, but that didn't get us
264 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800265 Slog.i(TAG, "Running low on memory. Sending notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 sendNotification();
267 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700268 } else {
269 if (localLOGV) Slog.v(TAG, "Running low on memory " +
270 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 }
273 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700274 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800276 Slog.i(TAG, "Memory available. Cancelling notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 cancelNotification();
278 mLowMemFlag = false;
279 }
280 }
Jake Hambybb371632010-08-23 18:16:48 -0700281 if (mFreeMem < mMemFullThreshold) {
282 if (!mMemFullFlag) {
283 sendFullNotification();
284 mMemFullFlag = true;
285 }
286 } else {
287 if (mMemFullFlag) {
288 cancelFullNotification();
289 mMemFullFlag = false;
290 }
291 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800293 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 //keep posting messages to itself periodically
295 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
296 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 private void postCheckMemoryMsg(boolean clearCache, long delay) {
299 // Remove queued messages
300 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
301 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
302 clearCache ?_TRUE : _FALSE, 0),
303 delay);
304 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700305
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 /*
Doug Zongker3161795b2009-10-07 15:14:03 -0700307 * just query settings to retrieve the memory threshold.
Doug Zongker43866e02010-01-07 12:09:54 -0800308 * Preferred this over using a ContentObserver since Settings.Secure caches the value
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 * any way
310 */
311 private long getMemThreshold() {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800312 long value = Settings.Secure.getInt(
Doug Zongker3161795b2009-10-07 15:14:03 -0700313 mContentResolver,
Doug Zongker43866e02010-01-07 12:09:54 -0800314 Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 DEFAULT_THRESHOLD_PERCENTAGE);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800316 if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value);
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700317 value = (value*mTotalMemory)/100;
Dianne Hackborn247fe742011-01-08 17:25:57 -0800318 long maxValue = Settings.Secure.getInt(
319 mContentResolver,
320 Settings.Secure.SYS_STORAGE_THRESHOLD_MAX_BYTES,
321 DEFAULT_THRESHOLD_MAX_BYTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 //evaluate threshold value
Dianne Hackborn247fe742011-01-08 17:25:57 -0800323 return value < maxValue ? value : maxValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 }
325
Jake Hambybb371632010-08-23 18:16:48 -0700326 /*
327 * just query settings to retrieve the memory full threshold.
328 * Preferred this over using a ContentObserver since Settings.Secure caches the value
329 * any way
330 */
331 private int getMemFullThreshold() {
332 int value = Settings.Secure.getInt(
333 mContentResolver,
334 Settings.Secure.SYS_STORAGE_FULL_THRESHOLD_BYTES,
335 DEFAULT_FULL_THRESHOLD_BYTES);
336 if(localLOGV) Slog.v(TAG, "Full Threshold Bytes="+value);
337 return value;
338 }
339
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 /**
341 * Constructor to run service. initializes the disk space threshold value
342 * and posts an empty message to kickstart the process.
343 */
344 public DeviceStorageMonitorService(Context context) {
345 mLastReportedFreeMemTime = 0;
346 mContext = context;
347 mContentResolver = mContext.getContentResolver();
348 //create StatFs object
Doug Zongker3161795b2009-10-07 15:14:03 -0700349 mDataFileStats = new StatFs(DATA_PATH);
350 mSystemFileStats = new StatFs(SYSTEM_PATH);
351 mCacheFileStats = new StatFs(CACHE_PATH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700353 mTotalMemory = (long)mDataFileStats.getBlockCount() *
354 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500356 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500358 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jake Hambybb371632010-08-23 18:16:48 -0700359 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
360 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
361 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
362 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
363 // cache storage thresholds
364 mMemLowThreshold = getMemThreshold();
365 mMemFullThreshold = getMemFullThreshold();
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700366 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
367 mMemCacheTrimToThreshold = mMemLowThreshold
368 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
369 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700371
372 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
373 mCacheFileDeletedObserver.startWatching();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700375
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376
377 /**
378 * This method sends a notification to NotificationManager to display
379 * an error dialog indicating low disk space and launch the Installer
380 * application
381 */
382 private final void sendNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800383 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 //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 -0800385 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 // Pack up the values and broadcast them to everyone
Dianne Hackbornf882efa2012-04-11 16:04:12 -0700387 Intent lowMemIntent = new Intent(Environment.isExternalStorageEmulated()
388 ? Settings.ACTION_INTERNAL_STORAGE_SETTINGS
389 : Intent.ACTION_MANAGE_PACKAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 lowMemIntent.putExtra("memory", mFreeMem);
391 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Doug Zongker3161795b2009-10-07 15:14:03 -0700392 NotificationManager mNotificationMgr =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 (NotificationManager)mContext.getSystemService(
394 Context.NOTIFICATION_SERVICE);
395 CharSequence title = mContext.getText(
396 com.android.internal.R.string.low_internal_storage_view_title);
397 CharSequence details = mContext.getText(
398 com.android.internal.R.string.low_internal_storage_view_text);
399 PendingIntent intent = PendingIntent.getActivity(mContext, 0, lowMemIntent, 0);
400 Notification notification = new Notification();
401 notification.icon = com.android.internal.R.drawable.stat_notify_disk_full;
402 notification.tickerText = title;
403 notification.flags |= Notification.FLAG_NO_CLEAR;
404 notification.setLatestEventInfo(mContext, title, details, intent);
405 mNotificationMgr.notify(LOW_MEMORY_NOTIFICATION_ID, notification);
406 mContext.sendStickyBroadcast(mStorageLowIntent);
407 }
408
409 /**
410 * Cancels low storage notification and sends OK intent.
411 */
412 private final void cancelNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800413 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 NotificationManager mNotificationMgr =
415 (NotificationManager)mContext.getSystemService(
416 Context.NOTIFICATION_SERVICE);
417 //cancel notification since memory has been freed
418 mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID);
419
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700420 mContext.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
421 mContext.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700423
Jake Hambybb371632010-08-23 18:16:48 -0700424 /**
425 * Send a notification when storage is full.
426 */
427 private final void sendFullNotification() {
428 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
429 mContext.sendStickyBroadcast(mStorageFullIntent);
430 }
431
432 /**
433 * Cancels memory full notification and sends "not full" intent.
434 */
435 private final void cancelFullNotification() {
436 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700437 mContext.removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
438 mContext.sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700439 }
440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 public void updateMemory() {
442 int callingUid = getCallingUid();
443 if(callingUid != Process.SYSTEM_UID) {
444 return;
445 }
446 // force an early check
447 postCheckMemoryMsg(true, 0);
448 }
Kenny Root62e1b4e2011-03-14 17:13:39 -0700449
450 /**
451 * Callable from other things in the system service to obtain the low memory
452 * threshold.
453 *
454 * @return low memory threshold in bytes
455 */
456 public long getMemoryLowThreshold() {
457 return mMemLowThreshold;
458 }
459
460 /**
461 * Callable from other things in the system process to check whether memory
462 * is low.
463 *
464 * @return true is memory is low
465 */
466 public boolean isMemoryLow() {
467 return mLowMemFlag;
468 }
Jeff Sharkey4b496572012-04-19 14:17:03 -0700469
470 public static class CacheFileDeletedObserver extends FileObserver {
471 public CacheFileDeletedObserver() {
472 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
473 }
474
475 @Override
476 public void onEvent(int event, String path) {
477 EventLogTags.writeCacheFileDeleted(path);
478 }
479 }
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700480
481 @Override
482 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
483 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
484 != PackageManager.PERMISSION_GRANTED) {
485
486 pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
487 + Binder.getCallingPid()
488 + ", uid=" + Binder.getCallingUid());
489 return;
490 }
491
492 pw.println("Current DeviceStorageMonitor state:");
493 pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(mContext, mFreeMem));
494 pw.print(" mTotalMemory=");
495 pw.println(Formatter.formatFileSize(mContext, mTotalMemory));
496 pw.print(" mFreeMemAfterLastCacheClear=");
497 pw.println(Formatter.formatFileSize(mContext, mFreeMemAfterLastCacheClear));
498 pw.print(" mLastReportedFreeMem=");
499 pw.print(Formatter.formatFileSize(mContext, mLastReportedFreeMem));
500 pw.print(" mLastReportedFreeMemTime=");
501 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
502 pw.println();
503 pw.print(" mLowMemFlag="); pw.print(mLowMemFlag);
504 pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
505 pw.print(" mClearSucceeded="); pw.print(mClearSucceeded);
506 pw.print(" mClearingCache="); pw.println(mClearingCache);
507 pw.print(" mMemLowThreshold=");
508 pw.print(Formatter.formatFileSize(mContext, mMemLowThreshold));
509 pw.print(" mMemFullThreshold=");
510 pw.println(Formatter.formatFileSize(mContext, mMemFullThreshold));
511 pw.print(" mMemCacheStartTrimThreshold=");
512 pw.print(Formatter.formatFileSize(mContext, mMemCacheStartTrimThreshold));
513 pw.print(" mMemCacheTrimToThreshold=");
514 pw.println(Formatter.formatFileSize(mContext, mMemCacheTrimToThreshold));
515 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516}