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
19182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskiimport com.android.server.EventLogTags;
20182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskiimport com.android.server.SystemService;
21eeea67b8c3678d882d3774edc41242c63daa60faFyodor Kupolovimport com.android.server.pm.InstructionSets;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.Notification;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.NotificationManager;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.PendingIntent;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ContentResolver;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Intent;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.pm.IPackageDataObserver;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.pm.IPackageManager;
30197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackbornimport android.content.pm.PackageManager;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Binder;
32f882efadd378e0476b7362e474f3a20dcf1c0d27Dianne Hackbornimport android.os.Environment;
334b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkeyimport android.os.FileObserver;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler;
35182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskiimport android.os.IBinder;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.RemoteException;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.ServiceManager;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.StatFs;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemProperties;
425ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackbornimport android.os.UserHandle;
43be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport android.os.storage.StorageManager;
4443866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongkerimport android.provider.Settings;
45197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackbornimport android.text.format.Formatter;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.EventLog;
478a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog;
48197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackbornimport android.util.TimeUtils;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
50be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport java.io.File;
51be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport java.io.FileDescriptor;
52be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkeyimport java.io.PrintWriter;
53be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
54a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstromimport dalvik.system.VMRuntime;
55a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
5743866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * This class implements a service to monitor the amount of disk
5843866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * storage space on the device.  If the free storage on device is less
5943866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * than a tunable threshold value (a secure settings parameter;
6043866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * default 10%) a low memory notification is displayed to alert the
6143866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * user. If the user clicks on the low memory notification the
6243866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * Application Manager application gets launched to let the user free
6343866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * storage space.
6443866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker *
6543866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * Event log events: A low memory event with the free storage on
6643866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * device in bytes is logged to the event log when the device goes low
6743866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * on storage space.  The amount of free storage on the device is
6843866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * periodically logged to the event log. The log interval is a secure
6943866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * settings parameter with a default value of 12 hours.  When the free
7043866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * storage differential goes below a threshold (again a secure
7143866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * settings parameter with a default value of 2MB), the free memory is
7243866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker * logged to the event log.
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
74182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskipublic class DeviceStorageMonitorService extends SystemService {
75182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    static final String TAG = "DeviceStorageMonitorService";
76be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
77529f91fc8e7e884ef19bef8eb3e4e3a1d69336f4Jeff Sharkey    // TODO: extend to watch and manage caches on all private volumes
78529f91fc8e7e884ef19bef8eb3e4e3a1d69336f4Jeff Sharkey
79182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    static final boolean DEBUG = false;
80182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    static final boolean localLOGV = false;
81be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
82182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    static final int DEVICE_MEMORY_WHAT = 1;
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MONITOR_INTERVAL = 1; //in minutes
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
85be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
89be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
903161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker    private long mFreeMem;  // on /data
91197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    private long mFreeMemAfterLastCacheClear;  // on /data
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private long mLastReportedFreeMem;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private long mLastReportedFreeMemTime;
94182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    boolean mLowMemFlag=false;
95bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby    private boolean mMemFullFlag=false;
96a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom    private final boolean mIsBootImageOnDisk;
97b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final ContentResolver mResolver;
98b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final long mTotalMemory;  // on /data
99b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final StatFs mDataFileStats;
100b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final StatFs mSystemFileStats;
101b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final StatFs mCacheFileStats;
102be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
103be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey    private static final File DATA_PATH = Environment.getDataDirectory();
104be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey    private static final File SYSTEM_PATH = Environment.getRootDirectory();
105be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey    private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
106be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
1073161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker    private long mThreadStartTime = -1;
108182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    boolean mClearSucceeded = false;
109182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    boolean mClearingCache;
110b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final Intent mStorageLowIntent;
111b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final Intent mStorageOkIntent;
112b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final Intent mStorageFullIntent;
113b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final Intent mStorageNotFullIntent;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private CachePackageDataObserver mClearCacheObserver;
115182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private CacheFileDeletedObserver mCacheFileDeletedObserver;
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int _TRUE = 1;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int _FALSE = 0;
118197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // This is the raw threshold that has been set at which we consider
119197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // storage to be low.
120182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    long mMemLowThreshold;
121197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // This is the threshold at which we start trying to flush caches
122197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // to get below the low threshold limit.  It is less than the low
123197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // threshold; we will allow storage to get a bit beyond the limit
124197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // before flushing and checking if we are actually low.
125197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    private long mMemCacheStartTrimThreshold;
126197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // This is the threshold that we try to get to when deleting cache
127197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // files.  This is greater than the low threshold so that we will flush
128197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // more files than absolutely needed, to reduce the frequency that
129197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    // flushing takes place.
130197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn    private long mMemCacheTrimToThreshold;
131be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey    private long mMemFullThreshold;
1323161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This string is used for ServiceManager access to this class.
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
136182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    static final String SERVICE = "devicestoragemonitor";
1373161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1393161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker    * Handler that checks the amount of disk space on the device and sends a
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    * notification if the device runs low on disk space
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    */
142b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    private final Handler mHandler = new Handler() {
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleMessage(Message msg) {
145bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby            //don't handle an invalid message
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (msg.what != DEVICE_MEMORY_WHAT) {
1478a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                Slog.e(TAG, "Will not process invalid message");
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            checkMemory(msg.arg1 == _TRUE);
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1533161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
154182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private class CachePackageDataObserver extends IPackageDataObserver.Stub {
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onRemoveCompleted(String packageName, boolean succeeded) {
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearSucceeded = succeeded;
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearingCache = false;
1588a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    +", mClearingCache:"+mClearingCache+" Forcing memory check");
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            postCheckMemoryMsg(false, 0);
1613161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        }
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1633161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
164182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private void restatDataDir() {
1653161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        try {
166be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey            mDataFileStats.restat(DATA_PATH.getAbsolutePath());
1673161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
1683161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                mDataFileStats.getBlockSize();
1693161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        } catch (IllegalArgumentException e) {
1703161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            // use the old value of mFreeMem
1713161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Allow freemem to be overridden by debug.freemem for testing
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String debugFreeMem = SystemProperties.get("debug.freemem");
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!"".equals(debugFreeMem)) {
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mFreeMem = Long.parseLong(debugFreeMem);
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17743866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker        // Read the log interval from secure settings
178be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey        long freeMemLogInterval = Settings.Global.getLong(mResolver,
179625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //log the amount of free memory in event log
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long currTime = SystemClock.elapsedRealtime();
1833161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        if((mLastReportedFreeMemTime == 0) ||
1843161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker           (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastReportedFreeMemTime = currTime;
1863161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            long mFreeSystem = -1, mFreeCache = -1;
1873161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            try {
188be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey                mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
1893161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
1903161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                    mSystemFileStats.getBlockSize();
1913161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            } catch (IllegalArgumentException e) {
1923161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                // ignore; report -1
1933161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            }
1943161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            try {
195be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey                mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
1963161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
1973161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                    mCacheFileStats.getBlockSize();
1983161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            } catch (IllegalArgumentException e) {
1993161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                // ignore; report -1
2003161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            }
201ab5c49c7e7f5f61040650109a76f38a443fb852dDoug Zongker            EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
2023161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker                                mFreeMem, mFreeSystem, mFreeCache);
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20443866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker        // Read the reporting threshold from secure settings
205be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey        long threshold = Settings.Global.getLong(mResolver,
206625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // If mFree changed significantly log the new value
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long delta = mFreeMem - mLastReportedFreeMem;
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (delta > threshold || delta < -threshold) {
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastReportedFreeMem = mFreeMem;
212ab5c49c7e7f5f61040650109a76f38a443fb852dDoug Zongker            EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2153161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
216182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private void clearCache() {
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mClearCacheObserver == null) {
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Lazy instantiation
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearCacheObserver = new CachePackageDataObserver();
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mClearingCache = true;
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
2238a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            if (localLOGV) Slog.i(TAG, "Clearing cache");
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
225529f91fc8e7e884ef19bef8eb3e4e3a1d69336f4Jeff Sharkey                    freeStorageAndNotify(null, mMemCacheTrimToThreshold, mClearCacheObserver);
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
2278a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearingCache = false;
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearSucceeded = false;
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2323161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
233182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    void checkMemory(boolean checkCache) {
2343161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        //if the thread that was started to clear cache is still running do nothing till its
2353161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        //finished clearing cache. Ideally this flag could be modified by clearCache
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // and should be accessed via a lock but even if it does this test will fail now and
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //hopefully the next time this flag will be set to the correct value.
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if(mClearingCache) {
2398a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            if(localLOGV) Slog.i(TAG, "Thread already running just skip");
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //make sure the thread is not hung for too long
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long diffTime = System.currentTimeMillis() - mThreadStartTime;
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if(diffTime > (10*60*1000)) {
2438a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                Slog.w(TAG, "Thread that clears cache file seems to run for ever");
2443161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker            }
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            restatDataDir();
2478a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            if (localLOGV)  Slog.v(TAG, "freeMemory="+mFreeMem);
2483161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //post intent to NotificationManager to display icon if necessary
250bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby            if (mFreeMem < mMemLowThreshold) {
251197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                if (checkCache) {
252197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    // We are allowed to clear cache files at this point to
253197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    // try to get down below the limit, because this is not
254197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    // the initial call after a cache clear has been attempted.
255197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    // In this case we will try a cache clear if our free
256197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    // space has gone below the cache clear limit.
257197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    if (mFreeMem < mMemCacheStartTrimThreshold) {
258197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        // We only clear the cache if the free storage has changed
259197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        // a significant amount since the last time.
260197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        if ((mFreeMemAfterLastCacheClear-mFreeMem)
261197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                                >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
262197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                            // See if clearing cache helps
263197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                            // Note that clearing cache is asynchronous and so we do a
264197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                            // memory check again once the cache has been cleared.
265197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                            mThreadStartTime = System.currentTimeMillis();
266197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                            mClearSucceeded = false;
267197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                            clearCache();
268197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        }
269197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    }
270197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                } else {
271197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    // This is a call from after clearing the cache.  Note
272197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    // the amount of free storage at this point.
273197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    mFreeMemAfterLastCacheClear = mFreeMem;
274197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    if (!mLowMemFlag) {
275197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        // We tried to clear the cache, but that didn't get us
276197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        // below the low storage limit.  Tell the user.
2778a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                        Slog.i(TAG, "Running low on memory. Sending notification");
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sendNotification();
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mLowMemFlag = true;
280197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                    } else {
281197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        if (localLOGV) Slog.v(TAG, "Running low on memory " +
282197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                                "notification already sent. do nothing");
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
286197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                mFreeMemAfterLastCacheClear = mFreeMem;
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mLowMemFlag) {
2888a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.i(TAG, "Memory available. Cancelling notification");
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    cancelNotification();
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mLowMemFlag = false;
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
293a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom            if (!mLowMemFlag && !mIsBootImageOnDisk) {
294a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                Slog.i(TAG, "No boot image on disk due to lack of space. Sending notification");
295a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                sendNotification();
296a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom            }
297bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby            if (mFreeMem < mMemFullThreshold) {
298bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                if (!mMemFullFlag) {
299bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                    sendFullNotification();
300bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                    mMemFullFlag = true;
301bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                }
302bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby            } else {
303bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                if (mMemFullFlag) {
304bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                    cancelFullNotification();
305bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                    mMemFullFlag = false;
306bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby                }
307bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby            }
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3098a9b22056b13477f59df934928c00c58b5871c95Joe Onorato        if(localLOGV) Slog.i(TAG, "Posting Message again");
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //keep posting messages to itself periodically
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3133161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
314182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    void postCheckMemoryMsg(boolean clearCache, long delay) {
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Remove queued messages
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mHandler.removeMessages(DEVICE_MEMORY_WHAT);
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                clearCache ?_TRUE : _FALSE, 0),
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                delay);
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3213161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
322b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    public DeviceStorageMonitorService(Context context) {
323b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        super(context);
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLastReportedFreeMemTime = 0;
325182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        mResolver = context.getContentResolver();
326a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        mIsBootImageOnDisk = isBootImageOnDisk();
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //create StatFs object
328be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey        mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
329be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey        mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
330be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey        mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //initialize total storage on device
332197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn        mTotalMemory = (long)mDataFileStats.getBlockCount() *
333197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                        mDataFileStats.getBlockSize();
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
3354b3309211ed6f3532b44481945a4725143d50b8aJeff Hamilton        mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
3374b3309211ed6f3532b44481945a4725143d50b8aJeff Hamilton        mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
338bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby        mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
339bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby        mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
340bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby        mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
341bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby        mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
342182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
343be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
344a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom    private static boolean isBootImageOnDisk() {
345eeea67b8c3678d882d3774edc41242c63daa60faFyodor Kupolov        for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
346a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom            if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
347a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                return false;
348a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom            }
349a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        }
350a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        return true;
351a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom    }
352a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom
353b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    /**
354b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    * Initializes the disk space threshold value and posts an empty message to
355b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    * kickstart the process.
356b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown    */
357182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    @Override
358182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    public void onStart() {
359bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby        // cache storage thresholds
360182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        final StorageManager sm = StorageManager.from(getContext());
361be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey        mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
362be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey        mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
363be72215c39916af9ae42332260c04b696bc73d7fJeff Sharkey
364197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn        mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
365197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn        mMemCacheTrimToThreshold = mMemLowThreshold
366197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn                + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
367197a0c82a1fbf337ec0a85d36b6b89c3d6e8a0acDianne Hackborn        mFreeMemAfterLastCacheClear = mTotalMemory;
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        checkMemory(true);
3694b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey
3704b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        mCacheFileDeletedObserver = new CacheFileDeletedObserver();
3714b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        mCacheFileDeletedObserver.startWatching();
372182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
373182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        publishBinderService(SERVICE, mRemoteService);
374182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
375182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
376182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
377182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
378182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
379182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        public void checkMemory() {
380182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            // force an early check
381182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            postCheckMemoryMsg(true, 0);
382182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
383182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
384182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
385182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        public boolean isMemoryLow() {
386a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom            return mLowMemFlag || !mIsBootImageOnDisk;
387182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
388182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
389182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
390182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        public long getMemoryLowThreshold() {
391182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            return mMemLowThreshold;
392182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
393182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    };
394182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
395182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private final IBinder mRemoteService = new Binder() {
396182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        @Override
397182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
398182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
399182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                    != PackageManager.PERMISSION_GRANTED) {
400182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
401182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                pw.println("Permission Denial: can't dump " + SERVICE + " from from pid="
402182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                        + Binder.getCallingPid()
403182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                        + ", uid=" + Binder.getCallingUid());
404182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                return;
405182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            }
406182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
407182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            dumpImpl(pw);
408182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
409182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    };
410182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
411182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    void dumpImpl(PrintWriter pw) {
412182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        final Context context = getContext();
413182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
414182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.println("Current DeviceStorageMonitor state:");
415182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
416182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print("  mFreeMem="); pw.print(Formatter.formatFileSize(context, mFreeMem));
417182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(" mTotalMemory=");
418182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.println(Formatter.formatFileSize(context, mTotalMemory));
419182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
420182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print("  mFreeMemAfterLastCacheClear=");
421182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear));
422182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
423182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print("  mLastReportedFreeMem=");
424182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem));
425182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(" mLastReportedFreeMemTime=");
426182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
427182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.println();
428182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
429182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print("  mLowMemFlag="); pw.print(mLowMemFlag);
430182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(" mMemFullFlag="); pw.println(mMemFullFlag);
431a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        pw.print(" mIsBootImageOnDisk="); pw.print(mIsBootImageOnDisk);
432182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
433182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print("  mClearSucceeded="); pw.print(mClearSucceeded);
434182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(" mClearingCache="); pw.println(mClearingCache);
435182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
436182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print("  mMemLowThreshold=");
437182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(Formatter.formatFileSize(context, mMemLowThreshold));
438182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(" mMemFullThreshold=");
439182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.println(Formatter.formatFileSize(context, mMemFullThreshold));
440182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
441182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print("  mMemCacheStartTrimThreshold=");
442182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold));
443182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.print(" mMemCacheTrimToThreshold=");
444182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold));
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4463161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    * This method sends a notification to NotificationManager to display
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    * an error dialog indicating low disk space and launch the Installer
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    * application
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    */
452182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private void sendNotification() {
453182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        final Context context = getContext();
4548a9b22056b13477f59df934928c00c58b5871c95Joe Onorato        if(localLOGV) Slog.i(TAG, "Sending low memory notification");
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //log the event to event log with the amount of free storage(in bytes) left on the device
456ab5c49c7e7f5f61040650109a76f38a443fb852dDoug Zongker        EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //  Pack up the values and broadcast them to everyone
458f882efadd378e0476b7362e474f3a20dcf1c0d27Dianne Hackborn        Intent lowMemIntent = new Intent(Environment.isExternalStorageEmulated()
459f882efadd378e0476b7362e474f3a20dcf1c0d27Dianne Hackborn                ? Settings.ACTION_INTERNAL_STORAGE_SETTINGS
460f882efadd378e0476b7362e474f3a20dcf1c0d27Dianne Hackborn                : Intent.ACTION_MANAGE_PACKAGE_STORAGE);
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lowMemIntent.putExtra("memory", mFreeMem);
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4633161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker        NotificationManager mNotificationMgr =
464182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                (NotificationManager)context.getSystemService(
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Context.NOTIFICATION_SERVICE);
466182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        CharSequence title = context.getText(
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                com.android.internal.R.string.low_internal_storage_view_title);
468a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        CharSequence details = context.getText(mIsBootImageOnDisk
469a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                ? com.android.internal.R.string.low_internal_storage_view_text
470a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
471182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        PendingIntent intent = PendingIntent.getActivityAsUser(context, 0,  lowMemIntent, 0,
47250cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackborn                null, UserHandle.CURRENT);
473a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom        Notification notification = new Notification.Builder(context)
474a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
475a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setTicker(title)
4764a357cd2e55293402d7172766f7f9419815fc1e8Alan Viverette                .setColor(context.getColor(
477a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                    com.android.internal.R.color.system_notification_accent_color))
478a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setContentTitle(title)
479a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setContentText(details)
480a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setContentIntent(intent)
481a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setStyle(new Notification.BigTextStyle()
482a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                      .bigText(details))
483a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setVisibility(Notification.VISIBILITY_PUBLIC)
484a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .setCategory(Notification.CATEGORY_SYSTEM)
485a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                .build();
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        notification.flags |= Notification.FLAG_NO_CLEAR;
48750cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackborn        mNotificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
48850cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackborn                UserHandle.ALL);
489182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        context.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Cancels low storage notification and sends OK intent.
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
495182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private void cancelNotification() {
496182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        final Context context = getContext();
4978a9b22056b13477f59df934928c00c58b5871c95Joe Onorato        if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        NotificationManager mNotificationMgr =
499182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                (NotificationManager)context.getSystemService(
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Context.NOTIFICATION_SERVICE);
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //cancel notification since memory has been freed
50250cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackborn        mNotificationMgr.cancelAsUser(null, LOW_MEMORY_NOTIFICATION_ID, UserHandle.ALL);
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
504182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
505182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        context.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5073161795b2353171bb0636fb3ea6dab7dec80a4f4Doug Zongker
508bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby    /**
509bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby     * Send a notification when storage is full.
510bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby     */
511182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private void sendFullNotification() {
512bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby        if(localLOGV) Slog.i(TAG, "Sending memory full notification");
513182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
514bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby    }
515bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby
516bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby    /**
517bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby     * Cancels memory full notification and sends "not full" intent.
518bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby     */
519182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private void cancelFullNotification() {
520bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby        if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
521182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
522182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
523bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby    }
524bb3716332321e22537a5015be13e2229fb9b90bcJake Hamby
525182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private static class CacheFileDeletedObserver extends FileObserver {
5264b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        public CacheFileDeletedObserver() {
5274b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey            super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE);
5284b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        }
5294b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey
5304b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        @Override
5314b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        public void onEvent(int event, String path) {
5324b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey            EventLogTags.writeCacheFileDeleted(path);
5334b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey        }
5344b49657c7f78f8cee30804f8b31a004a11fffd7fJeff Sharkey    }
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
536