blob: a35383f385f29eea393e4441c02c05fd8f94be59 [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;
Joe Onorato8a9b2202010-02-26 18:56:32 -080043import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
Jeff Sharkeyddff8072017-05-26 13:10:46 -060045import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
46import com.android.internal.notification.SystemNotificationChannels;
47import com.android.internal.util.DumpUtils;
48import com.android.internal.util.IndentingPrintWriter;
49import com.android.server.EventLogTags;
50import com.android.server.IoThread;
51import com.android.server.SystemService;
52import com.android.server.pm.InstructionSets;
53import com.android.server.pm.PackageManagerService;
Jeff Sharkeybe722152013-02-15 16:56:38 -080054
Brian Carlstroma39871e2014-09-29 13:44:04 -070055import dalvik.system.VMRuntime;
56
Jeff Sharkeyddff8072017-05-26 13:10:46 -060057import java.io.File;
58import java.io.FileDescriptor;
59import java.io.IOException;
60import java.io.PrintWriter;
61import java.util.Objects;
62import java.util.UUID;
63import java.util.concurrent.atomic.AtomicInteger;
64
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065/**
Jeff Sharkeyddff8072017-05-26 13:10:46 -060066 * Service that monitors and maintains free space on storage volumes.
67 * <p>
68 * As the free space on a volume nears the threshold defined by
69 * {@link StorageManager#getStorageLowBytes(File)}, this service will clear out
70 * cached data to keep the disk from entering this low state.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 */
Adam Lesinski182f73f2013-12-05 16:48:06 -080072public class DeviceStorageMonitorService extends SystemService {
Jeff Sharkeyddff8072017-05-26 13:10:46 -060073 private static final String TAG = "DeviceStorageMonitorService";
Jeff Sharkeybe722152013-02-15 16:56:38 -080074
Dianne Hackborn532ea262017-03-17 17:50:55 -070075 /**
76 * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
77 * Current int sequence number of the update.
78 */
79 public static final String EXTRA_SEQUENCE = "seq";
80
Jeff Sharkeyddff8072017-05-26 13:10:46 -060081 private static final int MSG_CHECK = 1;
Jeff Sharkey529f91f2015-04-18 20:23:13 -070082
Jeff Sharkeyddff8072017-05-26 13:10:46 -060083 private static final long DEFAULT_LOG_DELTA_BYTES = 64 * TrafficStats.MB_IN_BYTES;
84 private static final long DEFAULT_CHECK_INTERVAL = DateUtils.MINUTE_IN_MILLIS;
Jeff Sharkeybe722152013-02-15 16:56:38 -080085
Richard Uhlerd42fe852016-08-12 13:51:51 -070086 // com.android.internal.R.string.low_internal_storage_view_text_no_boot
87 // hard codes 250MB in the message as the storage space required for the
88 // boot image.
Jeff Sharkeyddff8072017-05-26 13:10:46 -060089 private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * TrafficStats.MB_IN_BYTES;
Richard Uhlerd42fe852016-08-12 13:51:51 -070090
Jeff Sharkeyddff8072017-05-26 13:10:46 -060091 private NotificationManager mNotifManager;
Jeff Sharkeybe722152013-02-15 16:56:38 -080092
Jeff Sharkeyddff8072017-05-26 13:10:46 -060093 /** Sequence number used for testing */
94 private final AtomicInteger mSeq = new AtomicInteger(1);
95 /** Forced level used for testing */
96 private volatile int mForceLevel = State.LEVEL_UNKNOWN;
Jeff Sharkeybe722152013-02-15 16:56:38 -080097
Jeff Sharkeyddff8072017-05-26 13:10:46 -060098 /** Map from storage volume UUID to internal state */
99 private final ArrayMap<UUID, State> mStates = new ArrayMap<>();
100
101 /**
102 * State for a specific storage volume, including the current "level" that
103 * we've alerted the user and apps about.
104 */
105 private static class State {
106 private static final int LEVEL_UNKNOWN = -1;
107 private static final int LEVEL_NORMAL = 0;
108 private static final int LEVEL_LOW = 1;
109 private static final int LEVEL_FULL = 2;
110
111 /** Last "level" that we alerted about */
112 public int level = LEVEL_NORMAL;
113 /** Last {@link File#getUsableSpace()} that we logged about */
114 public long lastUsableBytes = Long.MAX_VALUE;
115
116 /**
117 * Test if the given level transition is "entering" a specific level.
118 * <p>
119 * As an example, a transition from {@link #LEVEL_NORMAL} to
120 * {@link #LEVEL_FULL} is considered to "enter" both {@link #LEVEL_LOW}
121 * and {@link #LEVEL_FULL}.
122 */
123 private static boolean isEntering(int level, int oldLevel, int newLevel) {
124 return newLevel >= level && (oldLevel < level || oldLevel == LEVEL_UNKNOWN);
125 }
126
127 /**
128 * Test if the given level transition is "leaving" a specific level.
129 * <p>
130 * As an example, a transition from {@link #LEVEL_FULL} to
131 * {@link #LEVEL_NORMAL} is considered to "leave" both
132 * {@link #LEVEL_FULL} and {@link #LEVEL_LOW}.
133 */
134 private static boolean isLeaving(int level, int oldLevel, int newLevel) {
135 return newLevel < level && (oldLevel >= level || oldLevel == LEVEL_UNKNOWN);
136 }
137
138 private static String levelToString(int level) {
139 switch (level) {
140 case State.LEVEL_UNKNOWN: return "UNKNOWN";
141 case State.LEVEL_NORMAL: return "NORMAL";
142 case State.LEVEL_LOW: return "LOW";
143 case State.LEVEL_FULL: return "FULL";
144 default: return Integer.toString(level);
145 }
146 }
147 }
148
Adam Lesinski182f73f2013-12-05 16:48:06 -0800149 private CacheFileDeletedObserver mCacheFileDeletedObserver;
Doug Zongker3161795b2009-10-07 15:14:03 -0700150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 /**
152 * This string is used for ServiceManager access to this class.
153 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800154 static final String SERVICE = "devicestoragemonitor";
Doug Zongker3161795b2009-10-07 15:14:03 -0700155
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500156 private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800157
Jeff Sharkeya1e79942017-08-02 14:24:35 -0600158 private final HandlerThread mHandlerThread;
159 private final Handler mHandler;
Doug Zongker3161795b2009-10-07 15:14:03 -0700160
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600161 private State findOrCreateState(UUID uuid) {
162 State state = mStates.get(uuid);
163 if (state == null) {
164 state = new State();
165 mStates.put(uuid, state);
Doug Zongker3161795b2009-10-07 15:14:03 -0700166 }
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600167 return state;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700169
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600170 /**
171 * Core logic that checks the storage state of every mounted private volume.
172 * Since this can do heavy I/O, callers should invoke indirectly using
173 * {@link #MSG_CHECK}.
174 */
175 @WorkerThread
176 private void check() {
177 final StorageManager storage = getContext().getSystemService(StorageManager.class);
178 final int seq = mSeq.get();
Doug Zongker3161795b2009-10-07 15:14:03 -0700179
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600180 // Check every mounted private volume to see if they're low on space
181 for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
182 final File file = vol.getPath();
183 final long fullBytes = storage.getStorageFullBytes(file);
184 final long lowBytes = storage.getStorageLowBytes(file);
Doug Zongker3161795b2009-10-07 15:14:03 -0700185
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600186 // Automatically trim cached data when nearing the low threshold;
187 // when it's within 150% of the threshold, we try trimming usage
188 // back to 200% of the threshold.
189 if (file.getUsableSpace() < (lowBytes * 3) / 2) {
190 final PackageManagerService pms = (PackageManagerService) ServiceManager
191 .getService("package");
192 try {
193 pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
194 } catch (IOException e) {
195 Slog.w(TAG, e);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700196 }
197 }
Dianne Hackborn532ea262017-03-17 17:50:55 -0700198
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600199 // Send relevant broadcasts and show notifications based on any
200 // recently noticed state transitions.
201 final UUID uuid = StorageManager.convert(vol.getFsUuid());
202 final State state = findOrCreateState(uuid);
203 final long totalBytes = file.getTotalSpace();
204 final long usableBytes = file.getUsableSpace();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700205
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600206 int oldLevel = state.level;
207 int newLevel;
208 if (mForceLevel != State.LEVEL_UNKNOWN) {
209 // When in testing mode, use unknown old level to force sending
210 // of any relevant broadcasts.
211 oldLevel = State.LEVEL_UNKNOWN;
212 newLevel = mForceLevel;
213 } else if (usableBytes <= fullBytes) {
214 newLevel = State.LEVEL_FULL;
215 } else if (usableBytes <= lowBytes) {
216 newLevel = State.LEVEL_LOW;
217 } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
218 && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
219 newLevel = State.LEVEL_LOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 } else {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600221 newLevel = State.LEVEL_NORMAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700223
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600224 // Log whenever we notice drastic storage changes
225 if ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES)
226 || oldLevel != newLevel) {
227 EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel,
228 usableBytes, totalBytes);
229 state.lastUsableBytes = usableBytes;
230 }
231
232 updateNotifications(vol, oldLevel, newLevel);
233 updateBroadcasts(vol, oldLevel, newLevel, seq);
234
235 state.level = newLevel;
236 }
237
238 // Loop around to check again in future; we don't remove messages since
239 // there might be an immediate request pending.
240 if (!mHandler.hasMessages(MSG_CHECK)) {
241 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK),
242 DEFAULT_CHECK_INTERVAL);
243 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700245
Jeff Brownb880d882014-02-10 19:47:07 -0800246 public DeviceStorageMonitorService(Context context) {
247 super(context);
Jeff Sharkeya1e79942017-08-02 14:24:35 -0600248
249 mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
250 mHandlerThread.start();
251
252 mHandler = new Handler(mHandlerThread.getLooper()) {
253 @Override
254 public void handleMessage(Message msg) {
255 switch (msg.what) {
256 case MSG_CHECK:
257 check();
258 return;
259 }
260 }
261 };
Adam Lesinski182f73f2013-12-05 16:48:06 -0800262 }
Jeff Sharkeybe722152013-02-15 16:56:38 -0800263
Brian Carlstroma39871e2014-09-29 13:44:04 -0700264 private static boolean isBootImageOnDisk() {
Fyodor Kupoloveeea67b2015-02-23 17:14:45 -0800265 for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
Brian Carlstroma39871e2014-09-29 13:44:04 -0700266 if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
267 return false;
268 }
269 }
270 return true;
271 }
272
Adam Lesinski182f73f2013-12-05 16:48:06 -0800273 @Override
274 public void onStart() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600275 final Context context = getContext();
276 mNotifManager = context.getSystemService(NotificationManager.class);
Jeff Sharkey4b496572012-04-19 14:17:03 -0700277
278 mCacheFileDeletedObserver = new CacheFileDeletedObserver();
279 mCacheFileDeletedObserver.startWatching();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800280
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800281 // Ensure that the notification channel is set up
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800282 PackageManager packageManager = context.getPackageManager();
283 boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
284
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500285 if (isTv) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600286 mNotifManager.createNotificationChannel(new NotificationChannel(
Geoffrey Pitschaf759c52017-02-15 09:35:38 -0500287 TV_NOTIFICATION_CHANNEL_ID,
288 context.getString(
289 com.android.internal.R.string.device_storage_monitor_notification_channel),
290 NotificationManager.IMPORTANCE_HIGH));
291 }
Dmitri Plotnikovd6bd6b92017-01-27 16:42:12 -0800292
Adam Lesinski182f73f2013-12-05 16:48:06 -0800293 publishBinderService(SERVICE, mRemoteService);
294 publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600295
296 // Kick off pass to examine storage state
297 mHandler.removeMessages(MSG_CHECK);
298 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800299 }
300
301 private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
302 @Override
303 public void checkMemory() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600304 // Kick off pass to examine storage state
305 mHandler.removeMessages(MSG_CHECK);
306 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800307 }
308
309 @Override
310 public boolean isMemoryLow() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600311 return Environment.getDataDirectory().getUsableSpace() < getMemoryLowThreshold();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800312 }
313
314 @Override
315 public long getMemoryLowThreshold() {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600316 return getContext().getSystemService(StorageManager.class)
317 .getStorageLowBytes(Environment.getDataDirectory());
Adam Lesinski182f73f2013-12-05 16:48:06 -0800318 }
319 };
320
Dianne Hackborn532ea262017-03-17 17:50:55 -0700321 private final Binder mRemoteService = new Binder() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800322 @Override
323 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600324 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700325 dumpImpl(fd, pw, args);
326 }
327
328 @Override
329 public void onShellCommand(FileDescriptor in, FileDescriptor out,
330 FileDescriptor err, String[] args, ShellCallback callback,
331 ResultReceiver resultReceiver) {
332 (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800333 }
334 };
335
Dianne Hackborn532ea262017-03-17 17:50:55 -0700336 class Shell extends ShellCommand {
337 @Override
338 public int onCommand(String cmd) {
339 return onShellCommand(this, cmd);
340 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800341
Dianne Hackborn532ea262017-03-17 17:50:55 -0700342 @Override
343 public void onHelp() {
344 PrintWriter pw = getOutPrintWriter();
345 dumpHelp(pw);
346 }
347 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800348
Dianne Hackborn532ea262017-03-17 17:50:55 -0700349 static final int OPTION_FORCE_UPDATE = 1<<0;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800350
Dianne Hackborn532ea262017-03-17 17:50:55 -0700351 int parseOptions(Shell shell) {
352 String opt;
353 int opts = 0;
354 while ((opt = shell.getNextOption()) != null) {
355 if ("-f".equals(opt)) {
356 opts |= OPTION_FORCE_UPDATE;
357 }
358 }
359 return opts;
360 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800361
Dianne Hackborn532ea262017-03-17 17:50:55 -0700362 int onShellCommand(Shell shell, String cmd) {
363 if (cmd == null) {
364 return shell.handleDefaultCommands(cmd);
365 }
366 PrintWriter pw = shell.getOutPrintWriter();
367 switch (cmd) {
368 case "force-low": {
369 int opts = parseOptions(shell);
370 getContext().enforceCallingOrSelfPermission(
371 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600372 mForceLevel = State.LEVEL_LOW;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700373 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700374 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600375 mHandler.removeMessages(MSG_CHECK);
376 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700377 pw.println(seq);
378 }
379 } break;
380 case "force-not-low": {
381 int opts = parseOptions(shell);
382 getContext().enforceCallingOrSelfPermission(
383 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600384 mForceLevel = State.LEVEL_NORMAL;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700385 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700386 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600387 mHandler.removeMessages(MSG_CHECK);
388 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700389 pw.println(seq);
390 }
391 } break;
392 case "reset": {
393 int opts = parseOptions(shell);
394 getContext().enforceCallingOrSelfPermission(
395 android.Manifest.permission.DEVICE_POWER, null);
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600396 mForceLevel = State.LEVEL_UNKNOWN;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700397 int seq = mSeq.incrementAndGet();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700398 if ((opts & OPTION_FORCE_UPDATE) != 0) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600399 mHandler.removeMessages(MSG_CHECK);
400 mHandler.obtainMessage(MSG_CHECK).sendToTarget();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700401 pw.println(seq);
402 }
403 } break;
404 default:
405 return shell.handleDefaultCommands(cmd);
406 }
407 return 0;
408 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800409
Dianne Hackborn532ea262017-03-17 17:50:55 -0700410 static void dumpHelp(PrintWriter pw) {
411 pw.println("Device storage monitor service (devicestoragemonitor) commands:");
412 pw.println(" help");
413 pw.println(" Print this help text.");
414 pw.println(" force-low [-f]");
415 pw.println(" Force storage to be low, freezing storage state.");
416 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
417 pw.println(" force-not-low [-f]");
418 pw.println(" Force storage to not be low, freezing storage state.");
419 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
420 pw.println(" reset [-f]");
421 pw.println(" Unfreeze storage state, returning to current real values.");
422 pw.println(" -f: force a storage change broadcast be sent, prints new sequence.");
423 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800424
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600425 void dumpImpl(FileDescriptor fd, PrintWriter _pw, String[] args) {
426 final IndentingPrintWriter pw = new IndentingPrintWriter(_pw, " ");
Dianne Hackborn532ea262017-03-17 17:50:55 -0700427 if (args == null || args.length == 0 || "-a".equals(args[0])) {
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600428 pw.println("Known volumes:");
429 pw.increaseIndent();
430 for (int i = 0; i < mStates.size(); i++) {
431 final UUID uuid = mStates.keyAt(i);
432 final State state = mStates.valueAt(i);
433 if (StorageManager.UUID_DEFAULT.equals(uuid)) {
434 pw.println("Default:");
435 } else {
436 pw.println(uuid + ":");
437 }
438 pw.increaseIndent();
439 pw.printPair("level", State.levelToString(state.level));
440 pw.printPair("lastUsableBytes", state.lastUsableBytes);
441 pw.println();
442 pw.decreaseIndent();
443 }
444 pw.decreaseIndent();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700445 pw.println();
446
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600447 pw.printPair("mSeq", mSeq.get());
448 pw.printPair("mForceState", State.levelToString(mForceLevel));
449 pw.println();
450 pw.println();
Dianne Hackborn532ea262017-03-17 17:50:55 -0700451
Dianne Hackborn532ea262017-03-17 17:50:55 -0700452 } else {
453 Shell shell = new Shell();
454 shell.exec(mRemoteService, null, fd, null, args, null, new ResultReceiver(null));
455 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700457
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600458 private void updateNotifications(VolumeInfo vol, int oldLevel, int newLevel) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800459 final Context context = getContext();
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600460 final UUID uuid = StorageManager.convert(vol.getFsUuid());
461
462 if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
463 Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
464 lowMemIntent.putExtra(StorageManager.EXTRA_UUID, uuid);
465 lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
466
467 final CharSequence title = context.getText(
468 com.android.internal.R.string.low_internal_storage_view_title);
469
470 final CharSequence details;
471 if (StorageManager.UUID_DEFAULT.equals(uuid)) {
472 details = context.getText(isBootImageOnDisk()
473 ? com.android.internal.R.string.low_internal_storage_view_text
474 : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
475 } else {
476 details = context.getText(
477 com.android.internal.R.string.low_internal_storage_view_text);
478 }
479
480 PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
481 null, UserHandle.CURRENT);
482 Notification notification =
483 new Notification.Builder(context, SystemNotificationChannels.ALERTS)
484 .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
485 .setTicker(title)
486 .setColor(context.getColor(
487 com.android.internal.R.color.system_notification_accent_color))
488 .setContentTitle(title)
489 .setContentText(details)
490 .setContentIntent(intent)
491 .setStyle(new Notification.BigTextStyle()
492 .bigText(details))
493 .setVisibility(Notification.VISIBILITY_PUBLIC)
494 .setCategory(Notification.CATEGORY_SYSTEM)
495 .extend(new Notification.TvExtender()
496 .setChannelId(TV_NOTIFICATION_CHANNEL_ID))
497 .build();
498 notification.flags |= Notification.FLAG_NO_CLEAR;
499 mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
500 notification, UserHandle.ALL);
501 } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
502 mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
503 UserHandle.ALL);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700504 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 }
506
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600507 private void updateBroadcasts(VolumeInfo vol, int oldLevel, int newLevel, int seq) {
508 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, vol.getFsUuid())) {
509 // We don't currently send broadcasts for secondary volumes
510 return;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700511 }
Doug Zongker3161795b2009-10-07 15:14:03 -0700512
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600513 final Intent lowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW)
514 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
515 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
516 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
517 .putExtra(EXTRA_SEQUENCE, seq);
518 final Intent notLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK)
519 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
520 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
521 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
522 .putExtra(EXTRA_SEQUENCE, seq);
Jake Hambybb371632010-08-23 18:16:48 -0700523
Jeff Sharkeyddff8072017-05-26 13:10:46 -0600524 if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
525 getContext().sendStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
526 } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
527 getContext().removeStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
528 getContext().sendBroadcastAsUser(notLowIntent, UserHandle.ALL);
529 }
530
531 final Intent fullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL)
532 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
533 .putExtra(EXTRA_SEQUENCE, seq);
534 final Intent notFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)
535 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
536 .putExtra(EXTRA_SEQUENCE, seq);
537
538 if (State.isEntering(State.LEVEL_FULL, oldLevel, newLevel)) {
539 getContext().sendStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
540 } else if (State.isLeaving(State.LEVEL_FULL, oldLevel, newLevel)) {
541 getContext().removeStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
542 getContext().sendBroadcastAsUser(notFullIntent, UserHandle.ALL);
543 }
Jake Hambybb371632010-08-23 18:16:48 -0700544 }
545
Adam Lesinski182f73f2013-12-05 16:48:06 -0800546 private static class CacheFileDeletedObserver extends FileObserver {
Jeff Sharkey4b496572012-04-19 14:17:03 -0700547 public CacheFileDeletedObserver() {
548 super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
549 }
550
551 @Override
552 public void onEvent(int event, String path) {
553 EventLogTags.writeCacheFileDeleted(path);
554 }
555 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556}