blob: 923167441ecb789d2f28af6a57fdc18dad73244d [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;
Doug Zongker43866e02010-01-07 12:09:54 -080042import android.provider.Settings;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070043import android.text.format.Formatter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080045import android.util.Slog;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070046import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
Doug Zongker43866e02010-01-07 12:09:54 -080049 * This class implements a service to monitor the amount of disk
50 * storage space on the device. If the free storage on device is less
51 * than a tunable threshold value (a secure settings parameter;
52 * default 10%) a low memory notification is displayed to alert the
53 * user. If the user clicks on the low memory notification the
54 * Application Manager application gets launched to let the user free
55 * storage space.
56 *
57 * Event log events: A low memory event with the free storage on
58 * device in bytes is logged to the event log when the device goes low
59 * on storage space. The amount of free storage on the device is
60 * periodically logged to the event log. The log interval is a secure
61 * settings parameter with a default value of 12 hours. When the free
62 * storage differential goes below a threshold (again a secure
63 * settings parameter with a default value of 2MB), the free memory is
64 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 */
Kenny Rootcf0b38c2011-03-22 14:17:59 -070066public class DeviceStorageMonitorService extends Binder {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 private static final String TAG = "DeviceStorageMonitorService";
68 private static final boolean DEBUG = false;
Joe Onorato43a17652011-04-06 19:22:23 -070069 private static final boolean localLOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final int DEVICE_MEMORY_WHAT = 1;
71 private static final int MONITOR_INTERVAL = 1; //in minutes
72 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
73 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
Dianne Hackborn247fe742011-01-08 17:25:57 -080074 private static final int DEFAULT_THRESHOLD_MAX_BYTES = 500*1024*1024; // 500MB
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 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 -080076 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
77 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jake Hambybb371632010-08-23 18:16:48 -070078 private static final int DEFAULT_FULL_THRESHOLD_BYTES = 1024*1024; // 1MB
Doug Zongker3161795b2009-10-07 15:14:03 -070079 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -070080 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 private long mLastReportedFreeMem;
82 private long mLastReportedFreeMemTime;
83 private boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -070084 private boolean mMemFullFlag=false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private Context mContext;
86 private ContentResolver mContentResolver;
Doug Zongker3161795b2009-10-07 15:14:03 -070087 private long mTotalMemory; // on /data
88 private StatFs mDataFileStats;
89 private StatFs mSystemFileStats;
90 private StatFs mCacheFileStats;
91 private static final String DATA_PATH = "/data";
92 private static final String SYSTEM_PATH = "/system";
93 private static final String CACHE_PATH = "/cache";
94 private long mThreadStartTime = -1;
95 private boolean mClearSucceeded = false;
96 private boolean mClearingCache;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 private Intent mStorageLowIntent;
98 private Intent mStorageOkIntent;
Jake Hambybb371632010-08-23 18:16:48 -070099 private Intent mStorageFullIntent;
100 private Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private CachePackageDataObserver mClearCacheObserver;
Jeff Sharkey4b496572012-04-19 14:17:03 -0700102 private final CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private static final int _TRUE = 1;
104 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700105 // This is the raw threshold that has been set at which we consider
106 // storage to be low.
Jake Hambybb371632010-08-23 18:16:48 -0700107 private long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700108 // This is the threshold at which we start trying to flush caches
109 // to get below the low threshold limit. It is less than the low
110 // threshold; we will allow storage to get a bit beyond the limit
111 // before flushing and checking if we are actually low.
112 private long mMemCacheStartTrimThreshold;
113 // This is the threshold that we try to get to when deleting cache
114 // files. This is greater than the low threshold so that we will flush
115 // more files than absolutely needed, to reduce the frequency that
116 // flushing takes place.
117 private long mMemCacheTrimToThreshold;
Jake Hambybb371632010-08-23 18:16:48 -0700118 private int mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 /**
121 * This string is used for ServiceManager access to this class.
122 */
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700123 public static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700126 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 * notification if the device runs low on disk space
128 */
129 Handler mHandler = new Handler() {
130 @Override
131 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700132 //don't handle an invalid message
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 if (msg.what != DEVICE_MEMORY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800134 Slog.e(TAG, "Will not process invalid message");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 return;
136 }
137 checkMemory(msg.arg1 == _TRUE);
138 }
139 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 class CachePackageDataObserver extends IPackageDataObserver.Stub {
142 public void onRemoveCompleted(String packageName, boolean succeeded) {
143 mClearSucceeded = succeeded;
144 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800145 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 +", mClearingCache:"+mClearingCache+" Forcing memory check");
147 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700148 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 private final void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700152 try {
153 mDataFileStats.restat(DATA_PATH);
154 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
155 mDataFileStats.getBlockSize();
156 } catch (IllegalArgumentException e) {
157 // use the old value of mFreeMem
158 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 // Allow freemem to be overridden by debug.freemem for testing
160 String debugFreeMem = SystemProperties.get("debug.freemem");
161 if (!"".equals(debugFreeMem)) {
162 mFreeMem = Long.parseLong(debugFreeMem);
163 }
Doug Zongker43866e02010-01-07 12:09:54 -0800164 // Read the log interval from secure settings
165 long freeMemLogInterval = Settings.Secure.getLong(mContentResolver,
166 Settings.Secure.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
168 //log the amount of free memory in event log
169 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700170 if((mLastReportedFreeMemTime == 0) ||
171 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700173 long mFreeSystem = -1, mFreeCache = -1;
174 try {
175 mSystemFileStats.restat(SYSTEM_PATH);
176 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
177 mSystemFileStats.getBlockSize();
178 } catch (IllegalArgumentException e) {
179 // ignore; report -1
180 }
181 try {
182 mCacheFileStats.restat(CACHE_PATH);
183 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
184 mCacheFileStats.getBlockSize();
185 } catch (IllegalArgumentException e) {
186 // ignore; report -1
187 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800188 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700189 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
Doug Zongker43866e02010-01-07 12:09:54 -0800191 // Read the reporting threshold from secure settings
192 long threshold = Settings.Secure.getLong(mContentResolver,
193 Settings.Secure.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
195 // If mFree changed significantly log the new value
196 long delta = mFreeMem - mLastReportedFreeMem;
197 if (delta > threshold || delta < -threshold) {
198 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800199 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 }
201 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 private final void clearCache() {
204 if (mClearCacheObserver == null) {
205 // Lazy instantiation
206 mClearCacheObserver = new CachePackageDataObserver();
207 }
208 mClearingCache = true;
209 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800210 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700212 freeStorageAndNotify(mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800214 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 mClearingCache = false;
216 mClearSucceeded = false;
217 }
218 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 private final void checkMemory(boolean checkCache) {
Doug Zongker3161795b2009-10-07 15:14:03 -0700221 //if the thread that was started to clear cache is still running do nothing till its
222 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 // and should be accessed via a lock but even if it does this test will fail now and
224 //hopefully the next time this flag will be set to the correct value.
225 if(mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800226 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 //make sure the thread is not hung for too long
228 long diffTime = System.currentTimeMillis() - mThreadStartTime;
229 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800230 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700231 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 } else {
233 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800234 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700237 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700238 if (checkCache) {
239 // We are allowed to clear cache files at this point to
240 // try to get down below the limit, because this is not
241 // the initial call after a cache clear has been attempted.
242 // In this case we will try a cache clear if our free
243 // space has gone below the cache clear limit.
244 if (mFreeMem < mMemCacheStartTrimThreshold) {
245 // We only clear the cache if the free storage has changed
246 // a significant amount since the last time.
247 if ((mFreeMemAfterLastCacheClear-mFreeMem)
248 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
249 // See if clearing cache helps
250 // Note that clearing cache is asynchronous and so we do a
251 // memory check again once the cache has been cleared.
252 mThreadStartTime = System.currentTimeMillis();
253 mClearSucceeded = false;
254 clearCache();
255 }
256 }
257 } else {
258 // This is a call from after clearing the cache. Note
259 // the amount of free storage at this point.
260 mFreeMemAfterLastCacheClear = mFreeMem;
261 if (!mLowMemFlag) {
262 // We tried to clear the cache, but that didn't get us
263 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800264 Slog.i(TAG, "Running low on memory. Sending notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 sendNotification();
266 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700267 } else {
268 if (localLOGV) Slog.v(TAG, "Running low on memory " +
269 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 }
272 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700273 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800275 Slog.i(TAG, "Memory available. Cancelling notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 cancelNotification();
277 mLowMemFlag = false;
278 }
279 }
Jake Hambybb371632010-08-23 18:16:48 -0700280 if (mFreeMem < mMemFullThreshold) {
281 if (!mMemFullFlag) {
282 sendFullNotification();
283 mMemFullFlag = true;
284 }
285 } else {
286 if (mMemFullFlag) {
287 cancelFullNotification();
288 mMemFullFlag = false;
289 }
290 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800292 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 //keep posting messages to itself periodically
294 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
295 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700296
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 private void postCheckMemoryMsg(boolean clearCache, long delay) {
298 // Remove queued messages
299 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
300 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
301 clearCache ?_TRUE : _FALSE, 0),
302 delay);
303 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 /*
Doug Zongker3161795b2009-10-07 15:14:03 -0700306 * just query settings to retrieve the memory threshold.
Doug Zongker43866e02010-01-07 12:09:54 -0800307 * Preferred this over using a ContentObserver since Settings.Secure caches the value
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 * any way
309 */
310 private long getMemThreshold() {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800311 long value = Settings.Secure.getInt(
Doug Zongker3161795b2009-10-07 15:14:03 -0700312 mContentResolver,
Doug Zongker43866e02010-01-07 12:09:54 -0800313 Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 DEFAULT_THRESHOLD_PERCENTAGE);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800315 if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value);
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700316 value = (value*mTotalMemory)/100;
Dianne Hackborn247fe742011-01-08 17:25:57 -0800317 long maxValue = Settings.Secure.getInt(
318 mContentResolver,
319 Settings.Secure.SYS_STORAGE_THRESHOLD_MAX_BYTES,
320 DEFAULT_THRESHOLD_MAX_BYTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 //evaluate threshold value
Dianne Hackborn247fe742011-01-08 17:25:57 -0800322 return value < maxValue ? value : maxValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 }
324
Jake Hambybb371632010-08-23 18:16:48 -0700325 /*
326 * just query settings to retrieve the memory full threshold.
327 * Preferred this over using a ContentObserver since Settings.Secure caches the value
328 * any way
329 */
330 private int getMemFullThreshold() {
331 int value = Settings.Secure.getInt(
332 mContentResolver,
333 Settings.Secure.SYS_STORAGE_FULL_THRESHOLD_BYTES,
334 DEFAULT_FULL_THRESHOLD_BYTES);
335 if(localLOGV) Slog.v(TAG, "Full Threshold Bytes="+value);
336 return value;
337 }
338
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 /**
340 * Constructor to run service. initializes the disk space threshold value
341 * and posts an empty message to kickstart the process.
342 */
343 public DeviceStorageMonitorService(Context context) {
344 mLastReportedFreeMemTime = 0;
345 mContext = context;
346 mContentResolver = mContext.getContentResolver();
347 //create StatFs object
Doug Zongker3161795b2009-10-07 15:14:03 -0700348 mDataFileStats = new StatFs(DATA_PATH);
349 mSystemFileStats = new StatFs(SYSTEM_PATH);
350 mCacheFileStats = new StatFs(CACHE_PATH);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700352 mTotalMemory = (long)mDataFileStats.getBlockCount() *
353 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500355 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500357 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jake Hambybb371632010-08-23 18:16:48 -0700358 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
359 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
360 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
361 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
362 // cache storage thresholds
363 mMemLowThreshold = getMemThreshold();
364 mMemFullThreshold = getMemFullThreshold();
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700365 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
366 mMemCacheTrimToThreshold = mMemLowThreshold
367 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
368 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700370
371 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
372 mCacheFileDeletedObserver.startWatching();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375
376 /**
377 * This method sends a notification to NotificationManager to display
378 * an error dialog indicating low disk space and launch the Installer
379 * application
380 */
381 private final void sendNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800382 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 //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 -0800384 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 // Pack up the values and broadcast them to everyone
Dianne Hackbornf882efa2012-04-11 16:04:12 -0700386 Intent lowMemIntent = new Intent(Environment.isExternalStorageEmulated()
387 ? Settings.ACTION_INTERNAL_STORAGE_SETTINGS
388 : Intent.ACTION_MANAGE_PACKAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 lowMemIntent.putExtra("memory", mFreeMem);
390 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Doug Zongker3161795b2009-10-07 15:14:03 -0700391 NotificationManager mNotificationMgr =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 (NotificationManager)mContext.getSystemService(
393 Context.NOTIFICATION_SERVICE);
394 CharSequence title = mContext.getText(
395 com.android.internal.R.string.low_internal_storage_view_title);
396 CharSequence details = mContext.getText(
397 com.android.internal.R.string.low_internal_storage_view_text);
398 PendingIntent intent = PendingIntent.getActivity(mContext, 0, lowMemIntent, 0);
399 Notification notification = new Notification();
400 notification.icon = com.android.internal.R.drawable.stat_notify_disk_full;
401 notification.tickerText = title;
402 notification.flags |= Notification.FLAG_NO_CLEAR;
403 notification.setLatestEventInfo(mContext, title, details, intent);
404 mNotificationMgr.notify(LOW_MEMORY_NOTIFICATION_ID, notification);
405 mContext.sendStickyBroadcast(mStorageLowIntent);
406 }
407
408 /**
409 * Cancels low storage notification and sends OK intent.
410 */
411 private final void cancelNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800412 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 NotificationManager mNotificationMgr =
414 (NotificationManager)mContext.getSystemService(
415 Context.NOTIFICATION_SERVICE);
416 //cancel notification since memory has been freed
417 mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID);
418
419 mContext.removeStickyBroadcast(mStorageLowIntent);
420 mContext.sendBroadcast(mStorageOkIntent);
421 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700422
Jake Hambybb371632010-08-23 18:16:48 -0700423 /**
424 * Send a notification when storage is full.
425 */
426 private final void sendFullNotification() {
427 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
428 mContext.sendStickyBroadcast(mStorageFullIntent);
429 }
430
431 /**
432 * Cancels memory full notification and sends "not full" intent.
433 */
434 private final void cancelFullNotification() {
435 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
436 mContext.removeStickyBroadcast(mStorageFullIntent);
437 mContext.sendBroadcast(mStorageNotFullIntent);
438 }
439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 public void updateMemory() {
441 int callingUid = getCallingUid();
442 if(callingUid != Process.SYSTEM_UID) {
443 return;
444 }
445 // force an early check
446 postCheckMemoryMsg(true, 0);
447 }
Kenny Root62e1b4e2011-03-14 17:13:39 -0700448
449 /**
450 * Callable from other things in the system service to obtain the low memory
451 * threshold.
452 *
453 * @return low memory threshold in bytes
454 */
455 public long getMemoryLowThreshold() {
456 return mMemLowThreshold;
457 }
458
459 /**
460 * Callable from other things in the system process to check whether memory
461 * is low.
462 *
463 * @return true is memory is low
464 */
465 public boolean isMemoryLow() {
466 return mLowMemFlag;
467 }
Jeff Sharkey4b496572012-04-19 14:17:03 -0700468
469 public static class CacheFileDeletedObserver extends FileObserver {
470 public CacheFileDeletedObserver() {
471 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
472 }
473
474 @Override
475 public void onEvent(int event, String path) {
476 EventLogTags.writeCacheFileDeleted(path);
477 }
478 }
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700479
480 @Override
481 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
482 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
483 != PackageManager.PERMISSION_GRANTED) {
484
485 pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
486 + Binder.getCallingPid()
487 + ", uid=" + Binder.getCallingUid());
488 return;
489 }
490
491 pw.println("Current DeviceStorageMonitor state:");
492 pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(mContext, mFreeMem));
493 pw.print(" mTotalMemory=");
494 pw.println(Formatter.formatFileSize(mContext, mTotalMemory));
495 pw.print(" mFreeMemAfterLastCacheClear=");
496 pw.println(Formatter.formatFileSize(mContext, mFreeMemAfterLastCacheClear));
497 pw.print(" mLastReportedFreeMem=");
498 pw.print(Formatter.formatFileSize(mContext, mLastReportedFreeMem));
499 pw.print(" mLastReportedFreeMemTime=");
500 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
501 pw.println();
502 pw.print(" mLowMemFlag="); pw.print(mLowMemFlag);
503 pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
504 pw.print(" mClearSucceeded="); pw.print(mClearSucceeded);
505 pw.print(" mClearingCache="); pw.println(mClearingCache);
506 pw.print(" mMemLowThreshold=");
507 pw.print(Formatter.formatFileSize(mContext, mMemLowThreshold));
508 pw.print(" mMemFullThreshold=");
509 pw.println(Formatter.formatFileSize(mContext, mMemFullThreshold));
510 pw.print(" mMemCacheStartTrimThreshold=");
511 pw.print(Formatter.formatFileSize(mContext, mMemCacheStartTrimThreshold));
512 pw.print(" mMemCacheTrimToThreshold=");
513 pw.println(Formatter.formatFileSize(mContext, mMemCacheTrimToThreshold));
514 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515}