19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007-2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskipackage com.android.server.storage;
18182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
19ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport android.annotation.WorkerThread;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.Notification;
21ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport android.app.NotificationChannel;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.NotificationManager;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.PendingIntent;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Intent;
26197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackbornimport android.content.pm.PackageManager;
27ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport android.net.TrafficStats;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Binder;
29f882efadd378e0476b7362e474f3a20dcf1c0d27Dianne Hackbornimport android.os.Environment;
304b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkeyimport android.os.FileObserver;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler;
32a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkeyimport android.os.HandlerThread;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message;
34532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackbornimport android.os.ResultReceiver;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.ServiceManager;
36532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackbornimport android.os.ShellCallback;
37532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackbornimport android.os.ShellCommand;
385ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackbornimport android.os.UserHandle;
39be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport android.os.storage.StorageManager;
40ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport android.os.storage.VolumeInfo;
41ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport android.text.format.DateUtils;
42ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport android.util.ArrayMap;
438a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog;
44ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
45ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
46ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.internal.notification.SystemNotificationChannels;
47ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.internal.util.DumpUtils;
48ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.internal.util.IndentingPrintWriter;
49ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.server.EventLogTags;
50ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.server.IoThread;
51ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.server.SystemService;
52ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.server.pm.InstructionSets;
53ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport com.android.server.pm.PackageManagerService;
54ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
55ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport dalvik.system.VMRuntime;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
57be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport java.io.File;
58be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport java.io.FileDescriptor;
59ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport java.io.IOException;
60be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport java.io.PrintWriter;
61ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport java.util.Objects;
62ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkeyimport java.util.UUID;
63532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackbornimport java.util.concurrent.atomic.AtomicInteger;
64be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
66ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey * Service that monitors and maintains free space on storage volumes.
67ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey * <p>
68ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey * As the free space on a volume nears the threshold defined by
69ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey * {@link StorageManager#getStorageLowBytes(File)}, this service will clear out
70ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey * cached data to keep the disk from entering this low state.
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
72182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskipublic class DeviceStorageMonitorService extends SystemService {
73ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private static final String TAG = "DeviceStorageMonitorService";
74be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
75532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    /**
76532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
77532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn     * Current int sequence number of the update.
78532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn     */
79532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    public static final String EXTRA_SEQUENCE = "seq";
80532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn
81ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private static final int MSG_CHECK = 1;
82529f91fc8e7e884ef19bef8eb3e4e3a1d69336f4Jeff Sharkey
83ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private static final long DEFAULT_LOG_DELTA_BYTES = 64 * TrafficStats.MB_IN_BYTES;
84ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private static final long DEFAULT_CHECK_INTERVAL = DateUtils.MINUTE_IN_MILLIS;
85be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
86d42fe85bc93e4a27517555f27a8e7a591a135e19Richard Uhler    // com.android.internal.R.string.low_internal_storage_view_text_no_boot
87d42fe85bc93e4a27517555f27a8e7a591a135e19Richard Uhler    // hard codes 250MB in the message as the storage space required for the
88d42fe85bc93e4a27517555f27a8e7a591a135e19Richard Uhler    // boot image.
89ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * TrafficStats.MB_IN_BYTES;
90ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
91ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private NotificationManager mNotifManager;
92ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
93ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    /** Sequence number used for testing */
94ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private final AtomicInteger mSeq = new AtomicInteger(1);
95ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    /** Forced level used for testing */
96ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private volatile int mForceLevel = State.LEVEL_UNKNOWN;
97ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
98ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    /** Map from storage volume UUID to internal state */
99ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private final ArrayMap<UUID, State> mStates = new ArrayMap<>();
100ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
101ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    /**
102ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey     * State for a specific storage volume, including the current "level" that
103ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey     * we've alerted the user and apps about.
104ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey     */
105ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private static class State {
106ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        private static final int LEVEL_UNKNOWN = -1;
107ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        private static final int LEVEL_NORMAL = 0;
108ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        private static final int LEVEL_LOW = 1;
109ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        private static final int LEVEL_FULL = 2;
110ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
111ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        /** Last "level" that we alerted about */
112ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        public int level = LEVEL_NORMAL;
113ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        /** Last {@link File#getUsableSpace()} that we logged about */
114ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        public long lastUsableBytes = Long.MAX_VALUE;
115ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
116ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        /**
117ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * Test if the given level transition is "entering" a specific level.
118ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * <p>
119ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * As an example, a transition from {@link #LEVEL_NORMAL} to
120ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * {@link #LEVEL_FULL} is considered to "enter" both {@link #LEVEL_LOW}
121ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * and {@link #LEVEL_FULL}.
122ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         */
123ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        private static boolean isEntering(int level, int oldLevel, int newLevel) {
124ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            return newLevel >= level && (oldLevel < level || oldLevel == LEVEL_UNKNOWN);
125ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        }
126ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
127ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        /**
128ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * Test if the given level transition is "leaving" a specific level.
129ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * <p>
130ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * As an example, a transition from {@link #LEVEL_FULL} to
131ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * {@link #LEVEL_NORMAL} is considered to "leave" both
132ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         * {@link #LEVEL_FULL} and {@link #LEVEL_LOW}.
133ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey         */
134ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        private static boolean isLeaving(int level, int oldLevel, int newLevel) {
135ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            return newLevel < level && (oldLevel >= level || oldLevel == LEVEL_UNKNOWN);
136ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        }
137ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
138ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        private static String levelToString(int level) {
139ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            switch (level) {
140ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                case State.LEVEL_UNKNOWN: return "UNKNOWN";
141ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                case State.LEVEL_NORMAL: return "NORMAL";
142ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                case State.LEVEL_LOW: return "LOW";
143ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                case State.LEVEL_FULL: return "FULL";
144ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                default: return Integer.toString(level);
145ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            }
146ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        }
147ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    }
148ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
149182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private CacheFileDeletedObserver mCacheFileDeletedObserver;
1503161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This string is used for ServiceManager access to this class.
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
154182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    static final String SERVICE = "devicestoragemonitor";
1553161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
156af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch    private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
157d6bd6b9f649809dfaa2017a4ab54787a584b7748Dmitri Plotnikov
158a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey    private final HandlerThread mHandlerThread;
159a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey    private final Handler mHandler;
1603161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
161ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private State findOrCreateState(UUID uuid) {
162ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        State state = mStates.get(uuid);
163ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        if (state == null) {
164ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            state = new State();
165ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            mStates.put(uuid, state);
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
167ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        return state;
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1693161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
170ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    /**
171ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey     * Core logic that checks the storage state of every mounted private volume.
172ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey     * Since this can do heavy I/O, callers should invoke indirectly using
173ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey     * {@link #MSG_CHECK}.
174ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey     */
175ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    @WorkerThread
176ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private void check() {
177ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final StorageManager storage = getContext().getSystemService(StorageManager.class);
178ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final int seq = mSeq.get();
179ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
180ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        // Check every mounted private volume to see if they're low on space
181ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
182ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final File file = vol.getPath();
183ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final long fullBytes = storage.getStorageFullBytes(file);
184ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final long lowBytes = storage.getStorageLowBytes(file);
185ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
186ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // Automatically trim cached data when nearing the low threshold;
187ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // when it's within 150% of the threshold, we try trimming usage
188ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // back to 200% of the threshold.
189ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            if (file.getUsableSpace() < (lowBytes * 3) / 2) {
190ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                final PackageManagerService pms = (PackageManagerService) ServiceManager
191ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        .getService("package");
192ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                try {
193ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
194ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                } catch (IOException e) {
195ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    Slog.w(TAG, e);
196532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                }
197532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            }
198532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn
199ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // Send relevant broadcasts and show notifications based on any
200ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // recently noticed state transitions.
201ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final UUID uuid = StorageManager.convert(vol.getFsUuid());
202ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final State state = findOrCreateState(uuid);
203ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final long totalBytes = file.getTotalSpace();
204ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final long usableBytes = file.getUsableSpace();
205ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
206ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            int oldLevel = state.level;
207ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            int newLevel;
208ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            if (mForceLevel != State.LEVEL_UNKNOWN) {
209ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                // When in testing mode, use unknown old level to force sending
210ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                // of any relevant broadcasts.
211ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                oldLevel = State.LEVEL_UNKNOWN;
212ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                newLevel = mForceLevel;
213ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            } else if (usableBytes <= fullBytes) {
214ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                newLevel = State.LEVEL_FULL;
215ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            } else if (usableBytes <= lowBytes) {
216ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                newLevel = State.LEVEL_LOW;
217ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
218ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
219ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                newLevel = State.LEVEL_LOW;
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
221ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                newLevel = State.LEVEL_NORMAL;
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
223ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
224ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // Log whenever we notice drastic storage changes
225ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            if ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES)
226ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    || oldLevel != newLevel) {
227ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel,
228ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        usableBytes, totalBytes);
229ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                state.lastUsableBytes = usableBytes;
230bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby            }
231ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
232ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            updateNotifications(vol, oldLevel, newLevel);
233ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            updateBroadcasts(vol, oldLevel, newLevel, seq);
234ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
235ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            state.level = newLevel;
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2373161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
238ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        // Loop around to check again in future; we don't remove messages since
239ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        // there might be an immediate request pending.
240ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        if (!mHandler.hasMessages(MSG_CHECK)) {
241ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK),
242ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    DEFAULT_CHECK_INTERVAL);
243ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        }
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2453161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
246b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    public DeviceStorageMonitorService(Context context) {
247b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        super(context);
248a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey
249a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey        mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
250a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey        mHandlerThread.start();
251a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey
252a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey        mHandler = new Handler(mHandlerThread.getLooper()) {
253a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey            @Override
254a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey            public void handleMessage(Message msg) {
255a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey                switch (msg.what) {
256a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey                    case MSG_CHECK:
257a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey                        check();
258a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey                        return;
259a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey                }
260a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey            }
261a1e79948ecf1057fe91733baac2759ea91cc1894Jeff Sharkey        };
262182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
263be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
264a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom    private static boolean isBootImageOnDisk() {
265eeea67b8c3678d882d3774edc41242c63daa60faFyodor Kupolov        for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
266a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom            if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
267a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                return false;
268a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom            }
269a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        }
270a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        return true;
271a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom    }
272a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom
273182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    @Override
274182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    public void onStart() {
275ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final Context context = getContext();
276ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        mNotifManager = context.getSystemService(NotificationManager.class);
2774b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey
2784b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        mCacheFileDeletedObserver = new CacheFileDeletedObserver();
2794b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        mCacheFileDeletedObserver.startWatching();
280182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
281d6bd6b9f649809dfaa2017a4ab54787a584b7748Dmitri Plotnikov        // Ensure that the notification channel is set up
282d6bd6b9f649809dfaa2017a4ab54787a584b7748Dmitri Plotnikov        PackageManager packageManager = context.getPackageManager();
283d6bd6b9f649809dfaa2017a4ab54787a584b7748Dmitri Plotnikov        boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
284d6bd6b9f649809dfaa2017a4ab54787a584b7748Dmitri Plotnikov
285af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch        if (isTv) {
286ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            mNotifManager.createNotificationChannel(new NotificationChannel(
287af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                    TV_NOTIFICATION_CHANNEL_ID,
288af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                    context.getString(
289af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                        com.android.internal.R.string.device_storage_monitor_notification_channel),
290af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                    NotificationManager.IMPORTANCE_HIGH));
291af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch        }
292d6bd6b9f649809dfaa2017a4ab54787a584b7748Dmitri Plotnikov
293182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        publishBinderService(SERVICE, mRemoteService);
294182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
295ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
296ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        // Kick off pass to examine storage state
297ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        mHandler.removeMessages(MSG_CHECK);
298ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        mHandler.obtainMessage(MSG_CHECK).sendToTarget();
299182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
300182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
301182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
302182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
303182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        public void checkMemory() {
304ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // Kick off pass to examine storage state
305ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            mHandler.removeMessages(MSG_CHECK);
306ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            mHandler.obtainMessage(MSG_CHECK).sendToTarget();
307182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
308182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
309182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
310182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        public boolean isMemoryLow() {
311ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            return Environment.getDataDirectory().getUsableSpace() < getMemoryLowThreshold();
312182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
313182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
314182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
315182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        public long getMemoryLowThreshold() {
316ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            return getContext().getSystemService(StorageManager.class)
317ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    .getStorageLowBytes(Environment.getDataDirectory());
318182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
319182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    };
320182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
321532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    private final Binder mRemoteService = new Binder() {
322182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
323182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
324fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkey            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
325532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            dumpImpl(fd, pw, args);
326532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
327532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn
328532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        @Override
329532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        public void onShellCommand(FileDescriptor in, FileDescriptor out,
330532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                FileDescriptor err, String[] args, ShellCallback callback,
331532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                ResultReceiver resultReceiver) {
332532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
333182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
334182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    };
335182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
336532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    class Shell extends ShellCommand {
337532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        @Override
338532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        public int onCommand(String cmd) {
339532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            return onShellCommand(this, cmd);
340532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
341532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn
342532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        @Override
343532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        public void onHelp() {
344532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            PrintWriter pw = getOutPrintWriter();
345532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            dumpHelp(pw);
346532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
347532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    }
348532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn
349532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    static final int OPTION_FORCE_UPDATE = 1<<0;
350182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
351532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    int parseOptions(Shell shell) {
352532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        String opt;
353532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        int opts = 0;
354532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        while ((opt = shell.getNextOption()) != null) {
355532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            if ("-f".equals(opt)) {
356532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                opts |= OPTION_FORCE_UPDATE;
357532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            }
358532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
359532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        return opts;
360532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    }
361182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
362532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    int onShellCommand(Shell shell, String cmd) {
363532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        if (cmd == null) {
364532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            return shell.handleDefaultCommands(cmd);
365532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
366532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        PrintWriter pw = shell.getOutPrintWriter();
367532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        switch (cmd) {
368532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            case "force-low": {
369532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                int opts = parseOptions(shell);
370532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                getContext().enforceCallingOrSelfPermission(
371532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                        android.Manifest.permission.DEVICE_POWER, null);
372ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                mForceLevel = State.LEVEL_LOW;
373532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                int seq = mSeq.incrementAndGet();
374532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                if ((opts & OPTION_FORCE_UPDATE) != 0) {
375ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    mHandler.removeMessages(MSG_CHECK);
376ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    mHandler.obtainMessage(MSG_CHECK).sendToTarget();
377532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                    pw.println(seq);
378532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                }
379532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            } break;
380532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            case "force-not-low": {
381532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                int opts = parseOptions(shell);
382532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                getContext().enforceCallingOrSelfPermission(
383532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                        android.Manifest.permission.DEVICE_POWER, null);
384ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                mForceLevel = State.LEVEL_NORMAL;
385532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                int seq = mSeq.incrementAndGet();
386532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                if ((opts & OPTION_FORCE_UPDATE) != 0) {
387ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    mHandler.removeMessages(MSG_CHECK);
388ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    mHandler.obtainMessage(MSG_CHECK).sendToTarget();
389532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                    pw.println(seq);
390532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                }
391532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            } break;
392532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            case "reset": {
393532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                int opts = parseOptions(shell);
394532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                getContext().enforceCallingOrSelfPermission(
395532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                        android.Manifest.permission.DEVICE_POWER, null);
396ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                mForceLevel = State.LEVEL_UNKNOWN;
397532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                int seq = mSeq.incrementAndGet();
398532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                if ((opts & OPTION_FORCE_UPDATE) != 0) {
399ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    mHandler.removeMessages(MSG_CHECK);
400ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    mHandler.obtainMessage(MSG_CHECK).sendToTarget();
401532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                    pw.println(seq);
402532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                }
403532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            } break;
404532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            default:
405532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn                return shell.handleDefaultCommands(cmd);
406532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
407532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        return 0;
408532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    }
409182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
410532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    static void dumpHelp(PrintWriter pw) {
411532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("Device storage monitor service (devicestoragemonitor) commands:");
412532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("  help");
413532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("    Print this help text.");
414532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("  force-low [-f]");
415532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("    Force storage to be low, freezing storage state.");
416532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("    -f: force a storage change broadcast be sent, prints new sequence.");
417532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("  force-not-low [-f]");
418532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("    Force storage to not be low, freezing storage state.");
419532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("    -f: force a storage change broadcast be sent, prints new sequence.");
420532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("  reset [-f]");
421532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("    Unfreeze storage state, returning to current real values.");
422532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        pw.println("    -f: force a storage change broadcast be sent, prints new sequence.");
423532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn    }
424182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
425ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    void dumpImpl(FileDescriptor fd, PrintWriter _pw, String[] args) {
426ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final IndentingPrintWriter pw = new IndentingPrintWriter(_pw, "  ");
427532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        if (args == null || args.length == 0 || "-a".equals(args[0])) {
428ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.println("Known volumes:");
429ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.increaseIndent();
430ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            for (int i = 0; i < mStates.size(); i++) {
431ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                final UUID uuid = mStates.keyAt(i);
432ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                final State state = mStates.valueAt(i);
433ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                if (StorageManager.UUID_DEFAULT.equals(uuid)) {
434ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    pw.println("Default:");
435ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                } else {
436ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    pw.println(uuid + ":");
437ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                }
438ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                pw.increaseIndent();
439ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                pw.printPair("level", State.levelToString(state.level));
440ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                pw.printPair("lastUsableBytes", state.lastUsableBytes);
441ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                pw.println();
442ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                pw.decreaseIndent();
443532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            }
444ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.decreaseIndent();
445ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.println();
446532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn
447ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.printPair("mSeq", mSeq.get());
448ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.printPair("mForceState", State.levelToString(mForceLevel));
449ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.println();
450ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            pw.println();
451532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn
452532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        } else {
453532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            Shell shell = new Shell();
454532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn            shell.exec(mRemoteService, null, fd, null, args, null, new ResultReceiver(null));
455532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4573161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
458ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private void updateNotifications(VolumeInfo vol, int oldLevel, int newLevel) {
459182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        final Context context = getContext();
460ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final UUID uuid = StorageManager.convert(vol.getFsUuid());
461ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
462ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
463ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
464ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            lowMemIntent.putExtra(StorageManager.EXTRA_UUID, uuid);
465ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
466ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
467ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final CharSequence title = context.getText(
468ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    com.android.internal.R.string.low_internal_storage_view_title);
469ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
470ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            final CharSequence details;
471ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            if (StorageManager.UUID_DEFAULT.equals(uuid)) {
472ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                details = context.getText(isBootImageOnDisk()
473ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        ? com.android.internal.R.string.low_internal_storage_view_text
474ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
475ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            } else {
476ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                details = context.getText(
477ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        com.android.internal.R.string.low_internal_storage_view_text);
478ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            }
479ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
480ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
481ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    null, UserHandle.CURRENT);
482ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            Notification notification =
483ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    new Notification.Builder(context, SystemNotificationChannels.ALERTS)
484ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
485ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setTicker(title)
486ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setColor(context.getColor(
487ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                                com.android.internal.R.color.system_notification_accent_color))
488ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setContentTitle(title)
489ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setContentText(details)
490ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setContentIntent(intent)
491ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setStyle(new Notification.BigTextStyle()
492ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                                  .bigText(details))
493ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setVisibility(Notification.VISIBILITY_PUBLIC)
494ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .setCategory(Notification.CATEGORY_SYSTEM)
495ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .extend(new Notification.TvExtender()
496ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                                    .setChannelId(TV_NOTIFICATION_CHANNEL_ID))
497ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                            .build();
498ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            notification.flags |= Notification.FLAG_NO_CLEAR;
499ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
500ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    notification, UserHandle.ALL);
501ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
502ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
503ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                    UserHandle.ALL);
504532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
507ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey    private void updateBroadcasts(VolumeInfo vol, int oldLevel, int newLevel, int seq) {
508ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, vol.getFsUuid())) {
509ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            // We don't currently send broadcasts for secondary volumes
510ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            return;
511532ea26c7b66180b09524f96da8bca1110f41197Dianne Hackborn        }
5123161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
513ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final Intent lowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW)
514ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
515ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
516ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
517ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .putExtra(EXTRA_SEQUENCE, seq);
518ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final Intent notLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK)
519ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
520ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
521ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
522ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .putExtra(EXTRA_SEQUENCE, seq);
523ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
524ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
525ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            getContext().sendStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
526ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
527ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            getContext().removeStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
528ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            getContext().sendBroadcastAsUser(notLowIntent, UserHandle.ALL);
529ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        }
530bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby
531ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final Intent fullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL)
532ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
533ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .putExtra(EXTRA_SEQUENCE, seq);
534ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        final Intent notFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)
535ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
536ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey                .putExtra(EXTRA_SEQUENCE, seq);
537ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey
538ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        if (State.isEntering(State.LEVEL_FULL, oldLevel, newLevel)) {
539ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            getContext().sendStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
540ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        } else if (State.isLeaving(State.LEVEL_FULL, oldLevel, newLevel)) {
541ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            getContext().removeStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
542ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey            getContext().sendBroadcastAsUser(notFullIntent, UserHandle.ALL);
543ddff807b762a8a455287abc97aea8f97b98fb104Jeff Sharkey        }
544bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby    }
545bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby
546182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private static class CacheFileDeletedObserver extends FileObserver {
5474b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        public CacheFileDeletedObserver() {
5484b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey            super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
5494b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        }
5504b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey
5514b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        @Override
5524b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        public void onEvent(int event, String path) {
5534b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey            EventLogTags.writeCacheFileDeleted(path);
5544b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        }
5554b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey    }
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
557