blob: 016c561077dc15e0753408d1c81c11f3a32ae775 [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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.IPackageDataObserver;
26import android.content.pm.IPackageManager;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070027import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.Binder;
Dianne Hackbornf882efa2012-04-11 16:04:12 -070029import android.os.Environment;
Jeff Sharkey4b496572012-04-19 14:17:03 -070030import android.os.FileObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.Handler;
32import android.os.Message;
33import android.os.Process;
34import android.os.RemoteException;
35import android.os.ServiceManager;
36import android.os.StatFs;
37import android.os.SystemClock;
38import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070039import android.os.UserHandle;
Jeff Sharkeybe722152013-02-15 16:56:38 -080040import android.os.storage.StorageManager;
Doug Zongker43866e02010-01-07 12:09:54 -080041import android.provider.Settings;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070042import android.text.format.Formatter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080044import android.util.Slog;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070045import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
Jeff Sharkeybe722152013-02-15 16:56:38 -080047import java.io.File;
48import java.io.FileDescriptor;
49import java.io.PrintWriter;
50
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051/**
Doug Zongker43866e02010-01-07 12:09:54 -080052 * This class implements a service to monitor the amount of disk
53 * storage space on the device. If the free storage on device is less
54 * than a tunable threshold value (a secure settings parameter;
55 * default 10%) a low memory notification is displayed to alert the
56 * user. If the user clicks on the low memory notification the
57 * Application Manager application gets launched to let the user free
58 * storage space.
59 *
60 * Event log events: A low memory event with the free storage on
61 * device in bytes is logged to the event log when the device goes low
62 * on storage space. The amount of free storage on the device is
63 * periodically logged to the event log. The log interval is a secure
64 * settings parameter with a default value of 12 hours. When the free
65 * storage differential goes below a threshold (again a secure
66 * settings parameter with a default value of 2MB), the free memory is
67 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 */
Kenny Rootcf0b38c2011-03-22 14:17:59 -070069public class DeviceStorageMonitorService extends Binder {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 private static final boolean DEBUG = false;
Joe Onorato43a17652011-04-06 19:22:23 -070073 private static final boolean localLOGV = false;
Jeff Sharkeybe722152013-02-15 16:56:38 -080074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 private static final int DEVICE_MEMORY_WHAT = 1;
76 private static final int MONITOR_INTERVAL = 1; //in minutes
77 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
Jeff Sharkeybe722152013-02-15 16:56:38 -080078
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 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 -080080 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
81 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jeff Sharkeybe722152013-02-15 16:56:38 -080082
Doug Zongker3161795b2009-10-07 15:14:03 -070083 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -070084 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private long mLastReportedFreeMem;
86 private long mLastReportedFreeMemTime;
87 private boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -070088 private boolean mMemFullFlag=false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private Context mContext;
Jeff Sharkeybe722152013-02-15 16:56:38 -080090 private ContentResolver mResolver;
Doug Zongker3161795b2009-10-07 15:14:03 -070091 private long mTotalMemory; // on /data
92 private StatFs mDataFileStats;
93 private StatFs mSystemFileStats;
94 private StatFs mCacheFileStats;
Jeff Sharkeybe722152013-02-15 16:56:38 -080095
96 private static final File DATA_PATH = Environment.getDataDirectory();
97 private static final File SYSTEM_PATH = Environment.getRootDirectory();
98 private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
99
Doug Zongker3161795b2009-10-07 15:14:03 -0700100 private long mThreadStartTime = -1;
101 private boolean mClearSucceeded = false;
102 private boolean mClearingCache;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private Intent mStorageLowIntent;
104 private Intent mStorageOkIntent;
Jake Hambybb371632010-08-23 18:16:48 -0700105 private Intent mStorageFullIntent;
106 private Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 private CachePackageDataObserver mClearCacheObserver;
Jeff Sharkey4b496572012-04-19 14:17:03 -0700108 private final CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 private static final int _TRUE = 1;
110 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700111 // This is the raw threshold that has been set at which we consider
112 // storage to be low.
Jake Hambybb371632010-08-23 18:16:48 -0700113 private long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700114 // This is the threshold at which we start trying to flush caches
115 // to get below the low threshold limit. It is less than the low
116 // threshold; we will allow storage to get a bit beyond the limit
117 // before flushing and checking if we are actually low.
118 private long mMemCacheStartTrimThreshold;
119 // This is the threshold that we try to get to when deleting cache
120 // files. This is greater than the low threshold so that we will flush
121 // more files than absolutely needed, to reduce the frequency that
122 // flushing takes place.
123 private long mMemCacheTrimToThreshold;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800124 private long mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 /**
127 * This string is used for ServiceManager access to this class.
128 */
Kenny Rootcf0b38c2011-03-22 14:17:59 -0700129 public static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700132 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 * notification if the device runs low on disk space
134 */
135 Handler mHandler = new Handler() {
136 @Override
137 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700138 //don't handle an invalid message
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 if (msg.what != DEVICE_MEMORY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800140 Slog.e(TAG, "Will not process invalid message");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 return;
142 }
143 checkMemory(msg.arg1 == _TRUE);
144 }
145 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 class CachePackageDataObserver extends IPackageDataObserver.Stub {
148 public void onRemoveCompleted(String packageName, boolean succeeded) {
149 mClearSucceeded = succeeded;
150 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800151 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 +", mClearingCache:"+mClearingCache+" Forcing memory check");
153 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700154 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700156
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 private final void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700158 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800159 mDataFileStats.restat(DATA_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700160 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
161 mDataFileStats.getBlockSize();
162 } catch (IllegalArgumentException e) {
163 // use the old value of mFreeMem
164 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 // Allow freemem to be overridden by debug.freemem for testing
166 String debugFreeMem = SystemProperties.get("debug.freemem");
167 if (!"".equals(debugFreeMem)) {
168 mFreeMem = Long.parseLong(debugFreeMem);
169 }
Doug Zongker43866e02010-01-07 12:09:54 -0800170 // Read the log interval from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800171 long freeMemLogInterval = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700172 Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
174 //log the amount of free memory in event log
175 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700176 if((mLastReportedFreeMemTime == 0) ||
177 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700179 long mFreeSystem = -1, mFreeCache = -1;
180 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800181 mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700182 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
183 mSystemFileStats.getBlockSize();
184 } catch (IllegalArgumentException e) {
185 // ignore; report -1
186 }
187 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800188 mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700189 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
190 mCacheFileStats.getBlockSize();
191 } catch (IllegalArgumentException e) {
192 // ignore; report -1
193 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800194 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700195 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 }
Doug Zongker43866e02010-01-07 12:09:54 -0800197 // Read the reporting threshold from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800198 long threshold = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700199 Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
201 // If mFree changed significantly log the new value
202 long delta = mFreeMem - mLastReportedFreeMem;
203 if (delta > threshold || delta < -threshold) {
204 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800205 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 private final void clearCache() {
210 if (mClearCacheObserver == null) {
211 // Lazy instantiation
212 mClearCacheObserver = new CachePackageDataObserver();
213 }
214 mClearingCache = true;
215 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800216 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700218 freeStorageAndNotify(mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800220 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 mClearingCache = false;
222 mClearSucceeded = false;
223 }
224 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 private final void checkMemory(boolean checkCache) {
Doug Zongker3161795b2009-10-07 15:14:03 -0700227 //if the thread that was started to clear cache is still running do nothing till its
228 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 // and should be accessed via a lock but even if it does this test will fail now and
230 //hopefully the next time this flag will be set to the correct value.
231 if(mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800232 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 //make sure the thread is not hung for too long
234 long diffTime = System.currentTimeMillis() - mThreadStartTime;
235 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800236 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 } else {
239 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800240 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700241
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700243 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700244 if (checkCache) {
245 // We are allowed to clear cache files at this point to
246 // try to get down below the limit, because this is not
247 // the initial call after a cache clear has been attempted.
248 // In this case we will try a cache clear if our free
249 // space has gone below the cache clear limit.
250 if (mFreeMem < mMemCacheStartTrimThreshold) {
251 // We only clear the cache if the free storage has changed
252 // a significant amount since the last time.
253 if ((mFreeMemAfterLastCacheClear-mFreeMem)
254 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
255 // See if clearing cache helps
256 // Note that clearing cache is asynchronous and so we do a
257 // memory check again once the cache has been cleared.
258 mThreadStartTime = System.currentTimeMillis();
259 mClearSucceeded = false;
260 clearCache();
261 }
262 }
263 } else {
264 // This is a call from after clearing the cache. Note
265 // the amount of free storage at this point.
266 mFreeMemAfterLastCacheClear = mFreeMem;
267 if (!mLowMemFlag) {
268 // We tried to clear the cache, but that didn't get us
269 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800270 Slog.i(TAG, "Running low on memory. Sending notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 sendNotification();
272 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700273 } else {
274 if (localLOGV) Slog.v(TAG, "Running low on memory " +
275 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
278 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700279 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800281 Slog.i(TAG, "Memory available. Cancelling notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 cancelNotification();
283 mLowMemFlag = false;
284 }
285 }
Jake Hambybb371632010-08-23 18:16:48 -0700286 if (mFreeMem < mMemFullThreshold) {
287 if (!mMemFullFlag) {
288 sendFullNotification();
289 mMemFullFlag = true;
290 }
291 } else {
292 if (mMemFullFlag) {
293 cancelFullNotification();
294 mMemFullFlag = false;
295 }
296 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800298 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 //keep posting messages to itself periodically
300 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
301 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700302
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 private void postCheckMemoryMsg(boolean clearCache, long delay) {
304 // Remove queued messages
305 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
306 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
307 clearCache ?_TRUE : _FALSE, 0),
308 delay);
309 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 /**
312 * Constructor to run service. initializes the disk space threshold value
313 * and posts an empty message to kickstart the process.
314 */
315 public DeviceStorageMonitorService(Context context) {
316 mLastReportedFreeMemTime = 0;
317 mContext = context;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800318 mResolver = mContext.getContentResolver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 //create StatFs object
Jeff Sharkeybe722152013-02-15 16:56:38 -0800320 mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
321 mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
322 mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700324 mTotalMemory = (long)mDataFileStats.getBlockCount() *
325 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500327 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500329 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jake Hambybb371632010-08-23 18:16:48 -0700330 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
331 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
332 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
333 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jeff Sharkeybe722152013-02-15 16:56:38 -0800334
Jake Hambybb371632010-08-23 18:16:48 -0700335 // cache storage thresholds
Jeff Sharkeybe722152013-02-15 16:56:38 -0800336 final StorageManager sm = StorageManager.from(context);
337 mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
338 mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
339
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700340 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
341 mMemCacheTrimToThreshold = mMemLowThreshold
342 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
343 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700345
346 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
347 mCacheFileDeletedObserver.startWatching();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 /**
351 * This method sends a notification to NotificationManager to display
352 * an error dialog indicating low disk space and launch the Installer
353 * application
354 */
355 private final void sendNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800356 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 //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 -0800358 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 // Pack up the values and broadcast them to everyone
Dianne Hackbornf882efa2012-04-11 16:04:12 -0700360 Intent lowMemIntent = new Intent(Environment.isExternalStorageEmulated()
361 ? Settings.ACTION_INTERNAL_STORAGE_SETTINGS
362 : Intent.ACTION_MANAGE_PACKAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 lowMemIntent.putExtra("memory", mFreeMem);
364 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Doug Zongker3161795b2009-10-07 15:14:03 -0700365 NotificationManager mNotificationMgr =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 (NotificationManager)mContext.getSystemService(
367 Context.NOTIFICATION_SERVICE);
368 CharSequence title = mContext.getText(
369 com.android.internal.R.string.low_internal_storage_view_title);
370 CharSequence details = mContext.getText(
371 com.android.internal.R.string.low_internal_storage_view_text);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700372 PendingIntent intent = PendingIntent.getActivityAsUser(mContext, 0, lowMemIntent, 0,
373 null, UserHandle.CURRENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 Notification notification = new Notification();
375 notification.icon = com.android.internal.R.drawable.stat_notify_disk_full;
376 notification.tickerText = title;
377 notification.flags |= Notification.FLAG_NO_CLEAR;
378 notification.setLatestEventInfo(mContext, title, details, intent);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700379 mNotificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
380 UserHandle.ALL);
Dianne Hackbornaa4b3c72012-10-02 16:23:39 -0700381 mContext.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 }
383
384 /**
385 * Cancels low storage notification and sends OK intent.
386 */
387 private final void cancelNotification() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800388 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 NotificationManager mNotificationMgr =
390 (NotificationManager)mContext.getSystemService(
391 Context.NOTIFICATION_SERVICE);
392 //cancel notification since memory has been freed
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700393 mNotificationMgr.cancelAsUser(null, LOW_MEMORY_NOTIFICATION_ID, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700395 mContext.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
396 mContext.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700398
Jake Hambybb371632010-08-23 18:16:48 -0700399 /**
400 * Send a notification when storage is full.
401 */
402 private final void sendFullNotification() {
403 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
Dianne Hackbornaa4b3c72012-10-02 16:23:39 -0700404 mContext.sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700405 }
406
407 /**
408 * Cancels memory full notification and sends "not full" intent.
409 */
410 private final void cancelFullNotification() {
411 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700412 mContext.removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
413 mContext.sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700414 }
415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 public void updateMemory() {
417 int callingUid = getCallingUid();
418 if(callingUid != Process.SYSTEM_UID) {
419 return;
420 }
421 // force an early check
422 postCheckMemoryMsg(true, 0);
423 }
Kenny Root62e1b4e2011-03-14 17:13:39 -0700424
425 /**
426 * Callable from other things in the system service to obtain the low memory
427 * threshold.
428 *
429 * @return low memory threshold in bytes
430 */
431 public long getMemoryLowThreshold() {
432 return mMemLowThreshold;
433 }
434
435 /**
436 * Callable from other things in the system process to check whether memory
437 * is low.
438 *
439 * @return true is memory is low
440 */
441 public boolean isMemoryLow() {
442 return mLowMemFlag;
443 }
Jeff Sharkey4b496572012-04-19 14:17:03 -0700444
445 public static class CacheFileDeletedObserver extends FileObserver {
446 public CacheFileDeletedObserver() {
447 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
448 }
449
450 @Override
451 public void onEvent(int event, String path) {
452 EventLogTags.writeCacheFileDeleted(path);
453 }
454 }
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700455
456 @Override
457 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
458 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
459 != PackageManager.PERMISSION_GRANTED) {
460
461 pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
462 + Binder.getCallingPid()
463 + ", uid=" + Binder.getCallingUid());
464 return;
465 }
466
467 pw.println("Current DeviceStorageMonitor state:");
468 pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(mContext, mFreeMem));
469 pw.print(" mTotalMemory=");
470 pw.println(Formatter.formatFileSize(mContext, mTotalMemory));
471 pw.print(" mFreeMemAfterLastCacheClear=");
472 pw.println(Formatter.formatFileSize(mContext, mFreeMemAfterLastCacheClear));
473 pw.print(" mLastReportedFreeMem=");
474 pw.print(Formatter.formatFileSize(mContext, mLastReportedFreeMem));
475 pw.print(" mLastReportedFreeMemTime=");
476 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
477 pw.println();
478 pw.print(" mLowMemFlag="); pw.print(mLowMemFlag);
479 pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
480 pw.print(" mClearSucceeded="); pw.print(mClearSucceeded);
481 pw.print(" mClearingCache="); pw.println(mClearingCache);
482 pw.print(" mMemLowThreshold=");
483 pw.print(Formatter.formatFileSize(mContext, mMemLowThreshold));
484 pw.print(" mMemFullThreshold=");
485 pw.println(Formatter.formatFileSize(mContext, mMemFullThreshold));
486 pw.print(" mMemCacheStartTrimThreshold=");
487 pw.print(Formatter.formatFileSize(mContext, mMemCacheStartTrimThreshold));
488 pw.print(" mMemCacheTrimToThreshold=");
489 pw.println(Formatter.formatFileSize(mContext, mMemCacheTrimToThreshold));
490 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491}