blob: 88050841698fcc74a0b0bde869cf14b013d974d4 [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
Adam Lesinski182f73f2013-12-05 16:48:06 -080017package com.android.server.storage;
18
19import com.android.server.EventLogTags;
20import com.android.server.SystemService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021
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;
Adam Lesinski182f73f2013-12-05 16:48:06 -080035import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import 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;
Jeff Sharkeybe722152013-02-15 16:56:38 -080043import android.os.storage.StorageManager;
Doug Zongker43866e02010-01-07 12:09:54 -080044import android.provider.Settings;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070045import android.text.format.Formatter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080047import android.util.Slog;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070048import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
Jeff Sharkeybe722152013-02-15 16:56:38 -080050import java.io.File;
51import java.io.FileDescriptor;
52import java.io.PrintWriter;
53
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054/**
Doug Zongker43866e02010-01-07 12:09:54 -080055 * This class implements a service to monitor the amount of disk
56 * storage space on the device. If the free storage on device is less
57 * than a tunable threshold value (a secure settings parameter;
58 * default 10%) a low memory notification is displayed to alert the
59 * user. If the user clicks on the low memory notification the
60 * Application Manager application gets launched to let the user free
61 * storage space.
62 *
63 * Event log events: A low memory event with the free storage on
64 * device in bytes is logged to the event log when the device goes low
65 * on storage space. The amount of free storage on the device is
66 * periodically logged to the event log. The log interval is a secure
67 * settings parameter with a default value of 12 hours. When the free
68 * storage differential goes below a threshold (again a secure
69 * settings parameter with a default value of 2MB), the free memory is
70 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080072public class DeviceStorageMonitorService extends SystemService {
73 static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080074
Adam Lesinski182f73f2013-12-05 16:48:06 -080075 static final boolean DEBUG = false;
76 static final boolean localLOGV = false;
Jeff Sharkeybe722152013-02-15 16:56:38 -080077
Adam Lesinski182f73f2013-12-05 16:48:06 -080078 static final int DEVICE_MEMORY_WHAT = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 private static final int MONITOR_INTERVAL = 1; //in minutes
80 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
Jeff Sharkeybe722152013-02-15 16:56:38 -080081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 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 -080083 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
84 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jeff Sharkeybe722152013-02-15 16:56:38 -080085
Doug Zongker3161795b2009-10-07 15:14:03 -070086 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -070087 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 private long mLastReportedFreeMem;
89 private long mLastReportedFreeMemTime;
Adam Lesinski182f73f2013-12-05 16:48:06 -080090 boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -070091 private boolean mMemFullFlag=false;
Jeff Sharkeybe722152013-02-15 16:56:38 -080092 private ContentResolver mResolver;
Doug Zongker3161795b2009-10-07 15:14:03 -070093 private long mTotalMemory; // on /data
94 private StatFs mDataFileStats;
95 private StatFs mSystemFileStats;
96 private StatFs mCacheFileStats;
Jeff Sharkeybe722152013-02-15 16:56:38 -080097
98 private static final File DATA_PATH = Environment.getDataDirectory();
99 private static final File SYSTEM_PATH = Environment.getRootDirectory();
100 private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
101
Doug Zongker3161795b2009-10-07 15:14:03 -0700102 private long mThreadStartTime = -1;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800103 boolean mClearSucceeded = false;
104 boolean mClearingCache;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 private Intent mStorageLowIntent;
106 private Intent mStorageOkIntent;
Jake Hambybb371632010-08-23 18:16:48 -0700107 private Intent mStorageFullIntent;
108 private Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 private CachePackageDataObserver mClearCacheObserver;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800110 private CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 private static final int _TRUE = 1;
112 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700113 // This is the raw threshold that has been set at which we consider
114 // storage to be low.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800115 long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700116 // This is the threshold at which we start trying to flush caches
117 // to get below the low threshold limit. It is less than the low
118 // threshold; we will allow storage to get a bit beyond the limit
119 // before flushing and checking if we are actually low.
120 private long mMemCacheStartTrimThreshold;
121 // This is the threshold that we try to get to when deleting cache
122 // files. This is greater than the low threshold so that we will flush
123 // more files than absolutely needed, to reduce the frequency that
124 // flushing takes place.
125 private long mMemCacheTrimToThreshold;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800126 private long mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 /**
129 * This string is used for ServiceManager access to this class.
130 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800131 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700134 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 * notification if the device runs low on disk space
136 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800137 private Handler mHandler = new Handler() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 @Override
139 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700140 //don't handle an invalid message
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 if (msg.what != DEVICE_MEMORY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800142 Slog.e(TAG, "Will not process invalid message");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 return;
144 }
145 checkMemory(msg.arg1 == _TRUE);
146 }
147 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700148
Adam Lesinski182f73f2013-12-05 16:48:06 -0800149 private class CachePackageDataObserver extends IPackageDataObserver.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 public void onRemoveCompleted(String packageName, boolean succeeded) {
151 mClearSucceeded = succeeded;
152 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800153 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 +", mClearingCache:"+mClearingCache+" Forcing memory check");
155 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700156 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700158
Adam Lesinski182f73f2013-12-05 16:48:06 -0800159 private void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700160 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800161 mDataFileStats.restat(DATA_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700162 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
163 mDataFileStats.getBlockSize();
164 } catch (IllegalArgumentException e) {
165 // use the old value of mFreeMem
166 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 // Allow freemem to be overridden by debug.freemem for testing
168 String debugFreeMem = SystemProperties.get("debug.freemem");
169 if (!"".equals(debugFreeMem)) {
170 mFreeMem = Long.parseLong(debugFreeMem);
171 }
Doug Zongker43866e02010-01-07 12:09:54 -0800172 // Read the log interval from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800173 long freeMemLogInterval = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700174 Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
176 //log the amount of free memory in event log
177 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700178 if((mLastReportedFreeMemTime == 0) ||
179 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700181 long mFreeSystem = -1, mFreeCache = -1;
182 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800183 mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700184 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
185 mSystemFileStats.getBlockSize();
186 } catch (IllegalArgumentException e) {
187 // ignore; report -1
188 }
189 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800190 mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700191 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
192 mCacheFileStats.getBlockSize();
193 } catch (IllegalArgumentException e) {
194 // ignore; report -1
195 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800196 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700197 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 }
Doug Zongker43866e02010-01-07 12:09:54 -0800199 // Read the reporting threshold from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800200 long threshold = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700201 Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
203 // If mFree changed significantly log the new value
204 long delta = mFreeMem - mLastReportedFreeMem;
205 if (delta > threshold || delta < -threshold) {
206 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800207 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 }
209 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700210
Adam Lesinski182f73f2013-12-05 16:48:06 -0800211 private void clearCache() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 if (mClearCacheObserver == null) {
213 // Lazy instantiation
214 mClearCacheObserver = new CachePackageDataObserver();
215 }
216 mClearingCache = true;
217 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800218 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700220 freeStorageAndNotify(mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800222 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 mClearingCache = false;
224 mClearSucceeded = false;
225 }
226 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700227
Adam Lesinski182f73f2013-12-05 16:48:06 -0800228 void checkMemory(boolean checkCache) {
Doug Zongker3161795b2009-10-07 15:14:03 -0700229 //if the thread that was started to clear cache is still running do nothing till its
230 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 // and should be accessed via a lock but even if it does this test will fail now and
232 //hopefully the next time this flag will be set to the correct value.
233 if(mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800234 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 //make sure the thread is not hung for too long
236 long diffTime = System.currentTimeMillis() - mThreadStartTime;
237 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800238 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700239 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 } else {
241 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800242 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700245 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700246 if (checkCache) {
247 // We are allowed to clear cache files at this point to
248 // try to get down below the limit, because this is not
249 // the initial call after a cache clear has been attempted.
250 // In this case we will try a cache clear if our free
251 // space has gone below the cache clear limit.
252 if (mFreeMem < mMemCacheStartTrimThreshold) {
253 // We only clear the cache if the free storage has changed
254 // a significant amount since the last time.
255 if ((mFreeMemAfterLastCacheClear-mFreeMem)
256 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
257 // See if clearing cache helps
258 // Note that clearing cache is asynchronous and so we do a
259 // memory check again once the cache has been cleared.
260 mThreadStartTime = System.currentTimeMillis();
261 mClearSucceeded = false;
262 clearCache();
263 }
264 }
265 } else {
266 // This is a call from after clearing the cache. Note
267 // the amount of free storage at this point.
268 mFreeMemAfterLastCacheClear = mFreeMem;
269 if (!mLowMemFlag) {
270 // We tried to clear the cache, but that didn't get us
271 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800272 Slog.i(TAG, "Running low on memory. Sending notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 sendNotification();
274 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700275 } else {
276 if (localLOGV) Slog.v(TAG, "Running low on memory " +
277 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 }
280 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700281 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800283 Slog.i(TAG, "Memory available. Cancelling notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 cancelNotification();
285 mLowMemFlag = false;
286 }
287 }
Jake Hambybb371632010-08-23 18:16:48 -0700288 if (mFreeMem < mMemFullThreshold) {
289 if (!mMemFullFlag) {
290 sendFullNotification();
291 mMemFullFlag = true;
292 }
293 } else {
294 if (mMemFullFlag) {
295 cancelFullNotification();
296 mMemFullFlag = false;
297 }
298 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800300 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 //keep posting messages to itself periodically
302 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
303 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700304
Adam Lesinski182f73f2013-12-05 16:48:06 -0800305 void postCheckMemoryMsg(boolean clearCache, long delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 // Remove queued messages
307 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
308 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
309 clearCache ?_TRUE : _FALSE, 0),
310 delay);
311 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 /**
314 * Constructor to run service. initializes the disk space threshold value
315 * and posts an empty message to kickstart the process.
316 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800317 @Override
318 public void onCreate(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 mLastReportedFreeMemTime = 0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800320 mResolver = context.getContentResolver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 //create StatFs object
Jeff Sharkeybe722152013-02-15 16:56:38 -0800322 mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
323 mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
324 mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700326 mTotalMemory = (long)mDataFileStats.getBlockCount() *
327 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500329 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500331 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jake Hambybb371632010-08-23 18:16:48 -0700332 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
333 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
334 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
335 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800336 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800337
Adam Lesinski182f73f2013-12-05 16:48:06 -0800338 @Override
339 public void onStart() {
Jake Hambybb371632010-08-23 18:16:48 -0700340 // cache storage thresholds
Adam Lesinski182f73f2013-12-05 16:48:06 -0800341 final StorageManager sm = StorageManager.from(getContext());
Jeff Sharkeybe722152013-02-15 16:56:38 -0800342 mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
343 mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
344
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700345 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
346 mMemCacheTrimToThreshold = mMemLowThreshold
347 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
348 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700350
351 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
352 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800353
354 publishBinderService(SERVICE, mRemoteService);
355 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
356 }
357
358 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
359 @Override
360 public void checkMemory() {
361 // force an early check
362 postCheckMemoryMsg(true, 0);
363 }
364
365 @Override
366 public boolean isMemoryLow() {
367 return mLowMemFlag;
368 }
369
370 @Override
371 public long getMemoryLowThreshold() {
372 return mMemLowThreshold;
373 }
374 };
375
376 private final IBinder mRemoteService = new Binder() {
377 @Override
378 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
379 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
380 != PackageManager.PERMISSION_GRANTED) {
381
382 pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
383 + Binder.getCallingPid()
384 + ", uid=" + Binder.getCallingUid());
385 return;
386 }
387
388 dumpImpl(pw);
389 }
390 };
391
392 void dumpImpl(PrintWriter pw) {
393 final Context context = getContext();
394
395 pw.println("Current DeviceStorageMonitor state:");
396
397 pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(context, mFreeMem));
398 pw.print(" mTotalMemory=");
399 pw.println(Formatter.formatFileSize(context, mTotalMemory));
400
401 pw.print(" mFreeMemAfterLastCacheClear=");
402 pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear));
403
404 pw.print(" mLastReportedFreeMem=");
405 pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem));
406 pw.print(" mLastReportedFreeMemTime=");
407 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
408 pw.println();
409
410 pw.print(" mLowMemFlag="); pw.print(mLowMemFlag);
411 pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
412
413 pw.print(" mClearSucceeded="); pw.print(mClearSucceeded);
414 pw.print(" mClearingCache="); pw.println(mClearingCache);
415
416 pw.print(" mMemLowThreshold=");
417 pw.print(Formatter.formatFileSize(context, mMemLowThreshold));
418 pw.print(" mMemFullThreshold=");
419 pw.println(Formatter.formatFileSize(context, mMemFullThreshold));
420
421 pw.print(" mMemCacheStartTrimThreshold=");
422 pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold));
423 pw.print(" mMemCacheTrimToThreshold=");
424 pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 /**
428 * This method sends a notification to NotificationManager to display
429 * an error dialog indicating low disk space and launch the Installer
430 * application
431 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800432 private void sendNotification() {
433 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800434 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 //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 -0800436 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 // Pack up the values and broadcast them to everyone
Dianne Hackbornf882efa2012-04-11 16:04:12 -0700438 Intent lowMemIntent = new Intent(Environment.isExternalStorageEmulated()
439 ? Settings.ACTION_INTERNAL_STORAGE_SETTINGS
440 : Intent.ACTION_MANAGE_PACKAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 lowMemIntent.putExtra("memory", mFreeMem);
442 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Doug Zongker3161795b2009-10-07 15:14:03 -0700443 NotificationManager mNotificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800444 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 Context.NOTIFICATION_SERVICE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800446 CharSequence title = context.getText(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 com.android.internal.R.string.low_internal_storage_view_title);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800448 CharSequence details = context.getText(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 com.android.internal.R.string.low_internal_storage_view_text);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800450 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700451 null, UserHandle.CURRENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 Notification notification = new Notification();
453 notification.icon = com.android.internal.R.drawable.stat_notify_disk_full;
454 notification.tickerText = title;
455 notification.flags |= Notification.FLAG_NO_CLEAR;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800456 notification.setLatestEventInfo(context, title, details, intent);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700457 mNotificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
458 UserHandle.ALL);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800459 context.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
461
462 /**
463 * Cancels low storage notification and sends OK intent.
464 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800465 private void cancelNotification() {
466 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800467 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 NotificationManager mNotificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800469 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 Context.NOTIFICATION_SERVICE);
471 //cancel notification since memory has been freed
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700472 mNotificationMgr.cancelAsUser(null, LOW_MEMORY_NOTIFICATION_ID, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473
Adam Lesinski182f73f2013-12-05 16:48:06 -0800474 context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
475 context.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700477
Jake Hambybb371632010-08-23 18:16:48 -0700478 /**
479 * Send a notification when storage is full.
480 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800481 private void sendFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700482 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800483 getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700484 }
485
486 /**
487 * Cancels memory full notification and sends "not full" intent.
488 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800489 private void cancelFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700490 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800491 getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
492 getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700493 }
494
Adam Lesinski182f73f2013-12-05 16:48:06 -0800495 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700496 public CacheFileDeletedObserver() {
497 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
498 }
499
500 @Override
501 public void onEvent(int event, String path) {
502 EventLogTags.writeCacheFileDeleted(path);
503 }
504 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505}