blob: 0639eeed42e4ebaf017911d25922b9428252afc5 [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;
37import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.RemoteException;
Dianne Hackborn532ea262017-03-17 17:50:55 -070039import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.ServiceManager;
Dianne Hackborn532ea262017-03-17 17:50:55 -070041import android.os.ShellCallback;
42import android.os.ShellCommand;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.StatFs;
44import android.os.SystemClock;
45import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070046import android.os.UserHandle;
Jeff Sharkeybe722152013-02-15 16:56:38 -080047import android.os.storage.StorageManager;
Doug Zongker43866e02010-01-07 12:09:54 -080048import android.provider.Settings;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070049import android.text.format.Formatter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080051import android.util.Slog;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070052import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
Jeff Sharkeybe722152013-02-15 16:56:38 -080054import java.io.File;
55import java.io.FileDescriptor;
56import java.io.PrintWriter;
Dianne Hackborn532ea262017-03-17 17:50:55 -070057import java.util.concurrent.atomic.AtomicInteger;
Jeff Sharkeybe722152013-02-15 16:56:38 -080058
Brian Carlstroma39871e2014-09-29 13:44:04 -070059import dalvik.system.VMRuntime;
60
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061/**
Doug Zongker43866e02010-01-07 12:09:54 -080062 * This class implements a service to monitor the amount of disk
63 * storage space on the device. If the free storage on device is less
64 * than a tunable threshold value (a secure settings parameter;
65 * default 10%) a low memory notification is displayed to alert the
66 * user. If the user clicks on the low memory notification the
67 * Application Manager application gets launched to let the user free
68 * storage space.
69 *
70 * Event log events: A low memory event with the free storage on
71 * device in bytes is logged to the event log when the device goes low
72 * on storage space. The amount of free storage on the device is
73 * periodically logged to the event log. The log interval is a secure
74 * settings parameter with a default value of 12 hours. When the free
75 * storage differential goes below a threshold (again a secure
76 * settings parameter with a default value of 2MB), the free memory is
77 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080079public class DeviceStorageMonitorService extends SystemService {
80 static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080081
Dianne Hackborn532ea262017-03-17 17:50:55 -070082 /**
83 * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
84 * Current int sequence number of the update.
85 */
86 public static final String EXTRA_SEQUENCE = "seq";
87
Jeff Sharkey529f91f2015-04-18 20:23:13 -070088 // TODO: extend to watch and manage caches on all private volumes
89
Adam Lesinski182f73f2013-12-05 16:48:06 -080090 static final boolean DEBUG = false;
91 static final boolean localLOGV = false;
Jeff Sharkeybe722152013-02-15 16:56:38 -080092
Adam Lesinski182f73f2013-12-05 16:48:06 -080093 static final int DEVICE_MEMORY_WHAT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -070094 static final int FORCE_MEMORY_WHAT = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 private static final int MONITOR_INTERVAL = 1; //in minutes
96 private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
Jeff Sharkeybe722152013-02-15 16:56:38 -080097
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 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 -080099 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
100 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800101
Richard Uhlerd42fe852016-08-12 13:51:51 -0700102 // com.android.internal.R.string.low_internal_storage_view_text_no_boot
103 // hard codes 250MB in the message as the storage space required for the
104 // boot image.
105 private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * 1024 * 1024;
106
Doug Zongker3161795b2009-10-07 15:14:03 -0700107 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700108 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 private long mLastReportedFreeMem;
110 private long mLastReportedFreeMemTime;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800111 boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -0700112 private boolean mMemFullFlag=false;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700113 private final boolean mIsBootImageOnDisk;
Jeff Brownb880d882014-02-10 19:47:07 -0800114 private final ContentResolver mResolver;
115 private final long mTotalMemory; // on /data
116 private final StatFs mDataFileStats;
117 private final StatFs mSystemFileStats;
118 private final StatFs mCacheFileStats;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800119
120 private static final File DATA_PATH = Environment.getDataDirectory();
121 private static final File SYSTEM_PATH = Environment.getRootDirectory();
122 private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
123
Doug Zongker3161795b2009-10-07 15:14:03 -0700124 private long mThreadStartTime = -1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700125 boolean mUpdatesStopped;
126 AtomicInteger mSeq = new AtomicInteger(1);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800127 boolean mClearSucceeded = false;
128 boolean mClearingCache;
Jeff Brownb880d882014-02-10 19:47:07 -0800129 private final Intent mStorageLowIntent;
130 private final Intent mStorageOkIntent;
131 private final Intent mStorageFullIntent;
132 private final Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 private CachePackageDataObserver mClearCacheObserver;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800134 private CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 private static final int _TRUE = 1;
136 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700137 // This is the raw threshold that has been set at which we consider
138 // storage to be low.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800139 long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700140 // This is the threshold at which we start trying to flush caches
141 // to get below the low threshold limit. It is less than the low
142 // threshold; we will allow storage to get a bit beyond the limit
143 // before flushing and checking if we are actually low.
144 private long mMemCacheStartTrimThreshold;
145 // This is the threshold that we try to get to when deleting cache
146 // files. This is greater than the low threshold so that we will flush
147 // more files than absolutely needed, to reduce the frequency that
148 // flushing takes place.
149 private long mMemCacheTrimToThreshold;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800150 private long mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 /**
153 * This string is used for ServiceManager access to this class.
154 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800155 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700156
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500157 private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700160 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 * notification if the device runs low on disk space
162 */
Jeff Brownb880d882014-02-10 19:47:07 -0800163 private final Handler mHandler = new Handler() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 @Override
165 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700166 //don't handle an invalid message
Dianne Hackborn532ea262017-03-17 17:50:55 -0700167 switch (msg.what) {
168 case DEVICE_MEMORY_WHAT:
169 checkMemory(msg.arg1 == _TRUE);
170 return;
171 case FORCE_MEMORY_WHAT:
172 forceMemory(msg.arg1, msg.arg2);
173 return;
174 default:
175 Slog.w(TAG, "Will not process invalid message");
176 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
179 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700180
Adam Lesinski182f73f2013-12-05 16:48:06 -0800181 private class CachePackageDataObserver extends IPackageDataObserver.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 public void onRemoveCompleted(String packageName, boolean succeeded) {
183 mClearSucceeded = succeeded;
184 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800185 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 +", mClearingCache:"+mClearingCache+" Forcing memory check");
187 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700190
Adam Lesinski182f73f2013-12-05 16:48:06 -0800191 private void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700192 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800193 mDataFileStats.restat(DATA_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700194 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
195 mDataFileStats.getBlockSize();
196 } catch (IllegalArgumentException e) {
197 // use the old value of mFreeMem
198 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 // Allow freemem to be overridden by debug.freemem for testing
200 String debugFreeMem = SystemProperties.get("debug.freemem");
201 if (!"".equals(debugFreeMem)) {
202 mFreeMem = Long.parseLong(debugFreeMem);
203 }
Doug Zongker43866e02010-01-07 12:09:54 -0800204 // Read the log interval from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800205 long freeMemLogInterval = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700206 Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
208 //log the amount of free memory in event log
209 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700210 if((mLastReportedFreeMemTime == 0) ||
211 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700213 long mFreeSystem = -1, mFreeCache = -1;
214 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800215 mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700216 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
217 mSystemFileStats.getBlockSize();
218 } catch (IllegalArgumentException e) {
219 // ignore; report -1
220 }
221 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800222 mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700223 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
224 mCacheFileStats.getBlockSize();
225 } catch (IllegalArgumentException e) {
226 // ignore; report -1
227 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800228 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700229 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 }
Doug Zongker43866e02010-01-07 12:09:54 -0800231 // Read the reporting threshold from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800232 long threshold = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700233 Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
235 // If mFree changed significantly log the new value
236 long delta = mFreeMem - mLastReportedFreeMem;
237 if (delta > threshold || delta < -threshold) {
238 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800239 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 }
241 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700242
Adam Lesinski182f73f2013-12-05 16:48:06 -0800243 private void clearCache() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 if (mClearCacheObserver == null) {
245 // Lazy instantiation
246 mClearCacheObserver = new CachePackageDataObserver();
247 }
248 mClearingCache = true;
249 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800250 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Jeff Sharkey529f91f2015-04-18 20:23:13 -0700252 freeStorageAndNotify(null, mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800254 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 mClearingCache = false;
256 mClearSucceeded = false;
257 }
258 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700259
Dianne Hackborn532ea262017-03-17 17:50:55 -0700260 void forceMemory(int opts, int seq) {
261 if ((opts&OPTION_UPDATES_STOPPED) == 0) {
262 if (mUpdatesStopped) {
263 mUpdatesStopped = false;
264 checkMemory(true);
265 }
266 } else {
267 mUpdatesStopped = true;
268 final boolean forceLow = (opts&OPTION_STORAGE_LOW) != 0;
269 if (mLowMemFlag != forceLow || (opts&OPTION_FORCE_UPDATE) != 0) {
270 mLowMemFlag = forceLow;
271 if (forceLow) {
272 sendNotification(seq);
273 } else {
274 cancelNotification(seq);
275 }
276 }
277 }
278 }
279
Adam Lesinski182f73f2013-12-05 16:48:06 -0800280 void checkMemory(boolean checkCache) {
Dianne Hackborn532ea262017-03-17 17:50:55 -0700281 if (mUpdatesStopped) {
282 return;
283 }
284
Doug Zongker3161795b2009-10-07 15:14:03 -0700285 //if the thread that was started to clear cache is still running do nothing till its
286 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 // and should be accessed via a lock but even if it does this test will fail now and
288 //hopefully the next time this flag will be set to the correct value.
Dianne Hackborn532ea262017-03-17 17:50:55 -0700289 if (mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800290 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 //make sure the thread is not hung for too long
292 long diffTime = System.currentTimeMillis() - mThreadStartTime;
293 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800294 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700295 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 } else {
297 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800298 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700299
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700301 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700302 if (checkCache) {
303 // We are allowed to clear cache files at this point to
304 // try to get down below the limit, because this is not
305 // the initial call after a cache clear has been attempted.
306 // In this case we will try a cache clear if our free
307 // space has gone below the cache clear limit.
308 if (mFreeMem < mMemCacheStartTrimThreshold) {
309 // We only clear the cache if the free storage has changed
310 // a significant amount since the last time.
311 if ((mFreeMemAfterLastCacheClear-mFreeMem)
312 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
313 // See if clearing cache helps
314 // Note that clearing cache is asynchronous and so we do a
315 // memory check again once the cache has been cleared.
316 mThreadStartTime = System.currentTimeMillis();
317 mClearSucceeded = false;
318 clearCache();
319 }
320 }
321 } else {
322 // This is a call from after clearing the cache. Note
323 // the amount of free storage at this point.
324 mFreeMemAfterLastCacheClear = mFreeMem;
325 if (!mLowMemFlag) {
326 // We tried to clear the cache, but that didn't get us
327 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800328 Slog.i(TAG, "Running low on memory. Sending notification");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700329 sendNotification(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700331 } else {
332 if (localLOGV) Slog.v(TAG, "Running low on memory " +
333 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 }
336 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700337 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800339 Slog.i(TAG, "Memory available. Cancelling notification");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700340 cancelNotification(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 mLowMemFlag = false;
342 }
343 }
Richard Uhlerd42fe852016-08-12 13:51:51 -0700344 if (!mLowMemFlag && !mIsBootImageOnDisk && mFreeMem < BOOT_IMAGE_STORAGE_REQUIREMENT) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700345 Slog.i(TAG, "No boot image on disk due to lack of space. Sending notification");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700346 sendNotification(0);
Richard Uhlerd42fe852016-08-12 13:51:51 -0700347 mLowMemFlag = true;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700348 }
Jake Hambybb371632010-08-23 18:16:48 -0700349 if (mFreeMem < mMemFullThreshold) {
350 if (!mMemFullFlag) {
351 sendFullNotification();
352 mMemFullFlag = true;
353 }
354 } else {
355 if (mMemFullFlag) {
356 cancelFullNotification();
357 mMemFullFlag = false;
358 }
359 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800361 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 //keep posting messages to itself periodically
363 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
364 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700365
Adam Lesinski182f73f2013-12-05 16:48:06 -0800366 void postCheckMemoryMsg(boolean clearCache, long delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 // Remove queued messages
368 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
369 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
370 clearCache ?_TRUE : _FALSE, 0),
371 delay);
372 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700373
Jeff Brownb880d882014-02-10 19:47:07 -0800374 public DeviceStorageMonitorService(Context context) {
375 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 mLastReportedFreeMemTime = 0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800377 mResolver = context.getContentResolver();
Brian Carlstroma39871e2014-09-29 13:44:04 -0700378 mIsBootImageOnDisk = isBootImageOnDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 //create StatFs object
Jeff Sharkeybe722152013-02-15 16:56:38 -0800380 mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
381 mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
382 mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700384 mTotalMemory = (long)mDataFileStats.getBlockCount() *
385 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Christopher Tate42a386b2016-11-07 12:21:21 -0800387 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
388 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Christopher Tate42a386b2016-11-07 12:21:21 -0800390 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
391 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Jake Hambybb371632010-08-23 18:16:48 -0700392 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
393 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
394 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
395 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800396 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800397
Brian Carlstroma39871e2014-09-29 13:44:04 -0700398 private static boolean isBootImageOnDisk() {
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -0800399 for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700400 if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
401 return false;
402 }
403 }
404 return true;
405 }
406
Jeff Brownb880d882014-02-10 19:47:07 -0800407 /**
408 * Initializes the disk space threshold value and posts an empty message to
409 * kickstart the process.
410 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800411 @Override
412 public void onStart() {
Jake Hambybb371632010-08-23 18:16:48 -0700413 // cache storage thresholds
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800414 Context context = getContext();
415 final StorageManager sm = StorageManager.from(context);
Jeff Sharkeybe722152013-02-15 16:56:38 -0800416 mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
417 mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
418
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700419 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
420 mMemCacheTrimToThreshold = mMemLowThreshold
421 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
422 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700424
425 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
426 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800427
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800428 // Ensure that the notification channel is set up
429 NotificationManager notificationMgr =
430 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
431 PackageManager packageManager = context.getPackageManager();
432 boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
433
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500434 if (isTv) {
435 notificationMgr.createNotificationChannel(new NotificationChannel(
436 TV_NOTIFICATION_CHANNEL_ID,
437 context.getString(
438 com.android.internal.R.string.device_storage_monitor_notification_channel),
439 NotificationManager.IMPORTANCE_HIGH));
440 }
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800441
Adam Lesinski182f73f2013-12-05 16:48:06 -0800442 publishBinderService(SERVICE, mRemoteService);
443 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
444 }
445
446 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
447 @Override
448 public void checkMemory() {
449 // force an early check
450 postCheckMemoryMsg(true, 0);
451 }
452
453 @Override
454 public boolean isMemoryLow() {
Richard Uhlerd42fe852016-08-12 13:51:51 -0700455 return mLowMemFlag;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800456 }
457
458 @Override
459 public long getMemoryLowThreshold() {
460 return mMemLowThreshold;
461 }
462 };
463
Dianne Hackborn532ea262017-03-17 17:50:55 -0700464 private final Binder mRemoteService = new Binder() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800465 @Override
466 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
467 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
468 != PackageManager.PERMISSION_GRANTED) {
469
470 pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
471 + Binder.getCallingPid()
472 + ", uid=" + Binder.getCallingUid());
473 return;
474 }
475
Dianne Hackborn532ea262017-03-17 17:50:55 -0700476 dumpImpl(fd, pw, args);
477 }
478
479 @Override
480 public void onShellCommand(FileDescriptor in, FileDescriptor out,
481 FileDescriptor err, String[] args, ShellCallback callback,
482 ResultReceiver resultReceiver) {
483 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800484 }
485 };
486
Dianne Hackborn532ea262017-03-17 17:50:55 -0700487 class Shell extends ShellCommand {
488 @Override
489 public int onCommand(String cmd) {
490 return onShellCommand(this, cmd);
491 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800492
Dianne Hackborn532ea262017-03-17 17:50:55 -0700493 @Override
494 public void onHelp() {
495 PrintWriter pw = getOutPrintWriter();
496 dumpHelp(pw);
497 }
498 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800499
Dianne Hackborn532ea262017-03-17 17:50:55 -0700500 static final int OPTION_FORCE_UPDATE = 1<<0;
501 static final int OPTION_UPDATES_STOPPED = 1<<1;
502 static final int OPTION_STORAGE_LOW = 1<<2;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800503
Dianne Hackborn532ea262017-03-17 17:50:55 -0700504 int parseOptions(Shell shell) {
505 String opt;
506 int opts = 0;
507 while ((opt = shell.getNextOption()) != null) {
508 if ("-f".equals(opt)) {
509 opts |= OPTION_FORCE_UPDATE;
510 }
511 }
512 return opts;
513 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800514
Dianne Hackborn532ea262017-03-17 17:50:55 -0700515 int onShellCommand(Shell shell, String cmd) {
516 if (cmd == null) {
517 return shell.handleDefaultCommands(cmd);
518 }
519 PrintWriter pw = shell.getOutPrintWriter();
520 switch (cmd) {
521 case "force-low": {
522 int opts = parseOptions(shell);
523 getContext().enforceCallingOrSelfPermission(
524 android.Manifest.permission.DEVICE_POWER, null);
525 int seq = mSeq.incrementAndGet();
526 mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
527 opts | OPTION_UPDATES_STOPPED | OPTION_STORAGE_LOW, seq));
528 if ((opts & OPTION_FORCE_UPDATE) != 0) {
529 pw.println(seq);
530 }
531 } break;
532 case "force-not-low": {
533 int opts = parseOptions(shell);
534 getContext().enforceCallingOrSelfPermission(
535 android.Manifest.permission.DEVICE_POWER, null);
536 int seq = mSeq.incrementAndGet();
537 mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
538 opts | OPTION_UPDATES_STOPPED, seq));
539 if ((opts & OPTION_FORCE_UPDATE) != 0) {
540 pw.println(seq);
541 }
542 } break;
543 case "reset": {
544 int opts = parseOptions(shell);
545 getContext().enforceCallingOrSelfPermission(
546 android.Manifest.permission.DEVICE_POWER, null);
547 int seq = mSeq.incrementAndGet();
548 mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
549 opts, seq));
550 if ((opts & OPTION_FORCE_UPDATE) != 0) {
551 pw.println(seq);
552 }
553 } break;
554 default:
555 return shell.handleDefaultCommands(cmd);
556 }
557 return 0;
558 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800559
Dianne Hackborn532ea262017-03-17 17:50:55 -0700560 static void dumpHelp(PrintWriter pw) {
561 pw.println("Device storage monitor service (devicestoragemonitor) commands:");
562 pw.println(" help");
563 pw.println(" Print this help text.");
564 pw.println(" force-low [-f]");
565 pw.println(" Force storage to be low, freezing storage state.");
566 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
567 pw.println(" force-not-low [-f]");
568 pw.println(" Force storage to not be low, freezing storage state.");
569 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
570 pw.println(" reset [-f]");
571 pw.println(" Unfreeze storage state, returning to current real values.");
572 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
573 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800574
Dianne Hackborn532ea262017-03-17 17:50:55 -0700575 void dumpImpl(FileDescriptor fd, PrintWriter pw, String[] args) {
576 if (args == null || args.length == 0 || "-a".equals(args[0])) {
577 final Context context = getContext();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800578
Dianne Hackborn532ea262017-03-17 17:50:55 -0700579 pw.println("Current DeviceStorageMonitor state:");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800580
Dianne Hackborn532ea262017-03-17 17:50:55 -0700581 pw.print(" mFreeMem=");
582 pw.print(Formatter.formatFileSize(context, mFreeMem));
583 pw.print(" mTotalMemory=");
584 pw.println(Formatter.formatFileSize(context, mTotalMemory));
585
586 pw.print(" mFreeMemAfterLastCacheClear=");
587 pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear));
588
589 pw.print(" mLastReportedFreeMem=");
590 pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem));
591 pw.print(" mLastReportedFreeMemTime=");
592 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
593 pw.println();
594
595 if (mUpdatesStopped) {
596 pw.print(" mUpdatesStopped=");
597 pw.print(mUpdatesStopped);
598 pw.print(" mSeq=");
599 pw.println(mSeq.get());
600 } else {
601 pw.print(" mClearSucceeded=");
602 pw.print(mClearSucceeded);
603 pw.print(" mClearingCache=");
604 pw.println(mClearingCache);
605 }
606
607 pw.print(" mLowMemFlag=");
608 pw.print(mLowMemFlag);
609 pw.print(" mMemFullFlag=");
610 pw.println(mMemFullFlag);
611
612 pw.print(" mMemLowThreshold=");
613 pw.print(Formatter.formatFileSize(context, mMemLowThreshold));
614 pw.print(" mMemFullThreshold=");
615 pw.println(Formatter.formatFileSize(context, mMemFullThreshold));
616
617 pw.print(" mMemCacheStartTrimThreshold=");
618 pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold));
619 pw.print(" mMemCacheTrimToThreshold=");
620 pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold));
621
622 pw.print(" mIsBootImageOnDisk="); pw.println(mIsBootImageOnDisk);
623 } else {
624 Shell shell = new Shell();
625 shell.exec(mRemoteService, null, fd, null, args, null, new ResultReceiver(null));
626 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 /**
630 * This method sends a notification to NotificationManager to display
631 * an error dialog indicating low disk space and launch the Installer
632 * application
633 */
Dianne Hackborn532ea262017-03-17 17:50:55 -0700634 private void sendNotification(int seq) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800635 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800636 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 //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 -0800638 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 // Pack up the values and broadcast them to everyone
Daniel Nishi690346b2016-06-17 10:21:48 -0700640 Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 lowMemIntent.putExtra("memory", mFreeMem);
642 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800643 NotificationManager notificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800644 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 Context.NOTIFICATION_SERVICE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800646 CharSequence title = context.getText(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 com.android.internal.R.string.low_internal_storage_view_title);
Brian Carlstroma39871e2014-09-29 13:44:04 -0700648 CharSequence details = context.getText(mIsBootImageOnDisk
649 ? com.android.internal.R.string.low_internal_storage_view_text
650 : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800651 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700652 null, UserHandle.CURRENT);
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500653 Notification notification =
654 new Notification.Builder(context, SystemNotificationChannels.ALERTS)
655 .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
656 .setTicker(title)
657 .setColor(context.getColor(
658 com.android.internal.R.color.system_notification_accent_color))
659 .setContentTitle(title)
660 .setContentText(details)
661 .setContentIntent(intent)
662 .setStyle(new Notification.BigTextStyle()
663 .bigText(details))
664 .setVisibility(Notification.VISIBILITY_PUBLIC)
665 .setCategory(Notification.CATEGORY_SYSTEM)
666 .extend(new Notification.TvExtender()
667 .setChannel(TV_NOTIFICATION_CHANNEL_ID))
668 .build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 notification.flags |= Notification.FLAG_NO_CLEAR;
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800670 notificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700671 UserHandle.ALL);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700672 Intent broadcast = new Intent(mStorageLowIntent);
673 if (seq != 0) {
674 broadcast.putExtra(EXTRA_SEQUENCE, seq);
675 }
676 context.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 }
678
679 /**
680 * Cancels low storage notification and sends OK intent.
681 */
Dianne Hackborn532ea262017-03-17 17:50:55 -0700682 private void cancelNotification(int seq) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800683 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800684 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 NotificationManager mNotificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800686 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 Context.NOTIFICATION_SERVICE);
688 //cancel notification since memory has been freed
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700689 mNotificationMgr.cancelAsUser(null, LOW_MEMORY_NOTIFICATION_ID, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690
Adam Lesinski182f73f2013-12-05 16:48:06 -0800691 context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700692 Intent broadcast = new Intent(mStorageOkIntent);
693 if (seq != 0) {
694 broadcast.putExtra(EXTRA_SEQUENCE, seq);
695 }
696 context.sendBroadcastAsUser(broadcast, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700698
Jake Hambybb371632010-08-23 18:16:48 -0700699 /**
700 * Send a notification when storage is full.
701 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800702 private void sendFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700703 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800704 getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700705 }
706
707 /**
708 * Cancels memory full notification and sends "not full" intent.
709 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800710 private void cancelFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700711 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800712 getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
713 getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700714 }
715
Adam Lesinski182f73f2013-12-05 16:48:06 -0800716 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700717 public CacheFileDeletedObserver() {
718 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
719 }
720
721 @Override
722 public void onEvent(int event, String path) {
723 EventLogTags.writeCacheFileDeleted(path);
724 }
725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726}