blob: 88b6d870afd76775b5b5599fd94a17408933d2ef [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;
32import android.os.Message;
Dianne Hackborn532ea262017-03-17 17:50:55 -070033import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.ServiceManager;
Dianne Hackborn532ea262017-03-17 17:50:55 -070035import android.os.ShellCallback;
36import android.os.ShellCommand;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070037import android.os.UserHandle;
Jeff Sharkeybe722152013-02-15 16:56:38 -080038import android.os.storage.StorageManager;
Jeff Sharkeyddff8072017-05-26 13:10:46 -060039import android.os.storage.VolumeInfo;
40import android.text.format.DateUtils;
41import android.util.ArrayMap;
Joe Onorato8a9b2202010-02-26 18:56:32 -080042import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
Jeff Sharkeyddff8072017-05-26 13:10:46 -060044import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
45import com.android.internal.notification.SystemNotificationChannels;
46import com.android.internal.util.DumpUtils;
47import com.android.internal.util.IndentingPrintWriter;
48import com.android.server.EventLogTags;
49import com.android.server.IoThread;
50import com.android.server.SystemService;
51import com.android.server.pm.InstructionSets;
52import com.android.server.pm.PackageManagerService;
Jeff Sharkeybe722152013-02-15 16:56:38 -080053
Brian Carlstroma39871e2014-09-29 13:44:04 -070054import dalvik.system.VMRuntime;
55
Jeff Sharkeyddff8072017-05-26 13:10:46 -060056import java.io.File;
57import java.io.FileDescriptor;
58import java.io.IOException;
59import java.io.PrintWriter;
60import java.util.Objects;
61import java.util.UUID;
62import java.util.concurrent.atomic.AtomicInteger;
63
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064/**
Jeff Sharkeyddff8072017-05-26 13:10:46 -060065 * Service that monitors and maintains free space on storage volumes.
66 * <p>
67 * As the free space on a volume nears the threshold defined by
68 * {@link StorageManager#getStorageLowBytes(File)}, this service will clear out
69 * cached data to keep the disk from entering this low state.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080071public class DeviceStorageMonitorService extends SystemService {
Jeff Sharkeyddff8072017-05-26 13:10:46 -060072 private static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080073
Dianne Hackborn532ea262017-03-17 17:50:55 -070074 /**
75 * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
76 * Current int sequence number of the update.
77 */
78 public static final String EXTRA_SEQUENCE = "seq";
79
Jeff Sharkeyddff8072017-05-26 13:10:46 -060080 private static final int MSG_CHECK = 1;
Jeff Sharkey529f91f2015-04-18 20:23:13 -070081
Jeff Sharkeyddff8072017-05-26 13:10:46 -060082 private static final long DEFAULT_LOG_DELTA_BYTES = 64 * TrafficStats.MB_IN_BYTES;
83 private static final long DEFAULT_CHECK_INTERVAL = DateUtils.MINUTE_IN_MILLIS;
Jeff Sharkeybe722152013-02-15 16:56:38 -080084
Richard Uhlerd42fe852016-08-12 13:51:51 -070085 // com.android.internal.R.string.low_internal_storage_view_text_no_boot
86 // hard codes 250MB in the message as the storage space required for the
87 // boot image.
Jeff Sharkeyddff8072017-05-26 13:10:46 -060088 private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * TrafficStats.MB_IN_BYTES;
Richard Uhlerd42fe852016-08-12 13:51:51 -070089
Jeff Sharkeyddff8072017-05-26 13:10:46 -060090 private NotificationManager mNotifManager;
Jeff Sharkeybe722152013-02-15 16:56:38 -080091
Jeff Sharkeyddff8072017-05-26 13:10:46 -060092 /** Sequence number used for testing */
93 private final AtomicInteger mSeq = new AtomicInteger(1);
94 /** Forced level used for testing */
95 private volatile int mForceLevel = State.LEVEL_UNKNOWN;
Jeff Sharkeybe722152013-02-15 16:56:38 -080096
Jeff Sharkeyddff8072017-05-26 13:10:46 -060097 /** Map from storage volume UUID to internal state */
98 private final ArrayMap<UUID, State> mStates = new ArrayMap<>();
99
100 /**
101 * State for a specific storage volume, including the current "level" that
102 * we've alerted the user and apps about.
103 */
104 private static class State {
105 private static final int LEVEL_UNKNOWN = -1;
106 private static final int LEVEL_NORMAL = 0;
107 private static final int LEVEL_LOW = 1;
108 private static final int LEVEL_FULL = 2;
109
110 /** Last "level" that we alerted about */
111 public int level = LEVEL_NORMAL;
112 /** Last {@link File#getUsableSpace()} that we logged about */
113 public long lastUsableBytes = Long.MAX_VALUE;
114
115 /**
116 * Test if the given level transition is "entering" a specific level.
117 * <p>
118 * As an example, a transition from {@link #LEVEL_NORMAL} to
119 * {@link #LEVEL_FULL} is considered to "enter" both {@link #LEVEL_LOW}
120 * and {@link #LEVEL_FULL}.
121 */
122 private static boolean isEntering(int level, int oldLevel, int newLevel) {
123 return newLevel >= level && (oldLevel < level || oldLevel == LEVEL_UNKNOWN);
124 }
125
126 /**
127 * Test if the given level transition is "leaving" a specific level.
128 * <p>
129 * As an example, a transition from {@link #LEVEL_FULL} to
130 * {@link #LEVEL_NORMAL} is considered to "leave" both
131 * {@link #LEVEL_FULL} and {@link #LEVEL_LOW}.
132 */
133 private static boolean isLeaving(int level, int oldLevel, int newLevel) {
134 return newLevel < level && (oldLevel >= level || oldLevel == LEVEL_UNKNOWN);
135 }
136
137 private static String levelToString(int level) {
138 switch (level) {
139 case State.LEVEL_UNKNOWN: return "UNKNOWN";
140 case State.LEVEL_NORMAL: return "NORMAL";
141 case State.LEVEL_LOW: return "LOW";
142 case State.LEVEL_FULL: return "FULL";
143 default: return Integer.toString(level);
144 }
145 }
146 }
147
Adam Lesinski182f73f2013-12-05 16:48:06 -0800148 private CacheFileDeletedObserver mCacheFileDeletedObserver;
Doug Zongker3161795b2009-10-07 15:14:03 -0700149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 /**
151 * This string is used for ServiceManager access to this class.
152 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800153 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700154
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500155 private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800156
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 /**
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600158 * Handler that checks the amount of disk space on the device and sends a
159 * notification if the device runs low on disk space
160 */
161 private final Handler mHandler = new Handler(IoThread.get().getLooper()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 @Override
163 public void handleMessage(Message msg) {
Dianne Hackborn532ea262017-03-17 17:50:55 -0700164 switch (msg.what) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600165 case MSG_CHECK:
166 check();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700167 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 }
170 };
Doug Zongker3161795b2009-10-07 15:14:03 -0700171
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600172 private State findOrCreateState(UUID uuid) {
173 State state = mStates.get(uuid);
174 if (state == null) {
175 state = new State();
176 mStates.put(uuid, state);
Doug Zongker3161795b2009-10-07 15:14:03 -0700177 }
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600178 return state;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700180
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600181 /**
182 * Core logic that checks the storage state of every mounted private volume.
183 * Since this can do heavy I/O, callers should invoke indirectly using
184 * {@link #MSG_CHECK}.
185 */
186 @WorkerThread
187 private void check() {
188 final StorageManager storage = getContext().getSystemService(StorageManager.class);
189 final int seq = mSeq.get();
Doug Zongker3161795b2009-10-07 15:14:03 -0700190
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600191 // Check every mounted private volume to see if they're low on space
192 for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
193 final File file = vol.getPath();
194 final long fullBytes = storage.getStorageFullBytes(file);
195 final long lowBytes = storage.getStorageLowBytes(file);
Doug Zongker3161795b2009-10-07 15:14:03 -0700196
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600197 // Automatically trim cached data when nearing the low threshold;
198 // when it's within 150% of the threshold, we try trimming usage
199 // back to 200% of the threshold.
200 if (file.getUsableSpace() < (lowBytes * 3) / 2) {
201 final PackageManagerService pms = (PackageManagerService) ServiceManager
202 .getService("package");
203 try {
204 pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
205 } catch (IOException e) {
206 Slog.w(TAG, e);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700207 }
208 }
Dianne Hackborn532ea262017-03-17 17:50:55 -0700209
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600210 // Send relevant broadcasts and show notifications based on any
211 // recently noticed state transitions.
212 final UUID uuid = StorageManager.convert(vol.getFsUuid());
213 final State state = findOrCreateState(uuid);
214 final long totalBytes = file.getTotalSpace();
215 final long usableBytes = file.getUsableSpace();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700216
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600217 int oldLevel = state.level;
218 int newLevel;
219 if (mForceLevel != State.LEVEL_UNKNOWN) {
220 // When in testing mode, use unknown old level to force sending
221 // of any relevant broadcasts.
222 oldLevel = State.LEVEL_UNKNOWN;
223 newLevel = mForceLevel;
224 } else if (usableBytes <= fullBytes) {
225 newLevel = State.LEVEL_FULL;
226 } else if (usableBytes <= lowBytes) {
227 newLevel = State.LEVEL_LOW;
228 } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
229 && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
230 newLevel = State.LEVEL_LOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 } else {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600232 newLevel = State.LEVEL_NORMAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700234
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600235 // Log whenever we notice drastic storage changes
236 if ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES)
237 || oldLevel != newLevel) {
238 EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel,
239 usableBytes, totalBytes);
240 state.lastUsableBytes = usableBytes;
241 }
242
243 updateNotifications(vol, oldLevel, newLevel);
244 updateBroadcasts(vol, oldLevel, newLevel, seq);
245
246 state.level = newLevel;
247 }
248
249 // Loop around to check again in future; we don't remove messages since
250 // there might be an immediate request pending.
251 if (!mHandler.hasMessages(MSG_CHECK)) {
252 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK),
253 DEFAULT_CHECK_INTERVAL);
254 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700256
Jeff Brownb880d882014-02-10 19:47:07 -0800257 public DeviceStorageMonitorService(Context context) {
258 super(context);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800259 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800260
Brian Carlstroma39871e2014-09-29 13:44:04 -0700261 private static boolean isBootImageOnDisk() {
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -0800262 for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700263 if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
264 return false;
265 }
266 }
267 return true;
268 }
269
Adam Lesinski182f73f2013-12-05 16:48:06 -0800270 @Override
271 public void onStart() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600272 final Context context = getContext();
273 mNotifManager = context.getSystemService(NotificationManager.class);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700274
275 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
276 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800277
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800278 // Ensure that the notification channel is set up
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800279 PackageManager packageManager = context.getPackageManager();
280 boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
281
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500282 if (isTv) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600283 mNotifManager.createNotificationChannel(new NotificationChannel(
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500284 TV_NOTIFICATION_CHANNEL_ID,
285 context.getString(
286 com.android.internal.R.string.device_storage_monitor_notification_channel),
287 NotificationManager.IMPORTANCE_HIGH));
288 }
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800289
Adam Lesinski182f73f2013-12-05 16:48:06 -0800290 publishBinderService(SERVICE, mRemoteService);
291 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600292
293 // Kick off pass to examine storage state
294 mHandler.removeMessages(MSG_CHECK);
295 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800296 }
297
298 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
299 @Override
300 public void checkMemory() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600301 // Kick off pass to examine storage state
302 mHandler.removeMessages(MSG_CHECK);
303 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800304 }
305
306 @Override
307 public boolean isMemoryLow() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600308 return Environment.getDataDirectory().getUsableSpace() < getMemoryLowThreshold();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800309 }
310
311 @Override
312 public long getMemoryLowThreshold() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600313 return getContext().getSystemService(StorageManager.class)
314 .getStorageLowBytes(Environment.getDataDirectory());
Adam Lesinski182f73f2013-12-05 16:48:06 -0800315 }
316 };
317
Dianne Hackborn532ea262017-03-17 17:50:55 -0700318 private final Binder mRemoteService = new Binder() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800319 @Override
320 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600321 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700322 dumpImpl(fd, pw, args);
323 }
324
325 @Override
326 public void onShellCommand(FileDescriptor in, FileDescriptor out,
327 FileDescriptor err, String[] args, ShellCallback callback,
328 ResultReceiver resultReceiver) {
329 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800330 }
331 };
332
Dianne Hackborn532ea262017-03-17 17:50:55 -0700333 class Shell extends ShellCommand {
334 @Override
335 public int onCommand(String cmd) {
336 return onShellCommand(this, cmd);
337 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800338
Dianne Hackborn532ea262017-03-17 17:50:55 -0700339 @Override
340 public void onHelp() {
341 PrintWriter pw = getOutPrintWriter();
342 dumpHelp(pw);
343 }
344 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800345
Dianne Hackborn532ea262017-03-17 17:50:55 -0700346 static final int OPTION_FORCE_UPDATE = 1<<0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800347
Dianne Hackborn532ea262017-03-17 17:50:55 -0700348 int parseOptions(Shell shell) {
349 String opt;
350 int opts = 0;
351 while ((opt = shell.getNextOption()) != null) {
352 if ("-f".equals(opt)) {
353 opts |= OPTION_FORCE_UPDATE;
354 }
355 }
356 return opts;
357 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800358
Dianne Hackborn532ea262017-03-17 17:50:55 -0700359 int onShellCommand(Shell shell, String cmd) {
360 if (cmd == null) {
361 return shell.handleDefaultCommands(cmd);
362 }
363 PrintWriter pw = shell.getOutPrintWriter();
364 switch (cmd) {
365 case "force-low": {
366 int opts = parseOptions(shell);
367 getContext().enforceCallingOrSelfPermission(
368 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600369 mForceLevel = State.LEVEL_LOW;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700370 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700371 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600372 mHandler.removeMessages(MSG_CHECK);
373 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700374 pw.println(seq);
375 }
376 } break;
377 case "force-not-low": {
378 int opts = parseOptions(shell);
379 getContext().enforceCallingOrSelfPermission(
380 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600381 mForceLevel = State.LEVEL_NORMAL;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700382 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700383 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600384 mHandler.removeMessages(MSG_CHECK);
385 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700386 pw.println(seq);
387 }
388 } break;
389 case "reset": {
390 int opts = parseOptions(shell);
391 getContext().enforceCallingOrSelfPermission(
392 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600393 mForceLevel = State.LEVEL_UNKNOWN;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700394 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700395 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600396 mHandler.removeMessages(MSG_CHECK);
397 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700398 pw.println(seq);
399 }
400 } break;
401 default:
402 return shell.handleDefaultCommands(cmd);
403 }
404 return 0;
405 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800406
Dianne Hackborn532ea262017-03-17 17:50:55 -0700407 static void dumpHelp(PrintWriter pw) {
408 pw.println("Device storage monitor service (devicestoragemonitor) commands:");
409 pw.println(" help");
410 pw.println(" Print this help text.");
411 pw.println(" force-low [-f]");
412 pw.println(" Force storage to be low, freezing storage state.");
413 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
414 pw.println(" force-not-low [-f]");
415 pw.println(" Force storage to not be low, freezing storage state.");
416 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
417 pw.println(" reset [-f]");
418 pw.println(" Unfreeze storage state, returning to current real values.");
419 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
420 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800421
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600422 void dumpImpl(FileDescriptor fd, PrintWriter _pw, String[] args) {
423 final IndentingPrintWriter pw = new IndentingPrintWriter(_pw, " ");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700424 if (args == null || args.length == 0 || "-a".equals(args[0])) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600425 pw.println("Known volumes:");
426 pw.increaseIndent();
427 for (int i = 0; i < mStates.size(); i++) {
428 final UUID uuid = mStates.keyAt(i);
429 final State state = mStates.valueAt(i);
430 if (StorageManager.UUID_DEFAULT.equals(uuid)) {
431 pw.println("Default:");
432 } else {
433 pw.println(uuid + ":");
434 }
435 pw.increaseIndent();
436 pw.printPair("level", State.levelToString(state.level));
437 pw.printPair("lastUsableBytes", state.lastUsableBytes);
438 pw.println();
439 pw.decreaseIndent();
440 }
441 pw.decreaseIndent();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700442 pw.println();
443
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600444 pw.printPair("mSeq", mSeq.get());
445 pw.printPair("mForceState", State.levelToString(mForceLevel));
446 pw.println();
447 pw.println();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700448
Dianne Hackborn532ea262017-03-17 17:50:55 -0700449 } else {
450 Shell shell = new Shell();
451 shell.exec(mRemoteService, null, fd, null, args, null, new ResultReceiver(null));
452 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700454
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600455 private void updateNotifications(VolumeInfo vol, int oldLevel, int newLevel) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800456 final Context context = getContext();
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600457 final UUID uuid = StorageManager.convert(vol.getFsUuid());
458
459 if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
460 Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
461 lowMemIntent.putExtra(StorageManager.EXTRA_UUID, uuid);
462 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
463
464 final CharSequence title = context.getText(
465 com.android.internal.R.string.low_internal_storage_view_title);
466
467 final CharSequence details;
468 if (StorageManager.UUID_DEFAULT.equals(uuid)) {
469 details = context.getText(isBootImageOnDisk()
470 ? com.android.internal.R.string.low_internal_storage_view_text
471 : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
472 } else {
473 details = context.getText(
474 com.android.internal.R.string.low_internal_storage_view_text);
475 }
476
477 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
478 null, UserHandle.CURRENT);
479 Notification notification =
480 new Notification.Builder(context, SystemNotificationChannels.ALERTS)
481 .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
482 .setTicker(title)
483 .setColor(context.getColor(
484 com.android.internal.R.color.system_notification_accent_color))
485 .setContentTitle(title)
486 .setContentText(details)
487 .setContentIntent(intent)
488 .setStyle(new Notification.BigTextStyle()
489 .bigText(details))
490 .setVisibility(Notification.VISIBILITY_PUBLIC)
491 .setCategory(Notification.CATEGORY_SYSTEM)
492 .extend(new Notification.TvExtender()
493 .setChannelId(TV_NOTIFICATION_CHANNEL_ID))
494 .build();
495 notification.flags |= Notification.FLAG_NO_CLEAR;
496 mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
497 notification, UserHandle.ALL);
498 } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
499 mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
500 UserHandle.ALL);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 }
503
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600504 private void updateBroadcasts(VolumeInfo vol, int oldLevel, int newLevel, int seq) {
505 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, vol.getFsUuid())) {
506 // We don't currently send broadcasts for secondary volumes
507 return;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700508 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700509
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600510 final Intent lowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW)
511 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
512 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
513 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
514 .putExtra(EXTRA_SEQUENCE, seq);
515 final Intent notLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK)
516 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
517 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
518 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
519 .putExtra(EXTRA_SEQUENCE, seq);
Jake Hambybb371632010-08-23 18:16:48 -0700520
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600521 if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
522 getContext().sendStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
523 } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
524 getContext().removeStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
525 getContext().sendBroadcastAsUser(notLowIntent, UserHandle.ALL);
526 }
527
528 final Intent fullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL)
529 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
530 .putExtra(EXTRA_SEQUENCE, seq);
531 final Intent notFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)
532 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
533 .putExtra(EXTRA_SEQUENCE, seq);
534
535 if (State.isEntering(State.LEVEL_FULL, oldLevel, newLevel)) {
536 getContext().sendStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
537 } else if (State.isLeaving(State.LEVEL_FULL, oldLevel, newLevel)) {
538 getContext().removeStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
539 getContext().sendBroadcastAsUser(notFullIntent, UserHandle.ALL);
540 }
Jake Hambybb371632010-08-23 18:16:48 -0700541 }
542
Adam Lesinski182f73f2013-12-05 16:48:06 -0800543 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700544 public CacheFileDeletedObserver() {
545 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
546 }
547
548 @Override
549 public void onEvent(int event, String path) {
550 EventLogTags.writeCacheFileDeleted(path);
551 }
552 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553}