blob: 12836dbb4820d80734c30359c302cc805798f508 [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
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -080019import android.app.NotificationChannel;
Geoffrey Pitschaf759c52017-02-15 09:35:38 -050020import com.android.internal.notification.SystemNotificationChannels;
Adam Lesinski182f73f2013-12-05 16:48:06 -080021import com.android.server.EventLogTags;
22import com.android.server.SystemService;
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -080023import com.android.server.pm.InstructionSets;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.app.Notification;
25import android.app.NotificationManager;
26import android.app.PendingIntent;
27import android.content.ContentResolver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.pm.IPackageDataObserver;
31import android.content.pm.IPackageManager;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070032import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.os.Binder;
Dianne Hackbornf882efa2012-04-11 16:04:12 -070034import android.os.Environment;
Jeff Sharkey4b496572012-04-19 14:17:03 -070035import android.os.FileObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.Handler;
Adam Lesinski182f73f2013-12-05 16:48:06 -080037import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.RemoteException;
40import android.os.ServiceManager;
41import android.os.StatFs;
42import android.os.SystemClock;
43import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070044import android.os.UserHandle;
Jeff Sharkeybe722152013-02-15 16:56:38 -080045import android.os.storage.StorageManager;
Doug Zongker43866e02010-01-07 12:09:54 -080046import android.provider.Settings;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070047import android.text.format.Formatter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080049import android.util.Slog;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070050import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
Jeff Sharkeybe722152013-02-15 16:56:38 -080052import java.io.File;
53import java.io.FileDescriptor;
54import java.io.PrintWriter;
55
Brian Carlstroma39871e2014-09-29 13:44:04 -070056import dalvik.system.VMRuntime;
57
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058/**
Doug Zongker43866e02010-01-07 12:09:54 -080059 * This class implements a service to monitor the amount of disk
60 * storage space on the device. If the free storage on device is less
61 * than a tunable threshold value (a secure settings parameter;
62 * default 10%) a low memory notification is displayed to alert the
63 * user. If the user clicks on the low memory notification the
64 * Application Manager application gets launched to let the user free
65 * storage space.
66 *
67 * Event log events: A low memory event with the free storage on
68 * device in bytes is logged to the event log when the device goes low
69 * on storage space. The amount of free storage on the device is
70 * periodically logged to the event log. The log interval is a secure
71 * settings parameter with a default value of 12 hours. When the free
72 * storage differential goes below a threshold (again a secure
73 * settings parameter with a default value of 2MB), the free memory is
74 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080076public class DeviceStorageMonitorService extends SystemService {
77 static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080078
Jeff Sharkey529f91f2015-04-18 20:23:13 -070079 // TODO: extend to watch and manage caches on all private volumes
80
Adam Lesinski182f73f2013-12-05 16:48:06 -080081 static final boolean DEBUG = false;
82 static final boolean localLOGV = false;
Jeff Sharkeybe722152013-02-15 16:56:38 -080083
Adam Lesinski182f73f2013-12-05 16:48:06 -080084 static final int DEVICE_MEMORY_WHAT = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private static final int MONITOR_INTERVAL = 1; //in minutes
86 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
Jeff Sharkeybe722152013-02-15 16:56:38 -080087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 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 -080089 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
90 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jeff Sharkeybe722152013-02-15 16:56:38 -080091
Richard Uhlerd42fe852016-08-12 13:51:51 -070092 // com.android.internal.R.string.low_internal_storage_view_text_no_boot
93 // hard codes 250MB in the message as the storage space required for the
94 // boot image.
95 private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * 1024 * 1024;
96
Doug Zongker3161795b2009-10-07 15:14:03 -070097 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -070098 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private long mLastReportedFreeMem;
100 private long mLastReportedFreeMemTime;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800101 boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -0700102 private boolean mMemFullFlag=false;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700103 private final boolean mIsBootImageOnDisk;
Jeff Brownb880d882014-02-10 19:47:07 -0800104 private final ContentResolver mResolver;
105 private final long mTotalMemory; // on /data
106 private final StatFs mDataFileStats;
107 private final StatFs mSystemFileStats;
108 private final StatFs mCacheFileStats;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800109
110 private static final File DATA_PATH = Environment.getDataDirectory();
111 private static final File SYSTEM_PATH = Environment.getRootDirectory();
112 private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
113
Doug Zongker3161795b2009-10-07 15:14:03 -0700114 private long mThreadStartTime = -1;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800115 boolean mClearSucceeded = false;
116 boolean mClearingCache;
Jeff Brownb880d882014-02-10 19:47:07 -0800117 private final Intent mStorageLowIntent;
118 private final Intent mStorageOkIntent;
119 private final Intent mStorageFullIntent;
120 private final Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private CachePackageDataObserver mClearCacheObserver;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800122 private CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 private static final int _TRUE = 1;
124 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700125 // This is the raw threshold that has been set at which we consider
126 // storage to be low.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800127 long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700128 // This is the threshold at which we start trying to flush caches
129 // to get below the low threshold limit. It is less than the low
130 // threshold; we will allow storage to get a bit beyond the limit
131 // before flushing and checking if we are actually low.
132 private long mMemCacheStartTrimThreshold;
133 // This is the threshold that we try to get to when deleting cache
134 // files. This is greater than the low threshold so that we will flush
135 // more files than absolutely needed, to reduce the frequency that
136 // flushing takes place.
137 private long mMemCacheTrimToThreshold;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800138 private long mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 /**
141 * This string is used for ServiceManager access to this class.
142 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800143 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700144
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500145 private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700148 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 * notification if the device runs low on disk space
150 */
Jeff Brownb880d882014-02-10 19:47:07 -0800151 private final Handler mHandler = new Handler() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 @Override
153 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700154 //don't handle an invalid message
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 if (msg.what != DEVICE_MEMORY_WHAT) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800156 Slog.e(TAG, "Will not process invalid message");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 return;
158 }
159 checkMemory(msg.arg1 == _TRUE);
160 }
161 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700162
Adam Lesinski182f73f2013-12-05 16:48:06 -0800163 private class CachePackageDataObserver extends IPackageDataObserver.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 public void onRemoveCompleted(String packageName, boolean succeeded) {
165 mClearSucceeded = succeeded;
166 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800167 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 +", mClearingCache:"+mClearingCache+" Forcing memory check");
169 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700172
Adam Lesinski182f73f2013-12-05 16:48:06 -0800173 private void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700174 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800175 mDataFileStats.restat(DATA_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700176 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
177 mDataFileStats.getBlockSize();
178 } catch (IllegalArgumentException e) {
179 // use the old value of mFreeMem
180 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 // Allow freemem to be overridden by debug.freemem for testing
182 String debugFreeMem = SystemProperties.get("debug.freemem");
183 if (!"".equals(debugFreeMem)) {
184 mFreeMem = Long.parseLong(debugFreeMem);
185 }
Doug Zongker43866e02010-01-07 12:09:54 -0800186 // Read the log interval from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800187 long freeMemLogInterval = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700188 Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
190 //log the amount of free memory in event log
191 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700192 if((mLastReportedFreeMemTime == 0) ||
193 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700195 long mFreeSystem = -1, mFreeCache = -1;
196 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800197 mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700198 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
199 mSystemFileStats.getBlockSize();
200 } catch (IllegalArgumentException e) {
201 // ignore; report -1
202 }
203 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800204 mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700205 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
206 mCacheFileStats.getBlockSize();
207 } catch (IllegalArgumentException e) {
208 // ignore; report -1
209 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800210 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700211 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 }
Doug Zongker43866e02010-01-07 12:09:54 -0800213 // Read the reporting threshold from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800214 long threshold = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700215 Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
217 // If mFree changed significantly log the new value
218 long delta = mFreeMem - mLastReportedFreeMem;
219 if (delta > threshold || delta < -threshold) {
220 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800221 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
223 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700224
Adam Lesinski182f73f2013-12-05 16:48:06 -0800225 private void clearCache() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 if (mClearCacheObserver == null) {
227 // Lazy instantiation
228 mClearCacheObserver = new CachePackageDataObserver();
229 }
230 mClearingCache = true;
231 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800232 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Jeff Sharkey529f91f2015-04-18 20:23:13 -0700234 freeStorageAndNotify(null, mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800236 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 mClearingCache = false;
238 mClearSucceeded = false;
239 }
240 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700241
Adam Lesinski182f73f2013-12-05 16:48:06 -0800242 void checkMemory(boolean checkCache) {
Doug Zongker3161795b2009-10-07 15:14:03 -0700243 //if the thread that was started to clear cache is still running do nothing till its
244 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 // and should be accessed via a lock but even if it does this test will fail now and
246 //hopefully the next time this flag will be set to the correct value.
247 if(mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800248 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 //make sure the thread is not hung for too long
250 long diffTime = System.currentTimeMillis() - mThreadStartTime;
251 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800252 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 } else {
255 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800256 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700257
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700259 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700260 if (checkCache) {
261 // We are allowed to clear cache files at this point to
262 // try to get down below the limit, because this is not
263 // the initial call after a cache clear has been attempted.
264 // In this case we will try a cache clear if our free
265 // space has gone below the cache clear limit.
266 if (mFreeMem < mMemCacheStartTrimThreshold) {
267 // We only clear the cache if the free storage has changed
268 // a significant amount since the last time.
269 if ((mFreeMemAfterLastCacheClear-mFreeMem)
270 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
271 // See if clearing cache helps
272 // Note that clearing cache is asynchronous and so we do a
273 // memory check again once the cache has been cleared.
274 mThreadStartTime = System.currentTimeMillis();
275 mClearSucceeded = false;
276 clearCache();
277 }
278 }
279 } else {
280 // This is a call from after clearing the cache. Note
281 // the amount of free storage at this point.
282 mFreeMemAfterLastCacheClear = mFreeMem;
283 if (!mLowMemFlag) {
284 // We tried to clear the cache, but that didn't get us
285 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800286 Slog.i(TAG, "Running low on memory. Sending notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 sendNotification();
288 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700289 } else {
290 if (localLOGV) Slog.v(TAG, "Running low on memory " +
291 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 }
294 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700295 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800297 Slog.i(TAG, "Memory available. Cancelling notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 cancelNotification();
299 mLowMemFlag = false;
300 }
301 }
Richard Uhlerd42fe852016-08-12 13:51:51 -0700302 if (!mLowMemFlag && !mIsBootImageOnDisk && mFreeMem < BOOT_IMAGE_STORAGE_REQUIREMENT) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700303 Slog.i(TAG, "No boot image on disk due to lack of space. Sending notification");
304 sendNotification();
Richard Uhlerd42fe852016-08-12 13:51:51 -0700305 mLowMemFlag = true;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700306 }
Jake Hambybb371632010-08-23 18:16:48 -0700307 if (mFreeMem < mMemFullThreshold) {
308 if (!mMemFullFlag) {
309 sendFullNotification();
310 mMemFullFlag = true;
311 }
312 } else {
313 if (mMemFullFlag) {
314 cancelFullNotification();
315 mMemFullFlag = false;
316 }
317 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800319 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 //keep posting messages to itself periodically
321 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
322 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700323
Adam Lesinski182f73f2013-12-05 16:48:06 -0800324 void postCheckMemoryMsg(boolean clearCache, long delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 // Remove queued messages
326 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
327 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
328 clearCache ?_TRUE : _FALSE, 0),
329 delay);
330 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700331
Jeff Brownb880d882014-02-10 19:47:07 -0800332 public DeviceStorageMonitorService(Context context) {
333 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 mLastReportedFreeMemTime = 0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800335 mResolver = context.getContentResolver();
Brian Carlstroma39871e2014-09-29 13:44:04 -0700336 mIsBootImageOnDisk = isBootImageOnDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 //create StatFs object
Jeff Sharkeybe722152013-02-15 16:56:38 -0800338 mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
339 mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
340 mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700342 mTotalMemory = (long)mDataFileStats.getBlockCount() *
343 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Christopher Tate42a386b2016-11-07 12:21:21 -0800345 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
346 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Christopher Tate42a386b2016-11-07 12:21:21 -0800348 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
349 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Jake Hambybb371632010-08-23 18:16:48 -0700350 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
351 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
352 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
353 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800354 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800355
Brian Carlstroma39871e2014-09-29 13:44:04 -0700356 private static boolean isBootImageOnDisk() {
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -0800357 for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700358 if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
359 return false;
360 }
361 }
362 return true;
363 }
364
Jeff Brownb880d882014-02-10 19:47:07 -0800365 /**
366 * Initializes the disk space threshold value and posts an empty message to
367 * kickstart the process.
368 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800369 @Override
370 public void onStart() {
Jake Hambybb371632010-08-23 18:16:48 -0700371 // cache storage thresholds
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800372 Context context = getContext();
373 final StorageManager sm = StorageManager.from(context);
Jeff Sharkeybe722152013-02-15 16:56:38 -0800374 mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
375 mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
376
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700377 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
378 mMemCacheTrimToThreshold = mMemLowThreshold
379 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
380 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700382
383 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
384 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800385
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800386 // Ensure that the notification channel is set up
387 NotificationManager notificationMgr =
388 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
389 PackageManager packageManager = context.getPackageManager();
390 boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
391
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500392 if (isTv) {
393 notificationMgr.createNotificationChannel(new NotificationChannel(
394 TV_NOTIFICATION_CHANNEL_ID,
395 context.getString(
396 com.android.internal.R.string.device_storage_monitor_notification_channel),
397 NotificationManager.IMPORTANCE_HIGH));
398 }
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800399
Adam Lesinski182f73f2013-12-05 16:48:06 -0800400 publishBinderService(SERVICE, mRemoteService);
401 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
402 }
403
404 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
405 @Override
406 public void checkMemory() {
407 // force an early check
408 postCheckMemoryMsg(true, 0);
409 }
410
411 @Override
412 public boolean isMemoryLow() {
Richard Uhlerd42fe852016-08-12 13:51:51 -0700413 return mLowMemFlag;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800414 }
415
416 @Override
417 public long getMemoryLowThreshold() {
418 return mMemLowThreshold;
419 }
420 };
421
422 private final IBinder mRemoteService = new Binder() {
423 @Override
424 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
425 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
426 != PackageManager.PERMISSION_GRANTED) {
427
428 pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
429 + Binder.getCallingPid()
430 + ", uid=" + Binder.getCallingUid());
431 return;
432 }
433
434 dumpImpl(pw);
435 }
436 };
437
438 void dumpImpl(PrintWriter pw) {
439 final Context context = getContext();
440
441 pw.println("Current DeviceStorageMonitor state:");
442
443 pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(context, mFreeMem));
444 pw.print(" mTotalMemory=");
445 pw.println(Formatter.formatFileSize(context, mTotalMemory));
446
447 pw.print(" mFreeMemAfterLastCacheClear=");
448 pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear));
449
450 pw.print(" mLastReportedFreeMem=");
451 pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem));
452 pw.print(" mLastReportedFreeMemTime=");
453 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
454 pw.println();
455
456 pw.print(" mLowMemFlag="); pw.print(mLowMemFlag);
457 pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
Brian Carlstroma39871e2014-09-29 13:44:04 -0700458 pw.print(" mIsBootImageOnDisk="); pw.print(mIsBootImageOnDisk);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800459
460 pw.print(" mClearSucceeded="); pw.print(mClearSucceeded);
461 pw.print(" mClearingCache="); pw.println(mClearingCache);
462
463 pw.print(" mMemLowThreshold=");
464 pw.print(Formatter.formatFileSize(context, mMemLowThreshold));
465 pw.print(" mMemFullThreshold=");
466 pw.println(Formatter.formatFileSize(context, mMemFullThreshold));
467
468 pw.print(" mMemCacheStartTrimThreshold=");
469 pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold));
470 pw.print(" mMemCacheTrimToThreshold=");
471 pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 /**
475 * This method sends a notification to NotificationManager to display
476 * an error dialog indicating low disk space and launch the Installer
477 * application
478 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800479 private void sendNotification() {
480 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800481 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 //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 -0800483 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 // Pack up the values and broadcast them to everyone
Daniel Nishi690346b2016-06-17 10:21:48 -0700485 Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 lowMemIntent.putExtra("memory", mFreeMem);
487 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800488 NotificationManager notificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800489 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 Context.NOTIFICATION_SERVICE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800491 CharSequence title = context.getText(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 com.android.internal.R.string.low_internal_storage_view_title);
Brian Carlstroma39871e2014-09-29 13:44:04 -0700493 CharSequence details = context.getText(mIsBootImageOnDisk
494 ? com.android.internal.R.string.low_internal_storage_view_text
495 : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800496 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700497 null, UserHandle.CURRENT);
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500498 Notification notification =
499 new Notification.Builder(context, SystemNotificationChannels.ALERTS)
500 .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
501 .setTicker(title)
502 .setColor(context.getColor(
503 com.android.internal.R.color.system_notification_accent_color))
504 .setContentTitle(title)
505 .setContentText(details)
506 .setContentIntent(intent)
507 .setStyle(new Notification.BigTextStyle()
508 .bigText(details))
509 .setVisibility(Notification.VISIBILITY_PUBLIC)
510 .setCategory(Notification.CATEGORY_SYSTEM)
511 .extend(new Notification.TvExtender()
512 .setChannel(TV_NOTIFICATION_CHANNEL_ID))
513 .build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 notification.flags |= Notification.FLAG_NO_CLEAR;
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800515 notificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700516 UserHandle.ALL);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800517 context.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 }
519
520 /**
521 * Cancels low storage notification and sends OK intent.
522 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800523 private void cancelNotification() {
524 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800525 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 NotificationManager mNotificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800527 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 Context.NOTIFICATION_SERVICE);
529 //cancel notification since memory has been freed
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700530 mNotificationMgr.cancelAsUser(null, LOW_MEMORY_NOTIFICATION_ID, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531
Adam Lesinski182f73f2013-12-05 16:48:06 -0800532 context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
533 context.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700535
Jake Hambybb371632010-08-23 18:16:48 -0700536 /**
537 * Send a notification when storage is full.
538 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800539 private void sendFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700540 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800541 getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700542 }
543
544 /**
545 * Cancels memory full notification and sends "not full" intent.
546 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800547 private void cancelFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700548 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800549 getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
550 getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700551 }
552
Adam Lesinski182f73f2013-12-05 16:48:06 -0800553 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700554 public CacheFileDeletedObserver() {
555 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
556 }
557
558 @Override
559 public void onEvent(int event, String path) {
560 EventLogTags.writeCacheFileDeleted(path);
561 }
562 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563}