blob: f773d895b81f8f9d584fd12b0810049ce437be52 [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;
Chris Wren282cfef2017-03-27 15:01:44 -040020
21import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
Geoffrey Pitschaf759c52017-02-15 09:35:38 -050022import com.android.internal.notification.SystemNotificationChannels;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060023import com.android.internal.util.DumpUtils;
Adam Lesinski182f73f2013-12-05 16:48:06 -080024import com.android.server.EventLogTags;
25import com.android.server.SystemService;
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -080026import com.android.server.pm.InstructionSets;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.Notification;
28import android.app.NotificationManager;
29import android.app.PendingIntent;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.pm.IPackageDataObserver;
34import android.content.pm.IPackageManager;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070035import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.Binder;
Dianne Hackbornf882efa2012-04-11 16:04:12 -070037import android.os.Environment;
Jeff Sharkey4b496572012-04-19 14:17:03 -070038import android.os.FileObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.Handler;
40import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.RemoteException;
Dianne Hackborn532ea262017-03-17 17:50:55 -070042import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.ServiceManager;
Dianne Hackborn532ea262017-03-17 17:50:55 -070044import android.os.ShellCallback;
45import android.os.ShellCommand;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.StatFs;
47import android.os.SystemClock;
48import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070049import android.os.UserHandle;
Jeff Sharkeybe722152013-02-15 16:56:38 -080050import android.os.storage.StorageManager;
Doug Zongker43866e02010-01-07 12:09:54 -080051import android.provider.Settings;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070052import android.text.format.Formatter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.util.EventLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080054import android.util.Slog;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070055import android.util.TimeUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
Jeff Sharkeybe722152013-02-15 16:56:38 -080057import java.io.File;
58import java.io.FileDescriptor;
59import java.io.PrintWriter;
Dianne Hackborn532ea262017-03-17 17:50:55 -070060import java.util.concurrent.atomic.AtomicInteger;
Jeff Sharkeybe722152013-02-15 16:56:38 -080061
Brian Carlstroma39871e2014-09-29 13:44:04 -070062import dalvik.system.VMRuntime;
63
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064/**
Doug Zongker43866e02010-01-07 12:09:54 -080065 * This class implements a service to monitor the amount of disk
66 * storage space on the device. If the free storage on device is less
67 * than a tunable threshold value (a secure settings parameter;
68 * default 10%) a low memory notification is displayed to alert the
69 * user. If the user clicks on the low memory notification the
70 * Application Manager application gets launched to let the user free
71 * storage space.
72 *
73 * Event log events: A low memory event with the free storage on
74 * device in bytes is logged to the event log when the device goes low
75 * on storage space. The amount of free storage on the device is
76 * periodically logged to the event log. The log interval is a secure
77 * settings parameter with a default value of 12 hours. When the free
78 * storage differential goes below a threshold (again a secure
79 * settings parameter with a default value of 2MB), the free memory is
80 * logged to the event log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080082public class DeviceStorageMonitorService extends SystemService {
83 static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080084
Dianne Hackborn532ea262017-03-17 17:50:55 -070085 /**
86 * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
87 * Current int sequence number of the update.
88 */
89 public static final String EXTRA_SEQUENCE = "seq";
90
Jeff Sharkey529f91f2015-04-18 20:23:13 -070091 // TODO: extend to watch and manage caches on all private volumes
92
Adam Lesinski182f73f2013-12-05 16:48:06 -080093 static final boolean DEBUG = false;
94 static final boolean localLOGV = false;
Jeff Sharkeybe722152013-02-15 16:56:38 -080095
Adam Lesinski182f73f2013-12-05 16:48:06 -080096 static final int DEVICE_MEMORY_WHAT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -070097 static final int FORCE_MEMORY_WHAT = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 private static final int MONITOR_INTERVAL = 1; //in minutes
Jeff Sharkeybe722152013-02-15 16:56:38 -080099
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 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 -0800101 private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
102 private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800103
Richard Uhlerd42fe852016-08-12 13:51:51 -0700104 // com.android.internal.R.string.low_internal_storage_view_text_no_boot
105 // hard codes 250MB in the message as the storage space required for the
106 // boot image.
107 private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * 1024 * 1024;
108
Doug Zongker3161795b2009-10-07 15:14:03 -0700109 private long mFreeMem; // on /data
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700110 private long mFreeMemAfterLastCacheClear; // on /data
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 private long mLastReportedFreeMem;
112 private long mLastReportedFreeMemTime;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800113 boolean mLowMemFlag=false;
Jake Hambybb371632010-08-23 18:16:48 -0700114 private boolean mMemFullFlag=false;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700115 private final boolean mIsBootImageOnDisk;
Jeff Brownb880d882014-02-10 19:47:07 -0800116 private final ContentResolver mResolver;
117 private final long mTotalMemory; // on /data
118 private final StatFs mDataFileStats;
119 private final StatFs mSystemFileStats;
120 private final StatFs mCacheFileStats;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800121
122 private static final File DATA_PATH = Environment.getDataDirectory();
123 private static final File SYSTEM_PATH = Environment.getRootDirectory();
124 private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
125
Doug Zongker3161795b2009-10-07 15:14:03 -0700126 private long mThreadStartTime = -1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700127 boolean mUpdatesStopped;
128 AtomicInteger mSeq = new AtomicInteger(1);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800129 boolean mClearSucceeded = false;
130 boolean mClearingCache;
Jeff Brownb880d882014-02-10 19:47:07 -0800131 private final Intent mStorageLowIntent;
132 private final Intent mStorageOkIntent;
133 private final Intent mStorageFullIntent;
134 private final Intent mStorageNotFullIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 private CachePackageDataObserver mClearCacheObserver;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800136 private CacheFileDeletedObserver mCacheFileDeletedObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 private static final int _TRUE = 1;
138 private static final int _FALSE = 0;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700139 // This is the raw threshold that has been set at which we consider
140 // storage to be low.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800141 long mMemLowThreshold;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700142 // This is the threshold at which we start trying to flush caches
143 // to get below the low threshold limit. It is less than the low
144 // threshold; we will allow storage to get a bit beyond the limit
145 // before flushing and checking if we are actually low.
146 private long mMemCacheStartTrimThreshold;
147 // This is the threshold that we try to get to when deleting cache
148 // files. This is greater than the low threshold so that we will flush
149 // more files than absolutely needed, to reduce the frequency that
150 // flushing takes place.
151 private long mMemCacheTrimToThreshold;
Jeff Sharkeybe722152013-02-15 16:56:38 -0800152 private long mMemFullThreshold;
Doug Zongker3161795b2009-10-07 15:14:03 -0700153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 /**
155 * This string is used for ServiceManager access to this class.
156 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800157 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700158
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500159 private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 /**
Doug Zongker3161795b2009-10-07 15:14:03 -0700162 * Handler that checks the amount of disk space on the device and sends a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 * notification if the device runs low on disk space
164 */
Jeff Brownb880d882014-02-10 19:47:07 -0800165 private final Handler mHandler = new Handler() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 @Override
167 public void handleMessage(Message msg) {
Jake Hambybb371632010-08-23 18:16:48 -0700168 //don't handle an invalid message
Dianne Hackborn532ea262017-03-17 17:50:55 -0700169 switch (msg.what) {
170 case DEVICE_MEMORY_WHAT:
171 checkMemory(msg.arg1 == _TRUE);
172 return;
173 case FORCE_MEMORY_WHAT:
174 forceMemory(msg.arg1, msg.arg2);
175 return;
176 default:
177 Slog.w(TAG, "Will not process invalid message");
178 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 }
181 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700182
Adam Lesinski182f73f2013-12-05 16:48:06 -0800183 private class CachePackageDataObserver extends IPackageDataObserver.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 public void onRemoveCompleted(String packageName, boolean succeeded) {
185 mClearSucceeded = succeeded;
186 mClearingCache = false;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800187 if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 +", mClearingCache:"+mClearingCache+" Forcing memory check");
189 postCheckMemoryMsg(false, 0);
Doug Zongker3161795b2009-10-07 15:14:03 -0700190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700192
Adam Lesinski182f73f2013-12-05 16:48:06 -0800193 private void restatDataDir() {
Doug Zongker3161795b2009-10-07 15:14:03 -0700194 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800195 mDataFileStats.restat(DATA_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700196 mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
197 mDataFileStats.getBlockSize();
198 } catch (IllegalArgumentException e) {
199 // use the old value of mFreeMem
200 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 // Allow freemem to be overridden by debug.freemem for testing
202 String debugFreeMem = SystemProperties.get("debug.freemem");
203 if (!"".equals(debugFreeMem)) {
204 mFreeMem = Long.parseLong(debugFreeMem);
205 }
Doug Zongker43866e02010-01-07 12:09:54 -0800206 // Read the log interval from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800207 long freeMemLogInterval = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700208 Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
210 //log the amount of free memory in event log
211 long currTime = SystemClock.elapsedRealtime();
Doug Zongker3161795b2009-10-07 15:14:03 -0700212 if((mLastReportedFreeMemTime == 0) ||
213 (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 mLastReportedFreeMemTime = currTime;
Doug Zongker3161795b2009-10-07 15:14:03 -0700215 long mFreeSystem = -1, mFreeCache = -1;
216 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800217 mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700218 mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
219 mSystemFileStats.getBlockSize();
220 } catch (IllegalArgumentException e) {
221 // ignore; report -1
222 }
223 try {
Jeff Sharkeybe722152013-02-15 16:56:38 -0800224 mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
Doug Zongker3161795b2009-10-07 15:14:03 -0700225 mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
226 mCacheFileStats.getBlockSize();
227 } catch (IllegalArgumentException e) {
228 // ignore; report -1
229 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800230 EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
Doug Zongker3161795b2009-10-07 15:14:03 -0700231 mFreeMem, mFreeSystem, mFreeCache);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 }
Doug Zongker43866e02010-01-07 12:09:54 -0800233 // Read the reporting threshold from secure settings
Jeff Sharkeybe722152013-02-15 16:56:38 -0800234 long threshold = Settings.Global.getLong(mResolver,
Jeff Sharkey625239a2012-09-26 22:03:49 -0700235 Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
237 // If mFree changed significantly log the new value
238 long delta = mFreeMem - mLastReportedFreeMem;
239 if (delta > threshold || delta < -threshold) {
240 mLastReportedFreeMem = mFreeMem;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800241 EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 }
243 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700244
Adam Lesinski182f73f2013-12-05 16:48:06 -0800245 private void clearCache() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 if (mClearCacheObserver == null) {
247 // Lazy instantiation
248 mClearCacheObserver = new CachePackageDataObserver();
249 }
250 mClearingCache = true;
251 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800252 if (localLOGV) Slog.i(TAG, "Clearing cache");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
Jeff Sharkey529f91f2015-04-18 20:23:13 -0700254 freeStorageAndNotify(null, mMemCacheTrimToThreshold, mClearCacheObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800256 Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 mClearingCache = false;
258 mClearSucceeded = false;
259 }
260 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700261
Dianne Hackborn532ea262017-03-17 17:50:55 -0700262 void forceMemory(int opts, int seq) {
263 if ((opts&OPTION_UPDATES_STOPPED) == 0) {
264 if (mUpdatesStopped) {
265 mUpdatesStopped = false;
266 checkMemory(true);
267 }
268 } else {
269 mUpdatesStopped = true;
270 final boolean forceLow = (opts&OPTION_STORAGE_LOW) != 0;
271 if (mLowMemFlag != forceLow || (opts&OPTION_FORCE_UPDATE) != 0) {
272 mLowMemFlag = forceLow;
273 if (forceLow) {
274 sendNotification(seq);
275 } else {
276 cancelNotification(seq);
277 }
278 }
279 }
280 }
281
Adam Lesinski182f73f2013-12-05 16:48:06 -0800282 void checkMemory(boolean checkCache) {
Dianne Hackborn532ea262017-03-17 17:50:55 -0700283 if (mUpdatesStopped) {
284 return;
285 }
286
Doug Zongker3161795b2009-10-07 15:14:03 -0700287 //if the thread that was started to clear cache is still running do nothing till its
288 //finished clearing cache. Ideally this flag could be modified by clearCache
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 // and should be accessed via a lock but even if it does this test will fail now and
290 //hopefully the next time this flag will be set to the correct value.
Dianne Hackborn532ea262017-03-17 17:50:55 -0700291 if (mClearingCache) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800292 if(localLOGV) Slog.i(TAG, "Thread already running just skip");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 //make sure the thread is not hung for too long
294 long diffTime = System.currentTimeMillis() - mThreadStartTime;
295 if(diffTime > (10*60*1000)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800296 Slog.w(TAG, "Thread that clears cache file seems to run for ever");
Doug Zongker3161795b2009-10-07 15:14:03 -0700297 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 } else {
299 restatDataDir();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800300 if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem);
Doug Zongker3161795b2009-10-07 15:14:03 -0700301
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 //post intent to NotificationManager to display icon if necessary
Jake Hambybb371632010-08-23 18:16:48 -0700303 if (mFreeMem < mMemLowThreshold) {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700304 if (checkCache) {
305 // We are allowed to clear cache files at this point to
306 // try to get down below the limit, because this is not
307 // the initial call after a cache clear has been attempted.
308 // In this case we will try a cache clear if our free
309 // space has gone below the cache clear limit.
310 if (mFreeMem < mMemCacheStartTrimThreshold) {
311 // We only clear the cache if the free storage has changed
312 // a significant amount since the last time.
313 if ((mFreeMemAfterLastCacheClear-mFreeMem)
314 >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
315 // See if clearing cache helps
316 // Note that clearing cache is asynchronous and so we do a
317 // memory check again once the cache has been cleared.
318 mThreadStartTime = System.currentTimeMillis();
319 mClearSucceeded = false;
320 clearCache();
321 }
322 }
323 } else {
324 // This is a call from after clearing the cache. Note
325 // the amount of free storage at this point.
326 mFreeMemAfterLastCacheClear = mFreeMem;
327 if (!mLowMemFlag) {
328 // We tried to clear the cache, but that didn't get us
329 // below the low storage limit. Tell the user.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800330 Slog.i(TAG, "Running low on memory. Sending notification");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700331 sendNotification(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 mLowMemFlag = true;
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700333 } else {
334 if (localLOGV) Slog.v(TAG, "Running low on memory " +
335 "notification already sent. do nothing");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 }
338 } else {
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700339 mFreeMemAfterLastCacheClear = mFreeMem;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 if (mLowMemFlag) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800341 Slog.i(TAG, "Memory available. Cancelling notification");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700342 cancelNotification(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 mLowMemFlag = false;
344 }
345 }
Richard Uhlerd42fe852016-08-12 13:51:51 -0700346 if (!mLowMemFlag && !mIsBootImageOnDisk && mFreeMem < BOOT_IMAGE_STORAGE_REQUIREMENT) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700347 Slog.i(TAG, "No boot image on disk due to lack of space. Sending notification");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700348 sendNotification(0);
Richard Uhlerd42fe852016-08-12 13:51:51 -0700349 mLowMemFlag = true;
Brian Carlstroma39871e2014-09-29 13:44:04 -0700350 }
Jake Hambybb371632010-08-23 18:16:48 -0700351 if (mFreeMem < mMemFullThreshold) {
352 if (!mMemFullFlag) {
353 sendFullNotification();
354 mMemFullFlag = true;
355 }
356 } else {
357 if (mMemFullFlag) {
358 cancelFullNotification();
359 mMemFullFlag = false;
360 }
361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800363 if(localLOGV) Slog.i(TAG, "Posting Message again");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 //keep posting messages to itself periodically
365 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
366 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700367
Adam Lesinski182f73f2013-12-05 16:48:06 -0800368 void postCheckMemoryMsg(boolean clearCache, long delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 // Remove queued messages
370 mHandler.removeMessages(DEVICE_MEMORY_WHAT);
371 mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
372 clearCache ?_TRUE : _FALSE, 0),
373 delay);
374 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700375
Jeff Brownb880d882014-02-10 19:47:07 -0800376 public DeviceStorageMonitorService(Context context) {
377 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 mLastReportedFreeMemTime = 0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800379 mResolver = context.getContentResolver();
Brian Carlstroma39871e2014-09-29 13:44:04 -0700380 mIsBootImageOnDisk = isBootImageOnDisk();
Tobias Thierer0ad48dc2016-08-02 16:41:11 +0100381 // If these constructors throw IllegalArgumentException, something
382 // is so seriously wrong that we just let the Exception propagate.
Jeff Sharkeybe722152013-02-15 16:56:38 -0800383 mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
384 mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
385 mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 //initialize total storage on device
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700387 mTotalMemory = (long)mDataFileStats.getBlockCount() *
388 mDataFileStats.getBlockSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
Christopher Tate42a386b2016-11-07 12:21:21 -0800390 mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
Chad Brubaker6e392ed2017-03-22 14:36:37 -0700391 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
392 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
Christopher Tate42a386b2016-11-07 12:21:21 -0800394 mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
Chad Brubaker6e392ed2017-03-22 14:36:37 -0700395 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
396 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
Jake Hambybb371632010-08-23 18:16:48 -0700397 mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
398 mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
399 mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
400 mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800401 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800402
Brian Carlstroma39871e2014-09-29 13:44:04 -0700403 private static boolean isBootImageOnDisk() {
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -0800404 for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700405 if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
406 return false;
407 }
408 }
409 return true;
410 }
411
Jeff Brownb880d882014-02-10 19:47:07 -0800412 /**
413 * Initializes the disk space threshold value and posts an empty message to
414 * kickstart the process.
415 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800416 @Override
417 public void onStart() {
Jake Hambybb371632010-08-23 18:16:48 -0700418 // cache storage thresholds
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800419 Context context = getContext();
420 final StorageManager sm = StorageManager.from(context);
Jeff Sharkeybe722152013-02-15 16:56:38 -0800421 mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
422 mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
423
Dianne Hackborn197a0c82012-07-12 14:46:04 -0700424 mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
425 mMemCacheTrimToThreshold = mMemLowThreshold
426 + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
427 mFreeMemAfterLastCacheClear = mTotalMemory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 checkMemory(true);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700429
430 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
431 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800432
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800433 // Ensure that the notification channel is set up
434 NotificationManager notificationMgr =
435 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
436 PackageManager packageManager = context.getPackageManager();
437 boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
438
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500439 if (isTv) {
440 notificationMgr.createNotificationChannel(new NotificationChannel(
441 TV_NOTIFICATION_CHANNEL_ID,
442 context.getString(
443 com.android.internal.R.string.device_storage_monitor_notification_channel),
444 NotificationManager.IMPORTANCE_HIGH));
445 }
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800446
Adam Lesinski182f73f2013-12-05 16:48:06 -0800447 publishBinderService(SERVICE, mRemoteService);
448 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
449 }
450
451 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
452 @Override
453 public void checkMemory() {
454 // force an early check
455 postCheckMemoryMsg(true, 0);
456 }
457
458 @Override
459 public boolean isMemoryLow() {
Richard Uhlerd42fe852016-08-12 13:51:51 -0700460 return mLowMemFlag;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800461 }
462
463 @Override
464 public long getMemoryLowThreshold() {
465 return mMemLowThreshold;
466 }
467 };
468
Dianne Hackborn532ea262017-03-17 17:50:55 -0700469 private final Binder mRemoteService = new Binder() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800470 @Override
471 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600472 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700473 dumpImpl(fd, pw, args);
474 }
475
476 @Override
477 public void onShellCommand(FileDescriptor in, FileDescriptor out,
478 FileDescriptor err, String[] args, ShellCallback callback,
479 ResultReceiver resultReceiver) {
480 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800481 }
482 };
483
Dianne Hackborn532ea262017-03-17 17:50:55 -0700484 class Shell extends ShellCommand {
485 @Override
486 public int onCommand(String cmd) {
487 return onShellCommand(this, cmd);
488 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800489
Dianne Hackborn532ea262017-03-17 17:50:55 -0700490 @Override
491 public void onHelp() {
492 PrintWriter pw = getOutPrintWriter();
493 dumpHelp(pw);
494 }
495 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800496
Dianne Hackborn532ea262017-03-17 17:50:55 -0700497 static final int OPTION_FORCE_UPDATE = 1<<0;
498 static final int OPTION_UPDATES_STOPPED = 1<<1;
499 static final int OPTION_STORAGE_LOW = 1<<2;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800500
Dianne Hackborn532ea262017-03-17 17:50:55 -0700501 int parseOptions(Shell shell) {
502 String opt;
503 int opts = 0;
504 while ((opt = shell.getNextOption()) != null) {
505 if ("-f".equals(opt)) {
506 opts |= OPTION_FORCE_UPDATE;
507 }
508 }
509 return opts;
510 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800511
Dianne Hackborn532ea262017-03-17 17:50:55 -0700512 int onShellCommand(Shell shell, String cmd) {
513 if (cmd == null) {
514 return shell.handleDefaultCommands(cmd);
515 }
516 PrintWriter pw = shell.getOutPrintWriter();
517 switch (cmd) {
518 case "force-low": {
519 int opts = parseOptions(shell);
520 getContext().enforceCallingOrSelfPermission(
521 android.Manifest.permission.DEVICE_POWER, null);
522 int seq = mSeq.incrementAndGet();
523 mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
524 opts | OPTION_UPDATES_STOPPED | OPTION_STORAGE_LOW, seq));
525 if ((opts & OPTION_FORCE_UPDATE) != 0) {
526 pw.println(seq);
527 }
528 } break;
529 case "force-not-low": {
530 int opts = parseOptions(shell);
531 getContext().enforceCallingOrSelfPermission(
532 android.Manifest.permission.DEVICE_POWER, null);
533 int seq = mSeq.incrementAndGet();
534 mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
535 opts | OPTION_UPDATES_STOPPED, seq));
536 if ((opts & OPTION_FORCE_UPDATE) != 0) {
537 pw.println(seq);
538 }
539 } break;
540 case "reset": {
541 int opts = parseOptions(shell);
542 getContext().enforceCallingOrSelfPermission(
543 android.Manifest.permission.DEVICE_POWER, null);
544 int seq = mSeq.incrementAndGet();
545 mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
546 opts, seq));
547 if ((opts & OPTION_FORCE_UPDATE) != 0) {
548 pw.println(seq);
549 }
550 } break;
551 default:
552 return shell.handleDefaultCommands(cmd);
553 }
554 return 0;
555 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800556
Dianne Hackborn532ea262017-03-17 17:50:55 -0700557 static void dumpHelp(PrintWriter pw) {
558 pw.println("Device storage monitor service (devicestoragemonitor) commands:");
559 pw.println(" help");
560 pw.println(" Print this help text.");
561 pw.println(" force-low [-f]");
562 pw.println(" Force storage to be low, freezing storage state.");
563 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
564 pw.println(" force-not-low [-f]");
565 pw.println(" Force storage to not be low, freezing storage state.");
566 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
567 pw.println(" reset [-f]");
568 pw.println(" Unfreeze storage state, returning to current real values.");
569 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
570 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800571
Dianne Hackborn532ea262017-03-17 17:50:55 -0700572 void dumpImpl(FileDescriptor fd, PrintWriter pw, String[] args) {
573 if (args == null || args.length == 0 || "-a".equals(args[0])) {
574 final Context context = getContext();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800575
Dianne Hackborn532ea262017-03-17 17:50:55 -0700576 pw.println("Current DeviceStorageMonitor state:");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800577
Dianne Hackborn532ea262017-03-17 17:50:55 -0700578 pw.print(" mFreeMem=");
579 pw.print(Formatter.formatFileSize(context, mFreeMem));
580 pw.print(" mTotalMemory=");
581 pw.println(Formatter.formatFileSize(context, mTotalMemory));
582
583 pw.print(" mFreeMemAfterLastCacheClear=");
584 pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear));
585
586 pw.print(" mLastReportedFreeMem=");
587 pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem));
588 pw.print(" mLastReportedFreeMemTime=");
589 TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
590 pw.println();
591
592 if (mUpdatesStopped) {
593 pw.print(" mUpdatesStopped=");
594 pw.print(mUpdatesStopped);
595 pw.print(" mSeq=");
596 pw.println(mSeq.get());
597 } else {
598 pw.print(" mClearSucceeded=");
599 pw.print(mClearSucceeded);
600 pw.print(" mClearingCache=");
601 pw.println(mClearingCache);
602 }
603
604 pw.print(" mLowMemFlag=");
605 pw.print(mLowMemFlag);
606 pw.print(" mMemFullFlag=");
607 pw.println(mMemFullFlag);
608
609 pw.print(" mMemLowThreshold=");
610 pw.print(Formatter.formatFileSize(context, mMemLowThreshold));
611 pw.print(" mMemFullThreshold=");
612 pw.println(Formatter.formatFileSize(context, mMemFullThreshold));
613
614 pw.print(" mMemCacheStartTrimThreshold=");
615 pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold));
616 pw.print(" mMemCacheTrimToThreshold=");
617 pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold));
618
619 pw.print(" mIsBootImageOnDisk="); pw.println(mIsBootImageOnDisk);
620 } else {
621 Shell shell = new Shell();
622 shell.exec(mRemoteService, null, fd, null, args, null, new ResultReceiver(null));
623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700625
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 /**
627 * This method sends a notification to NotificationManager to display
628 * an error dialog indicating low disk space and launch the Installer
629 * application
630 */
Dianne Hackborn532ea262017-03-17 17:50:55 -0700631 private void sendNotification(int seq) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800632 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800633 if(localLOGV) Slog.i(TAG, "Sending low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 //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 -0800635 EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 // Pack up the values and broadcast them to everyone
Daniel Nishi690346b2016-06-17 10:21:48 -0700637 Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 lowMemIntent.putExtra("memory", mFreeMem);
639 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800640 NotificationManager notificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800641 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 Context.NOTIFICATION_SERVICE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800643 CharSequence title = context.getText(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 com.android.internal.R.string.low_internal_storage_view_title);
Brian Carlstroma39871e2014-09-29 13:44:04 -0700645 CharSequence details = context.getText(mIsBootImageOnDisk
646 ? com.android.internal.R.string.low_internal_storage_view_text
647 : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800648 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700649 null, UserHandle.CURRENT);
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500650 Notification notification =
651 new Notification.Builder(context, SystemNotificationChannels.ALERTS)
652 .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
653 .setTicker(title)
654 .setColor(context.getColor(
655 com.android.internal.R.color.system_notification_accent_color))
656 .setContentTitle(title)
657 .setContentText(details)
658 .setContentIntent(intent)
659 .setStyle(new Notification.BigTextStyle()
660 .bigText(details))
661 .setVisibility(Notification.VISIBILITY_PUBLIC)
662 .setCategory(Notification.CATEGORY_SYSTEM)
663 .extend(new Notification.TvExtender()
664 .setChannel(TV_NOTIFICATION_CHANNEL_ID))
665 .build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 notification.flags |= Notification.FLAG_NO_CLEAR;
Chris Wren282cfef2017-03-27 15:01:44 -0400667 notificationMgr.notifyAsUser(null, SystemMessage.NOTE_LOW_STORAGE, notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700668 UserHandle.ALL);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700669 Intent broadcast = new Intent(mStorageLowIntent);
670 if (seq != 0) {
671 broadcast.putExtra(EXTRA_SEQUENCE, seq);
672 }
673 context.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 }
675
676 /**
677 * Cancels low storage notification and sends OK intent.
678 */
Dianne Hackborn532ea262017-03-17 17:50:55 -0700679 private void cancelNotification(int seq) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800680 final Context context = getContext();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800681 if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 NotificationManager mNotificationMgr =
Adam Lesinski182f73f2013-12-05 16:48:06 -0800683 (NotificationManager)context.getSystemService(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 Context.NOTIFICATION_SERVICE);
685 //cancel notification since memory has been freed
Chris Wren282cfef2017-03-27 15:01:44 -0400686 mNotificationMgr.cancelAsUser(null, SystemMessage.NOTE_LOW_STORAGE, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687
Adam Lesinski182f73f2013-12-05 16:48:06 -0800688 context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700689 Intent broadcast = new Intent(mStorageOkIntent);
690 if (seq != 0) {
691 broadcast.putExtra(EXTRA_SEQUENCE, seq);
692 }
693 context.sendBroadcastAsUser(broadcast, UserHandle.ALL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700695
Jake Hambybb371632010-08-23 18:16:48 -0700696 /**
697 * Send a notification when storage is full.
698 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800699 private void sendFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700700 if(localLOGV) Slog.i(TAG, "Sending memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800701 getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700702 }
703
704 /**
705 * Cancels memory full notification and sends "not full" intent.
706 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800707 private void cancelFullNotification() {
Jake Hambybb371632010-08-23 18:16:48 -0700708 if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800709 getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
710 getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
Jake Hambybb371632010-08-23 18:16:48 -0700711 }
712
Adam Lesinski182f73f2013-12-05 16:48:06 -0800713 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700714 public CacheFileDeletedObserver() {
715 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
716 }
717
718 @Override
719 public void onEvent(int event, String path) {
720 EventLogTags.writeCacheFileDeleted(path);
721 }
722 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723}