blob: 90c711a4794149c7f24068fc3c3629a3ab8fe330 [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;
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -080021import com.android.server.pm.InstructionSets;
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
Brian Carlstroma39871e2014-09-29 13:44:04 -070054import dalvik.system.VMRuntime;
55
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056/**
Doug Zongker43866e02010-01-07 12:09:54 -080057 * This class implements a service to monitor the amount of disk
58 * storage space on the device. If the free storage on device is less
59 * than a tunable threshold value (a secure settings parameter;
60 * default 10%) a low memory notification is displayed to alert the
61 * user. If the user clicks on the low memory notification the
62 * Application Manager application gets launched to let the user free
63 * storage space.
64 *
65 * Event log events: A low memory event with the free storage on
66 * device in bytes is logged to the event log when the device goes low
67 * on storage space. The amount of free storage on the device is
68 * periodically logged to the event log. The log interval is a secure
69 * settings parameter with a default value of 12 hours. When the free
70 * storage differential goes below a threshold (again a secure
71 * settings parameter with a default value of 2MB), the free memory is
72 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080074public class DeviceStorageMonitorService extends SystemService {
75 static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080076
Jeff Sharkey529f91f2015-04-18 20:23:13 -070077 // TODO: extend to watch and manage caches on all private volumes
78
Adam Lesinski182f73f2013-12-05 16:48:06 -080079 static final boolean DEBUG = false;
80 static final boolean localLOGV = false;
Jeff Sharkeybe722152013-02-15 16:56:38 -080081
Adam Lesinski182f73f2013-12-05 16:48:06 -080082 static final int DEVICE_MEMORY_WHAT = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 private static final int MONITOR_INTERVAL = 1; //in minutes
84 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
Jeff Sharkeybe722152013-02-15 16:56:38 -080085
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 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 -080087 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
88 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jeff Sharkeybe722152013-02-15 16:56:38 -080089
Richard Uhlerd42fe852016-08-12 13:51:51 -070090 // com.android.internal.R.string.low_internal_storage_view_text_no_boot
91 // hard codes 250MB in the message as the storage space required for the
92 // boot image.
93 private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * 1024 * 1024;
94
Doug Zongker3161795b2009-10-07 15:14:03 -070095 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -070096 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 private long mLastReportedFreeMem;
98 private long mLastReportedFreeMemTime;
Adam Lesinski182f73f2013-12-05 16:48:06 -080099 boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -0700100 private boolean mMemFullFlag=false;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700101 private final boolean mIsBootImageOnDisk;
Jeff Brownb880d882014-02-10 19:47:07 -0800102 private final ContentResolver mResolver;
103 private final long mTotalMemory; // on /data
104 private final StatFs mDataFileStats;
105 private final StatFs mSystemFileStats;
106 private final StatFs mCacheFileStats;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800107
108 private static final File DATA_PATH = Environment.getDataDirectory();
109 private static final File SYSTEM_PATH = Environment.getRootDirectory();
110 private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
111
Doug Zongker3161795b2009-10-07 15:14:03 -0700112 private long mThreadStartTime = -1;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800113 boolean mClearSucceeded = false;
114 boolean mClearingCache;
Jeff Brownb880d882014-02-10 19:47:07 -0800115 private final Intent mStorageLowIntent;
116 private final Intent mStorageOkIntent;
117 private final Intent mStorageFullIntent;
118 private final Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 private CachePackageDataObserver mClearCacheObserver;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800120 private CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private static final int _TRUE = 1;
122 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700123 // This is the raw threshold that has been set at which we consider
124 // storage to be low.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800125 long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700126 // This is the threshold at which we start trying to flush caches
127 // to get below the low threshold limit. It is less than the low
128 // threshold; we will allow storage to get a bit beyond the limit
129 // before flushing and checking if we are actually low.
130 private long mMemCacheStartTrimThreshold;
131 // This is the threshold that we try to get to when deleting cache
132 // files. This is greater than the low threshold so that we will flush
133 // more files than absolutely needed, to reduce the frequency that
134 // flushing takes place.
135 private long mMemCacheTrimToThreshold;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800136 private long mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 /**
139 * This string is used for ServiceManager access to this class.
140 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800141 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700144 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 * notification if the device runs low on disk space
146 */
Jeff Brownb880d882014-02-10 19:47:07 -0800147 private final Handler mHandler = new Handler() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 @Override
149 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700150 //don't handle an invalid message
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 if (msg.what != DEVICE_MEMORY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800152 Slog.e(TAG, "Will not process invalid message");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 return;
154 }
155 checkMemory(msg.arg1 == _TRUE);
156 }
157 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700158
Adam Lesinski182f73f2013-12-05 16:48:06 -0800159 private class CachePackageDataObserver extends IPackageDataObserver.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 public void onRemoveCompleted(String packageName, boolean succeeded) {
161 mClearSucceeded = succeeded;
162 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800163 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 +", mClearingCache:"+mClearingCache+" Forcing memory check");
165 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700166 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700168
Adam Lesinski182f73f2013-12-05 16:48:06 -0800169 private void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700170 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800171 mDataFileStats.restat(DATA_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700172 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
173 mDataFileStats.getBlockSize();
174 } catch (IllegalArgumentException e) {
175 // use the old value of mFreeMem
176 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 // Allow freemem to be overridden by debug.freemem for testing
178 String debugFreeMem = SystemProperties.get("debug.freemem");
179 if (!"".equals(debugFreeMem)) {
180 mFreeMem = Long.parseLong(debugFreeMem);
181 }
Doug Zongker43866e02010-01-07 12:09:54 -0800182 // Read the log interval from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800183 long freeMemLogInterval = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700184 Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
186 //log the amount of free memory in event log
187 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700188 if((mLastReportedFreeMemTime == 0) ||
189 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700191 long mFreeSystem = -1, mFreeCache = -1;
192 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800193 mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700194 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
195 mSystemFileStats.getBlockSize();
196 } catch (IllegalArgumentException e) {
197 // ignore; report -1
198 }
199 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800200 mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700201 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
202 mCacheFileStats.getBlockSize();
203 } catch (IllegalArgumentException e) {
204 // ignore; report -1
205 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800206 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700207 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 }
Doug Zongker43866e02010-01-07 12:09:54 -0800209 // Read the reporting threshold from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800210 long threshold = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700211 Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
213 // If mFree changed significantly log the new value
214 long delta = mFreeMem - mLastReportedFreeMem;
215 if (delta > threshold || delta < -threshold) {
216 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800217 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
219 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700220
Adam Lesinski182f73f2013-12-05 16:48:06 -0800221 private void clearCache() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 if (mClearCacheObserver == null) {
223 // Lazy instantiation
224 mClearCacheObserver = new CachePackageDataObserver();
225 }
226 mClearingCache = true;
227 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800228 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Jeff Sharkey529f91f2015-04-18 20:23:13 -0700230 freeStorageAndNotify(null, mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800232 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 mClearingCache = false;
234 mClearSucceeded = false;
235 }
236 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700237
Adam Lesinski182f73f2013-12-05 16:48:06 -0800238 void checkMemory(boolean checkCache) {
Doug Zongker3161795b2009-10-07 15:14:03 -0700239 //if the thread that was started to clear cache is still running do nothing till its
240 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 // and should be accessed via a lock but even if it does this test will fail now and
242 //hopefully the next time this flag will be set to the correct value.
243 if(mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800244 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 //make sure the thread is not hung for too long
246 long diffTime = System.currentTimeMillis() - mThreadStartTime;
247 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800248 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 } else {
251 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800252 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700255 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700256 if (checkCache) {
257 // We are allowed to clear cache files at this point to
258 // try to get down below the limit, because this is not
259 // the initial call after a cache clear has been attempted.
260 // In this case we will try a cache clear if our free
261 // space has gone below the cache clear limit.
262 if (mFreeMem < mMemCacheStartTrimThreshold) {
263 // We only clear the cache if the free storage has changed
264 // a significant amount since the last time.
265 if ((mFreeMemAfterLastCacheClear-mFreeMem)
266 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
267 // See if clearing cache helps
268 // Note that clearing cache is asynchronous and so we do a
269 // memory check again once the cache has been cleared.
270 mThreadStartTime = System.currentTimeMillis();
271 mClearSucceeded = false;
272 clearCache();
273 }
274 }
275 } else {
276 // This is a call from after clearing the cache. Note
277 // the amount of free storage at this point.
278 mFreeMemAfterLastCacheClear = mFreeMem;
279 if (!mLowMemFlag) {
280 // We tried to clear the cache, but that didn't get us
281 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800282 Slog.i(TAG, "Running low on memory. Sending notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 sendNotification();
284 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700285 } else {
286 if (localLOGV) Slog.v(TAG, "Running low on memory " +
287 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
290 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700291 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800293 Slog.i(TAG, "Memory available. Cancelling notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 cancelNotification();
295 mLowMemFlag = false;
296 }
297 }
Richard Uhlerd42fe852016-08-12 13:51:51 -0700298 if (!mLowMemFlag && !mIsBootImageOnDisk && mFreeMem < BOOT_IMAGE_STORAGE_REQUIREMENT) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700299 Slog.i(TAG, "No boot image on disk due to lack of space. Sending notification");
300 sendNotification();
Richard Uhlerd42fe852016-08-12 13:51:51 -0700301 mLowMemFlag = true;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700302 }
Jake Hambybb371632010-08-23 18:16:48 -0700303 if (mFreeMem < mMemFullThreshold) {
304 if (!mMemFullFlag) {
305 sendFullNotification();
306 mMemFullFlag = true;
307 }
308 } else {
309 if (mMemFullFlag) {
310 cancelFullNotification();
311 mMemFullFlag = false;
312 }
313 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800315 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 //keep posting messages to itself periodically
317 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
318 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700319
Adam Lesinski182f73f2013-12-05 16:48:06 -0800320 void postCheckMemoryMsg(boolean clearCache, long delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 // Remove queued messages
322 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
323 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
324 clearCache ?_TRUE : _FALSE, 0),
325 delay);
326 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700327
Jeff Brownb880d882014-02-10 19:47:07 -0800328 public DeviceStorageMonitorService(Context context) {
329 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 mLastReportedFreeMemTime = 0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800331 mResolver = context.getContentResolver();
Brian Carlstroma39871e2014-09-29 13:44:04 -0700332 mIsBootImageOnDisk = isBootImageOnDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 //create StatFs object
Jeff Sharkeybe722152013-02-15 16:56:38 -0800334 mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
335 mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
336 mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700338 mTotalMemory = (long)mDataFileStats.getBlockCount() *
339 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500341 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Jeff Hamilton4b330922010-06-04 15:16:06 -0500343 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jake Hambybb371632010-08-23 18:16:48 -0700344 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
345 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
346 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
347 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800348 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800349
Brian Carlstroma39871e2014-09-29 13:44:04 -0700350 private static boolean isBootImageOnDisk() {
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -0800351 for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700352 if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
353 return false;
354 }
355 }
356 return true;
357 }
358
Jeff Brownb880d882014-02-10 19:47:07 -0800359 /**
360 * Initializes the disk space threshold value and posts an empty message to
361 * kickstart the process.
362 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800363 @Override
364 public void onStart() {
Jake Hambybb371632010-08-23 18:16:48 -0700365 // cache storage thresholds
Adam Lesinski182f73f2013-12-05 16:48:06 -0800366 final StorageManager sm = StorageManager.from(getContext());
Jeff Sharkeybe722152013-02-15 16:56:38 -0800367 mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
368 mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
369
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700370 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
371 mMemCacheTrimToThreshold = mMemLowThreshold
372 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
373 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700375
376 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
377 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800378
379 publishBinderService(SERVICE, mRemoteService);
380 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
381 }
382
383 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
384 @Override
385 public void checkMemory() {
386 // force an early check
387 postCheckMemoryMsg(true, 0);
388 }
389
390 @Override
391 public boolean isMemoryLow() {
Richard Uhlerd42fe852016-08-12 13:51:51 -0700392 return mLowMemFlag;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800393 }
394
395 @Override
396 public long getMemoryLowThreshold() {
397 return mMemLowThreshold;
398 }
399 };
400
401 private final IBinder mRemoteService = new Binder() {
402 @Override
403 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
404 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
405 != PackageManager.PERMISSION_GRANTED) {
406
407 pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
408 + Binder.getCallingPid()
409 + ", uid=" + Binder.getCallingUid());
410 return;
411 }
412
413 dumpImpl(pw);
414 }
415 };
416
417 void dumpImpl(PrintWriter pw) {
418 final Context context = getContext();
419
420 pw.println("Current DeviceStorageMonitor state:");
421
422 pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(context, mFreeMem));
423 pw.print(" mTotalMemory=");
424 pw.println(Formatter.formatFileSize(context, mTotalMemory));
425
426 pw.print(" mFreeMemAfterLastCacheClear=");
427 pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear));
428
429 pw.print(" mLastReportedFreeMem=");
430 pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem));
431 pw.print(" mLastReportedFreeMemTime=");
432 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
433 pw.println();
434
435 pw.print(" mLowMemFlag="); pw.print(mLowMemFlag);
436 pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
Brian Carlstroma39871e2014-09-29 13:44:04 -0700437 pw.print(" mIsBootImageOnDisk="); pw.print(mIsBootImageOnDisk);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800438
439 pw.print(" mClearSucceeded="); pw.print(mClearSucceeded);
440 pw.print(" mClearingCache="); pw.println(mClearingCache);
441
442 pw.print(" mMemLowThreshold=");
443 pw.print(Formatter.formatFileSize(context, mMemLowThreshold));
444 pw.print(" mMemFullThreshold=");
445 pw.println(Formatter.formatFileSize(context, mMemFullThreshold));
446
447 pw.print(" mMemCacheStartTrimThreshold=");
448 pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold));
449 pw.print(" mMemCacheTrimToThreshold=");
450 pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 /**
454 * This method sends a notification to NotificationManager to display
455 * an error dialog indicating low disk space and launch the Installer
456 * application
457 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800458 private void sendNotification() {
459 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800460 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 //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 -0800462 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 // Pack up the values and broadcast them to everyone
Daniel Nishi690346b2016-06-17 10:21:48 -0700464 Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 lowMemIntent.putExtra("memory", mFreeMem);
466 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Doug Zongker3161795b2009-10-07 15:14:03 -0700467 NotificationManager mNotificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800468 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 Context.NOTIFICATION_SERVICE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800470 CharSequence title = context.getText(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 com.android.internal.R.string.low_internal_storage_view_title);
Brian Carlstroma39871e2014-09-29 13:44:04 -0700472 CharSequence details = context.getText(mIsBootImageOnDisk
473 ? com.android.internal.R.string.low_internal_storage_view_text
474 : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800475 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700476 null, UserHandle.CURRENT);
Brian Carlstroma39871e2014-09-29 13:44:04 -0700477 Notification notification = new Notification.Builder(context)
478 .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
479 .setTicker(title)
Alan Viverette4a357cd2015-03-18 18:37:18 -0700480 .setColor(context.getColor(
Brian Carlstroma39871e2014-09-29 13:44:04 -0700481 com.android.internal.R.color.system_notification_accent_color))
482 .setContentTitle(title)
483 .setContentText(details)
484 .setContentIntent(intent)
485 .setStyle(new Notification.BigTextStyle()
486 .bigText(details))
487 .setVisibility(Notification.VISIBILITY_PUBLIC)
488 .setCategory(Notification.CATEGORY_SYSTEM)
489 .build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 notification.flags |= Notification.FLAG_NO_CLEAR;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700491 mNotificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
492 UserHandle.ALL);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800493 context.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 }
495
496 /**
497 * Cancels low storage notification and sends OK intent.
498 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800499 private void cancelNotification() {
500 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800501 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 NotificationManager mNotificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800503 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 Context.NOTIFICATION_SERVICE);
505 //cancel notification since memory has been freed
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700506 mNotificationMgr.cancelAsUser(null, LOW_MEMORY_NOTIFICATION_ID, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507
Adam Lesinski182f73f2013-12-05 16:48:06 -0800508 context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
509 context.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700511
Jake Hambybb371632010-08-23 18:16:48 -0700512 /**
513 * Send a notification when storage is full.
514 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800515 private void sendFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700516 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800517 getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700518 }
519
520 /**
521 * Cancels memory full notification and sends "not full" intent.
522 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800523 private void cancelFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700524 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800525 getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
526 getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700527 }
528
Adam Lesinski182f73f2013-12-05 16:48:06 -0800529 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700530 public CacheFileDeletedObserver() {
531 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
532 }
533
534 @Override
535 public void onEvent(int event, String path) {
536 EventLogTags.writeCacheFileDeleted(path);
537 }
538 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539}