blob: f7cc4432f9bccac72e5df3bdbf430f790c88413f [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
Jeff Sharkeyddff8072017-05-26 13:10:46 -060019import android.annotation.WorkerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.Notification;
Jeff Sharkeyddff8072017-05-26 13:10:46 -060021import android.app.NotificationChannel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.app.NotificationManager;
23import android.app.PendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
25import android.content.Intent;
Dianne Hackborn197a0c82012-07-12 14:46:04 -070026import android.content.pm.PackageManager;
Jeff Sharkeyddff8072017-05-26 13:10:46 -060027import android.net.TrafficStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.Binder;
Dianne Hackbornf882efa2012-04-11 16:04:12 -070029import android.os.Environment;
Jeff Sharkey4b496572012-04-19 14:17:03 -070030import android.os.FileObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.Handler;
Jeff Sharkeya1e79942017-08-02 14:24:35 -060032import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.os.Message;
Dianne Hackborn532ea262017-03-17 17:50:55 -070034import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.os.ServiceManager;
Dianne Hackborn532ea262017-03-17 17:50:55 -070036import android.os.ShellCallback;
37import android.os.ShellCommand;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070038import android.os.UserHandle;
Jeff Sharkeybe722152013-02-15 16:56:38 -080039import android.os.storage.StorageManager;
Jeff Sharkeyddff8072017-05-26 13:10:46 -060040import android.os.storage.VolumeInfo;
41import android.text.format.DateUtils;
42import android.util.ArrayMap;
Jeff Sharkey9f2dc052018-01-07 16:47:31 -070043import android.util.DataUnit;
Joe Onorato8a9b2202010-02-26 18:56:32 -080044import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
Jeff Sharkeyddff8072017-05-26 13:10:46 -060046import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
47import com.android.internal.notification.SystemNotificationChannels;
48import com.android.internal.util.DumpUtils;
49import com.android.internal.util.IndentingPrintWriter;
50import com.android.server.EventLogTags;
51import com.android.server.IoThread;
52import com.android.server.SystemService;
53import com.android.server.pm.InstructionSets;
54import com.android.server.pm.PackageManagerService;
Jeff Sharkeybe722152013-02-15 16:56:38 -080055
Brian Carlstroma39871e2014-09-29 13:44:04 -070056import dalvik.system.VMRuntime;
57
Jeff Sharkeyddff8072017-05-26 13:10:46 -060058import java.io.File;
59import java.io.FileDescriptor;
60import java.io.IOException;
61import java.io.PrintWriter;
62import java.util.Objects;
63import java.util.UUID;
64import java.util.concurrent.atomic.AtomicInteger;
65
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066/**
Jeff Sharkeyddff8072017-05-26 13:10:46 -060067 * Service that monitors and maintains free space on storage volumes.
68 * <p>
69 * As the free space on a volume nears the threshold defined by
70 * {@link StorageManager#getStorageLowBytes(File)}, this service will clear out
71 * cached data to keep the disk from entering this low state.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080073public class DeviceStorageMonitorService extends SystemService {
Jeff Sharkeyddff8072017-05-26 13:10:46 -060074 private static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080075
Dianne Hackborn532ea262017-03-17 17:50:55 -070076 /**
77 * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
78 * Current int sequence number of the update.
79 */
80 public static final String EXTRA_SEQUENCE = "seq";
81
Jeff Sharkeyddff8072017-05-26 13:10:46 -060082 private static final int MSG_CHECK = 1;
Jeff Sharkey529f91f2015-04-18 20:23:13 -070083
Jeff Sharkey9f2dc052018-01-07 16:47:31 -070084 private static final long DEFAULT_LOG_DELTA_BYTES = DataUnit.MEBIBYTES.toBytes(64);
Jeff Sharkeyddff8072017-05-26 13:10:46 -060085 private static final long DEFAULT_CHECK_INTERVAL = DateUtils.MINUTE_IN_MILLIS;
Jeff Sharkeybe722152013-02-15 16:56:38 -080086
Richard Uhlerd42fe852016-08-12 13:51:51 -070087 // com.android.internal.R.string.low_internal_storage_view_text_no_boot
88 // hard codes 250MB in the message as the storage space required for the
89 // boot image.
Jeff Sharkey9f2dc052018-01-07 16:47:31 -070090 private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = DataUnit.MEBIBYTES.toBytes(250);
Richard Uhlerd42fe852016-08-12 13:51:51 -070091
Jeff Sharkeyddff8072017-05-26 13:10:46 -060092 private NotificationManager mNotifManager;
Jeff Sharkeybe722152013-02-15 16:56:38 -080093
Jeff Sharkeyddff8072017-05-26 13:10:46 -060094 /** Sequence number used for testing */
95 private final AtomicInteger mSeq = new AtomicInteger(1);
96 /** Forced level used for testing */
97 private volatile int mForceLevel = State.LEVEL_UNKNOWN;
Jeff Sharkeybe722152013-02-15 16:56:38 -080098
Jeff Sharkeyddff8072017-05-26 13:10:46 -060099 /** Map from storage volume UUID to internal state */
100 private final ArrayMap<UUID, State> mStates = new ArrayMap<>();
101
102 /**
103 * State for a specific storage volume, including the current "level" that
104 * we've alerted the user and apps about.
105 */
106 private static class State {
107 private static final int LEVEL_UNKNOWN = -1;
108 private static final int LEVEL_NORMAL = 0;
109 private static final int LEVEL_LOW = 1;
110 private static final int LEVEL_FULL = 2;
111
112 /** Last "level" that we alerted about */
113 public int level = LEVEL_NORMAL;
114 /** Last {@link File#getUsableSpace()} that we logged about */
115 public long lastUsableBytes = Long.MAX_VALUE;
116
117 /**
118 * Test if the given level transition is "entering" a specific level.
119 * <p>
120 * As an example, a transition from {@link #LEVEL_NORMAL} to
121 * {@link #LEVEL_FULL} is considered to "enter" both {@link #LEVEL_LOW}
122 * and {@link #LEVEL_FULL}.
123 */
124 private static boolean isEntering(int level, int oldLevel, int newLevel) {
125 return newLevel >= level && (oldLevel < level || oldLevel == LEVEL_UNKNOWN);
126 }
127
128 /**
129 * Test if the given level transition is "leaving" a specific level.
130 * <p>
131 * As an example, a transition from {@link #LEVEL_FULL} to
132 * {@link #LEVEL_NORMAL} is considered to "leave" both
133 * {@link #LEVEL_FULL} and {@link #LEVEL_LOW}.
134 */
135 private static boolean isLeaving(int level, int oldLevel, int newLevel) {
136 return newLevel < level && (oldLevel >= level || oldLevel == LEVEL_UNKNOWN);
137 }
138
139 private static String levelToString(int level) {
140 switch (level) {
141 case State.LEVEL_UNKNOWN: return "UNKNOWN";
142 case State.LEVEL_NORMAL: return "NORMAL";
143 case State.LEVEL_LOW: return "LOW";
144 case State.LEVEL_FULL: return "FULL";
145 default: return Integer.toString(level);
146 }
147 }
148 }
149
Adam Lesinski182f73f2013-12-05 16:48:06 -0800150 private CacheFileDeletedObserver mCacheFileDeletedObserver;
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
Jeff Sharkeya1e79942017-08-02 14:24:35 -0600159 private final HandlerThread mHandlerThread;
160 private final Handler mHandler;
Doug Zongker3161795b2009-10-07 15:14:03 -0700161
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600162 private State findOrCreateState(UUID uuid) {
163 State state = mStates.get(uuid);
164 if (state == null) {
165 state = new State();
166 mStates.put(uuid, state);
Doug Zongker3161795b2009-10-07 15:14:03 -0700167 }
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600168 return state;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700170
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600171 /**
172 * Core logic that checks the storage state of every mounted private volume.
173 * Since this can do heavy I/O, callers should invoke indirectly using
174 * {@link #MSG_CHECK}.
175 */
176 @WorkerThread
177 private void check() {
178 final StorageManager storage = getContext().getSystemService(StorageManager.class);
179 final int seq = mSeq.get();
Doug Zongker3161795b2009-10-07 15:14:03 -0700180
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600181 // Check every mounted private volume to see if they're low on space
182 for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
183 final File file = vol.getPath();
184 final long fullBytes = storage.getStorageFullBytes(file);
185 final long lowBytes = storage.getStorageLowBytes(file);
Doug Zongker3161795b2009-10-07 15:14:03 -0700186
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600187 // Automatically trim cached data when nearing the low threshold;
188 // when it's within 150% of the threshold, we try trimming usage
189 // back to 200% of the threshold.
190 if (file.getUsableSpace() < (lowBytes * 3) / 2) {
191 final PackageManagerService pms = (PackageManagerService) ServiceManager
192 .getService("package");
193 try {
194 pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
195 } catch (IOException e) {
196 Slog.w(TAG, e);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700197 }
198 }
Dianne Hackborn532ea262017-03-17 17:50:55 -0700199
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600200 // Send relevant broadcasts and show notifications based on any
201 // recently noticed state transitions.
202 final UUID uuid = StorageManager.convert(vol.getFsUuid());
203 final State state = findOrCreateState(uuid);
204 final long totalBytes = file.getTotalSpace();
205 final long usableBytes = file.getUsableSpace();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700206
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600207 int oldLevel = state.level;
208 int newLevel;
209 if (mForceLevel != State.LEVEL_UNKNOWN) {
210 // When in testing mode, use unknown old level to force sending
211 // of any relevant broadcasts.
212 oldLevel = State.LEVEL_UNKNOWN;
213 newLevel = mForceLevel;
214 } else if (usableBytes <= fullBytes) {
215 newLevel = State.LEVEL_FULL;
216 } else if (usableBytes <= lowBytes) {
217 newLevel = State.LEVEL_LOW;
218 } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
219 && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
220 newLevel = State.LEVEL_LOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 } else {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600222 newLevel = State.LEVEL_NORMAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700224
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600225 // Log whenever we notice drastic storage changes
226 if ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES)
227 || oldLevel != newLevel) {
228 EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel,
229 usableBytes, totalBytes);
230 state.lastUsableBytes = usableBytes;
231 }
232
233 updateNotifications(vol, oldLevel, newLevel);
234 updateBroadcasts(vol, oldLevel, newLevel, seq);
235
236 state.level = newLevel;
237 }
238
239 // Loop around to check again in future; we don't remove messages since
240 // there might be an immediate request pending.
241 if (!mHandler.hasMessages(MSG_CHECK)) {
242 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK),
243 DEFAULT_CHECK_INTERVAL);
244 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700246
Jeff Brownb880d882014-02-10 19:47:07 -0800247 public DeviceStorageMonitorService(Context context) {
248 super(context);
Jeff Sharkeya1e79942017-08-02 14:24:35 -0600249
250 mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
251 mHandlerThread.start();
252
253 mHandler = new Handler(mHandlerThread.getLooper()) {
254 @Override
255 public void handleMessage(Message msg) {
256 switch (msg.what) {
257 case MSG_CHECK:
258 check();
259 return;
260 }
261 }
262 };
Adam Lesinski182f73f2013-12-05 16:48:06 -0800263 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800264
Brian Carlstroma39871e2014-09-29 13:44:04 -0700265 private static boolean isBootImageOnDisk() {
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -0800266 for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700267 if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
268 return false;
269 }
270 }
271 return true;
272 }
273
Adam Lesinski182f73f2013-12-05 16:48:06 -0800274 @Override
275 public void onStart() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600276 final Context context = getContext();
277 mNotifManager = context.getSystemService(NotificationManager.class);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700278
279 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
280 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800281
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800282 // Ensure that the notification channel is set up
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800283 PackageManager packageManager = context.getPackageManager();
284 boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
285
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500286 if (isTv) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600287 mNotifManager.createNotificationChannel(new NotificationChannel(
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500288 TV_NOTIFICATION_CHANNEL_ID,
289 context.getString(
290 com.android.internal.R.string.device_storage_monitor_notification_channel),
291 NotificationManager.IMPORTANCE_HIGH));
292 }
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800293
Adam Lesinski182f73f2013-12-05 16:48:06 -0800294 publishBinderService(SERVICE, mRemoteService);
295 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600296
297 // Kick off pass to examine storage state
298 mHandler.removeMessages(MSG_CHECK);
299 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800300 }
301
302 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
303 @Override
304 public void checkMemory() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600305 // Kick off pass to examine storage state
306 mHandler.removeMessages(MSG_CHECK);
307 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800308 }
309
310 @Override
311 public boolean isMemoryLow() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600312 return Environment.getDataDirectory().getUsableSpace() < getMemoryLowThreshold();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800313 }
314
315 @Override
316 public long getMemoryLowThreshold() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600317 return getContext().getSystemService(StorageManager.class)
318 .getStorageLowBytes(Environment.getDataDirectory());
Adam Lesinski182f73f2013-12-05 16:48:06 -0800319 }
320 };
321
Dianne Hackborn532ea262017-03-17 17:50:55 -0700322 private final Binder mRemoteService = new Binder() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800323 @Override
324 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600325 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700326 dumpImpl(fd, pw, args);
327 }
328
329 @Override
330 public void onShellCommand(FileDescriptor in, FileDescriptor out,
331 FileDescriptor err, String[] args, ShellCallback callback,
332 ResultReceiver resultReceiver) {
333 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800334 }
335 };
336
Dianne Hackborn532ea262017-03-17 17:50:55 -0700337 class Shell extends ShellCommand {
338 @Override
339 public int onCommand(String cmd) {
340 return onShellCommand(this, cmd);
341 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800342
Dianne Hackborn532ea262017-03-17 17:50:55 -0700343 @Override
344 public void onHelp() {
345 PrintWriter pw = getOutPrintWriter();
346 dumpHelp(pw);
347 }
348 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800349
Dianne Hackborn532ea262017-03-17 17:50:55 -0700350 static final int OPTION_FORCE_UPDATE = 1<<0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800351
Dianne Hackborn532ea262017-03-17 17:50:55 -0700352 int parseOptions(Shell shell) {
353 String opt;
354 int opts = 0;
355 while ((opt = shell.getNextOption()) != null) {
356 if ("-f".equals(opt)) {
357 opts |= OPTION_FORCE_UPDATE;
358 }
359 }
360 return opts;
361 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800362
Dianne Hackborn532ea262017-03-17 17:50:55 -0700363 int onShellCommand(Shell shell, String cmd) {
364 if (cmd == null) {
365 return shell.handleDefaultCommands(cmd);
366 }
367 PrintWriter pw = shell.getOutPrintWriter();
368 switch (cmd) {
369 case "force-low": {
370 int opts = parseOptions(shell);
371 getContext().enforceCallingOrSelfPermission(
372 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600373 mForceLevel = State.LEVEL_LOW;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700374 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700375 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600376 mHandler.removeMessages(MSG_CHECK);
377 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700378 pw.println(seq);
379 }
380 } break;
381 case "force-not-low": {
382 int opts = parseOptions(shell);
383 getContext().enforceCallingOrSelfPermission(
384 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600385 mForceLevel = State.LEVEL_NORMAL;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700386 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700387 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600388 mHandler.removeMessages(MSG_CHECK);
389 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700390 pw.println(seq);
391 }
392 } break;
393 case "reset": {
394 int opts = parseOptions(shell);
395 getContext().enforceCallingOrSelfPermission(
396 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600397 mForceLevel = State.LEVEL_UNKNOWN;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700398 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700399 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600400 mHandler.removeMessages(MSG_CHECK);
401 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700402 pw.println(seq);
403 }
404 } break;
405 default:
406 return shell.handleDefaultCommands(cmd);
407 }
408 return 0;
409 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800410
Dianne Hackborn532ea262017-03-17 17:50:55 -0700411 static void dumpHelp(PrintWriter pw) {
412 pw.println("Device storage monitor service (devicestoragemonitor) commands:");
413 pw.println(" help");
414 pw.println(" Print this help text.");
415 pw.println(" force-low [-f]");
416 pw.println(" Force storage to be low, freezing storage state.");
417 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
418 pw.println(" force-not-low [-f]");
419 pw.println(" Force storage to not be low, freezing storage state.");
420 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
421 pw.println(" reset [-f]");
422 pw.println(" Unfreeze storage state, returning to current real values.");
423 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
424 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800425
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600426 void dumpImpl(FileDescriptor fd, PrintWriter _pw, String[] args) {
427 final IndentingPrintWriter pw = new IndentingPrintWriter(_pw, " ");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700428 if (args == null || args.length == 0 || "-a".equals(args[0])) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600429 pw.println("Known volumes:");
430 pw.increaseIndent();
431 for (int i = 0; i < mStates.size(); i++) {
432 final UUID uuid = mStates.keyAt(i);
433 final State state = mStates.valueAt(i);
434 if (StorageManager.UUID_DEFAULT.equals(uuid)) {
435 pw.println("Default:");
436 } else {
437 pw.println(uuid + ":");
438 }
439 pw.increaseIndent();
440 pw.printPair("level", State.levelToString(state.level));
441 pw.printPair("lastUsableBytes", state.lastUsableBytes);
442 pw.println();
443 pw.decreaseIndent();
444 }
445 pw.decreaseIndent();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700446 pw.println();
447
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600448 pw.printPair("mSeq", mSeq.get());
449 pw.printPair("mForceState", State.levelToString(mForceLevel));
450 pw.println();
451 pw.println();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700452
Dianne Hackborn532ea262017-03-17 17:50:55 -0700453 } else {
454 Shell shell = new Shell();
455 shell.exec(mRemoteService, null, fd, null, args, null, new ResultReceiver(null));
456 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700458
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600459 private void updateNotifications(VolumeInfo vol, int oldLevel, int newLevel) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800460 final Context context = getContext();
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600461 final UUID uuid = StorageManager.convert(vol.getFsUuid());
462
463 if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
464 Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
465 lowMemIntent.putExtra(StorageManager.EXTRA_UUID, uuid);
466 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
467
468 final CharSequence title = context.getText(
469 com.android.internal.R.string.low_internal_storage_view_title);
470
471 final CharSequence details;
472 if (StorageManager.UUID_DEFAULT.equals(uuid)) {
473 details = context.getText(isBootImageOnDisk()
474 ? com.android.internal.R.string.low_internal_storage_view_text
475 : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
476 } else {
477 details = context.getText(
478 com.android.internal.R.string.low_internal_storage_view_text);
479 }
480
481 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
482 null, UserHandle.CURRENT);
483 Notification notification =
484 new Notification.Builder(context, SystemNotificationChannels.ALERTS)
485 .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
486 .setTicker(title)
487 .setColor(context.getColor(
488 com.android.internal.R.color.system_notification_accent_color))
489 .setContentTitle(title)
490 .setContentText(details)
491 .setContentIntent(intent)
492 .setStyle(new Notification.BigTextStyle()
493 .bigText(details))
494 .setVisibility(Notification.VISIBILITY_PUBLIC)
495 .setCategory(Notification.CATEGORY_SYSTEM)
496 .extend(new Notification.TvExtender()
497 .setChannelId(TV_NOTIFICATION_CHANNEL_ID))
498 .build();
499 notification.flags |= Notification.FLAG_NO_CLEAR;
500 mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
501 notification, UserHandle.ALL);
502 } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
503 mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
504 UserHandle.ALL);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 }
507
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600508 private void updateBroadcasts(VolumeInfo vol, int oldLevel, int newLevel, int seq) {
509 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, vol.getFsUuid())) {
510 // We don't currently send broadcasts for secondary volumes
511 return;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700512 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700513
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600514 final Intent lowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW)
515 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
516 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
517 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
518 .putExtra(EXTRA_SEQUENCE, seq);
519 final Intent notLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK)
520 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
521 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
522 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
523 .putExtra(EXTRA_SEQUENCE, seq);
Jake Hambybb371632010-08-23 18:16:48 -0700524
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600525 if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
526 getContext().sendStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
527 } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
528 getContext().removeStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
529 getContext().sendBroadcastAsUser(notLowIntent, UserHandle.ALL);
530 }
531
532 final Intent fullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL)
533 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
534 .putExtra(EXTRA_SEQUENCE, seq);
535 final Intent notFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)
536 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
537 .putExtra(EXTRA_SEQUENCE, seq);
538
539 if (State.isEntering(State.LEVEL_FULL, oldLevel, newLevel)) {
540 getContext().sendStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
541 } else if (State.isLeaving(State.LEVEL_FULL, oldLevel, newLevel)) {
542 getContext().removeStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
543 getContext().sendBroadcastAsUser(notFullIntent, UserHandle.ALL);
544 }
Jake Hambybb371632010-08-23 18:16:48 -0700545 }
546
Adam Lesinski182f73f2013-12-05 16:48:06 -0800547 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700548 public CacheFileDeletedObserver() {
549 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
550 }
551
552 @Override
553 public void onEvent(int event, String path) {
554 EventLogTags.writeCacheFileDeleted(path);
555 }
556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557}