179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu/*
279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * Copyright (C) 2009 The Android Open Source Project
379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * Licensed under the Apache License, Version 2.0 (the "License");
579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * you may not use this file except in compliance with the License.
679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * You may obtain a copy of the License at
779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *      http://www.apache.org/licenses/LICENSE-2.0
979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
1079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * Unless required by applicable law or agreed to in writing, software
1179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * distributed under the License is distributed on an "AS IS" BASIS,
1279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * See the License for the specific language governing permissions and
1479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * limitations under the License.
1579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu */
1679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
1779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescupackage com.android.browser;
1879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
1910200795554ffeb16bf5662fd66ae6442c6f5cf0John Reckimport com.android.browser.preferences.WebsiteSettingsFragment;
2010200795554ffeb16bf5662fd66ae6442c6f5cf0John Reck
2186943777260a399df282517241085d0c5e10ee88Andrei Popescuimport android.app.Notification;
2286943777260a399df282517241085d0c5e10ee88Andrei Popescuimport android.app.NotificationManager;
2386943777260a399df282517241085d0c5e10ee88Andrei Popescuimport android.app.PendingIntent;
2479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescuimport android.content.Context;
2586943777260a399df282517241085d0c5e10ee88Andrei Popescuimport android.content.Intent;
2679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescuimport android.os.StatFs;
2710200795554ffeb16bf5662fd66ae6442c6f5cf0John Reckimport android.preference.PreferenceActivity;
2879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescuimport android.util.Log;
2979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescuimport android.webkit.WebStorage;
3079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
3179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescuimport java.io.File;
3279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
3379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
3479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu/**
3579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * Package level class for managing the disk size consumed by the WebDatabase
3679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * and ApplicationCaches APIs (henceforth called Web storage).
3779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
3879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * Currently, the situation on the WebKit side is as follows:
3979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *  - WebDatabase enforces a quota for each origin.
4079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *  - Session/LocalStorage do not enforce any disk limits.
4179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *  - ApplicationCaches enforces a maximum size for all origins.
4279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
4379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * The WebStorageSizeManager maintains a global limit for the disk space
4479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * consumed by the WebDatabase and ApplicationCaches. As soon as WebKit will
4579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * have a limit for Session/LocalStorage, this class will manage the space used
4679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * by those APIs as well.
4779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
4879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * The global limit is computed as a function of the size of the partition where
4979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * these APIs store their data (they must store it on the same partition for
5079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * this to work) and the size of the available space on that partition.
5179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * The global limit is not subject to user configuration but we do provide
5279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * a debug-only setting.
5379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * TODO(andreip): implement the debug setting.
5479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
5579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * The size of the disk space used for Web storage is initially divided between
5679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * WebDatabase and ApplicationCaches as follows:
5779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
5879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * 75% for WebDatabase
5979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * 25% for ApplicationCaches
6079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
6179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * When an origin's database usage reaches its current quota, WebKit invokes
6279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * the following callback function:
6379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * - exceededDatabaseQuota(Frame* frame, const String& database_name);
6479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * Note that the default quota for a new origin is 0, so we will receive the
6579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * 'exceededDatabaseQuota' callback before a new origin gets the chance to
6679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * create its first database.
6779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
6879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * When the total ApplicationCaches usage reaches its current quota, WebKit
6979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * invokes the following callback function:
7079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * - void reachedMaxAppCacheSize(int64_t spaceNeeded);
7179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
7279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * The WebStorageSizeManager's main job is to respond to the above two callbacks
7379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * by inspecting the amount of unused Web storage quota (i.e. global limit -
7479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * sum of all other origins' quota) and deciding if a quota increase for the
7579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * out-of-space origin is allowed or not.
7679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
7725a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch * The default quota for an origin is its estimated size. If we cannot satisfy
7825a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch * the estimated size, then WebCore will not create the database.
7979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * Quota increases are done in steps, where the increase step is
8079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * min(QUOTA_INCREASE_STEP, unused_quota).
8179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu *
8279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * When all the Web storage space is used, the WebStorageSizeManager creates
8379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * a system notification that will guide the user to the WebSettings UI. There,
8479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * the user can free some of the Web storage space by deleting all the data used
8579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu * by an origin.
8679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu */
8710200795554ffeb16bf5662fd66ae6442c6f5cf0John Reckpublic class WebStorageSizeManager {
8879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // Logging flags.
8979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    private final static boolean LOGV_ENABLED = com.android.browser.Browser.LOGV_ENABLED;
9079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED;
9179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    private final static String LOGTAG = "browser";
9279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // The default quota value for an origin.
93907c5763bb700c705108948328c8892500e59185Andrei Popescu    public final static long ORIGIN_DEFAULT_QUOTA = 3 * 1024 * 1024;  // 3MB
9479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // The default value for quota increases.
95907c5763bb700c705108948328c8892500e59185Andrei Popescu    public final static long QUOTA_INCREASE_STEP = 1 * 1024 * 1024;  // 1MB
9686943777260a399df282517241085d0c5e10ee88Andrei Popescu    // Extra padding space for appcache maximum size increases. This is needed
9786943777260a399df282517241085d0c5e10ee88Andrei Popescu    // because WebKit sends us an estimate of the amount of space needed
9886943777260a399df282517241085d0c5e10ee88Andrei Popescu    // but this estimate may, currently, be slightly less than what is actually
9986943777260a399df282517241085d0c5e10ee88Andrei Popescu    // needed. We therefore add some 'padding'.
10086943777260a399df282517241085d0c5e10ee88Andrei Popescu    // TODO(andreip): fix this in WebKit.
10186943777260a399df282517241085d0c5e10ee88Andrei Popescu    public final static long APPCACHE_MAXSIZE_PADDING = 512 * 1024; // 512KB
10286943777260a399df282517241085d0c5e10ee88Andrei Popescu    // The system status bar notification id.
10386943777260a399df282517241085d0c5e10ee88Andrei Popescu    private final static int OUT_OF_SPACE_ID = 1;
104eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    // The time of the last out of space notification
105eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    private static long mLastOutOfSpaceNotificationTime = -1;
106eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    // Delay between two notification in ms
107eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    private final static long NOTIFICATION_INTERVAL = 5 * 60 * 1000;
108eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    // Delay in ms used when resetting the notification time
109eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    private final static long RESET_NOTIFICATION_INTERVAL = 3 * 1000;
11079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // The application context.
111907c5763bb700c705108948328c8892500e59185Andrei Popescu    private final Context mContext;
11279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // The global Web storage limit.
113907c5763bb700c705108948328c8892500e59185Andrei Popescu    private final long mGlobalLimit;
11479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // The maximum size of the application cache file.
11579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    private long mAppCacheMaxSize;
11679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
11779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    /**
118907c5763bb700c705108948328c8892500e59185Andrei Popescu     * Interface used by the WebStorageSizeManager to obtain information
119907c5763bb700c705108948328c8892500e59185Andrei Popescu     * about the underlying file system. This functionality is separated
120907c5763bb700c705108948328c8892500e59185Andrei Popescu     * into its own interface mainly for testing purposes.
12179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     */
122907c5763bb700c705108948328c8892500e59185Andrei Popescu    public interface DiskInfo {
123907c5763bb700c705108948328c8892500e59185Andrei Popescu        /**
124907c5763bb700c705108948328c8892500e59185Andrei Popescu         * @return the size of the free space in the file system.
125907c5763bb700c705108948328c8892500e59185Andrei Popescu         */
126907c5763bb700c705108948328c8892500e59185Andrei Popescu        public long getFreeSpaceSizeBytes();
127907c5763bb700c705108948328c8892500e59185Andrei Popescu
128907c5763bb700c705108948328c8892500e59185Andrei Popescu        /**
129907c5763bb700c705108948328c8892500e59185Andrei Popescu         * @return the total size of the file system.
130907c5763bb700c705108948328c8892500e59185Andrei Popescu         */
131907c5763bb700c705108948328c8892500e59185Andrei Popescu        public long getTotalSizeBytes();
132907c5763bb700c705108948328c8892500e59185Andrei Popescu    };
133907c5763bb700c705108948328c8892500e59185Andrei Popescu
134907c5763bb700c705108948328c8892500e59185Andrei Popescu    private DiskInfo mDiskInfo;
135907c5763bb700c705108948328c8892500e59185Andrei Popescu    // For convenience, we provide a DiskInfo implementation that uses StatFs.
136907c5763bb700c705108948328c8892500e59185Andrei Popescu    public static class StatFsDiskInfo implements DiskInfo {
137907c5763bb700c705108948328c8892500e59185Andrei Popescu        private StatFs mFs;
138907c5763bb700c705108948328c8892500e59185Andrei Popescu
139907c5763bb700c705108948328c8892500e59185Andrei Popescu        public StatFsDiskInfo(String path) {
140907c5763bb700c705108948328c8892500e59185Andrei Popescu            mFs = new StatFs(path);
141907c5763bb700c705108948328c8892500e59185Andrei Popescu        }
142907c5763bb700c705108948328c8892500e59185Andrei Popescu
143907c5763bb700c705108948328c8892500e59185Andrei Popescu        public long getFreeSpaceSizeBytes() {
144d90f9705d6962e809191c10c14f611f7212918faBen Murdoch            return (long)(mFs.getAvailableBlocks()) * mFs.getBlockSize();
145907c5763bb700c705108948328c8892500e59185Andrei Popescu        }
146907c5763bb700c705108948328c8892500e59185Andrei Popescu
147907c5763bb700c705108948328c8892500e59185Andrei Popescu        public long getTotalSizeBytes() {
148d90f9705d6962e809191c10c14f611f7212918faBen Murdoch            return (long)(mFs.getBlockCount()) * mFs.getBlockSize();
149907c5763bb700c705108948328c8892500e59185Andrei Popescu        }
150907c5763bb700c705108948328c8892500e59185Andrei Popescu    };
151907c5763bb700c705108948328c8892500e59185Andrei Popescu
152907c5763bb700c705108948328c8892500e59185Andrei Popescu    /**
153907c5763bb700c705108948328c8892500e59185Andrei Popescu     * Interface used by the WebStorageSizeManager to obtain information
154907c5763bb700c705108948328c8892500e59185Andrei Popescu     * about the appcache file. This functionality is separated into its own
155907c5763bb700c705108948328c8892500e59185Andrei Popescu     * interface mainly for testing purposes.
156907c5763bb700c705108948328c8892500e59185Andrei Popescu     */
157907c5763bb700c705108948328c8892500e59185Andrei Popescu    public interface AppCacheInfo {
158907c5763bb700c705108948328c8892500e59185Andrei Popescu        /**
159907c5763bb700c705108948328c8892500e59185Andrei Popescu         * @return the current size of the appcache file.
160907c5763bb700c705108948328c8892500e59185Andrei Popescu         */
161907c5763bb700c705108948328c8892500e59185Andrei Popescu        public long getAppCacheSizeBytes();
162907c5763bb700c705108948328c8892500e59185Andrei Popescu    };
163907c5763bb700c705108948328c8892500e59185Andrei Popescu
164907c5763bb700c705108948328c8892500e59185Andrei Popescu    // For convenience, we provide an AppCacheInfo implementation.
165907c5763bb700c705108948328c8892500e59185Andrei Popescu    public static class WebKitAppCacheInfo implements AppCacheInfo {
166907c5763bb700c705108948328c8892500e59185Andrei Popescu        // The name of the application cache file. Keep in sync with
167907c5763bb700c705108948328c8892500e59185Andrei Popescu        // WebCore/loader/appcache/ApplicationCacheStorage.cpp
168907c5763bb700c705108948328c8892500e59185Andrei Popescu        private final static String APPCACHE_FILE = "ApplicationCache.db";
169907c5763bb700c705108948328c8892500e59185Andrei Popescu        private String mAppCachePath;
170907c5763bb700c705108948328c8892500e59185Andrei Popescu
171907c5763bb700c705108948328c8892500e59185Andrei Popescu        public WebKitAppCacheInfo(String path) {
172907c5763bb700c705108948328c8892500e59185Andrei Popescu            mAppCachePath = path;
173907c5763bb700c705108948328c8892500e59185Andrei Popescu        }
174907c5763bb700c705108948328c8892500e59185Andrei Popescu
175907c5763bb700c705108948328c8892500e59185Andrei Popescu        public long getAppCacheSizeBytes() {
176907c5763bb700c705108948328c8892500e59185Andrei Popescu            File file = new File(mAppCachePath
177907c5763bb700c705108948328c8892500e59185Andrei Popescu                    + File.separator
178907c5763bb700c705108948328c8892500e59185Andrei Popescu                    + APPCACHE_FILE);
179907c5763bb700c705108948328c8892500e59185Andrei Popescu            return file.length();
180907c5763bb700c705108948328c8892500e59185Andrei Popescu        }
181907c5763bb700c705108948328c8892500e59185Andrei Popescu    };
182907c5763bb700c705108948328c8892500e59185Andrei Popescu
183907c5763bb700c705108948328c8892500e59185Andrei Popescu    /**
184907c5763bb700c705108948328c8892500e59185Andrei Popescu     * Public ctor
185907c5763bb700c705108948328c8892500e59185Andrei Popescu     * @param ctx is the application context
186907c5763bb700c705108948328c8892500e59185Andrei Popescu     * @param diskInfo is the DiskInfo instance used to query the file system.
187907c5763bb700c705108948328c8892500e59185Andrei Popescu     * @param appCacheInfo is the AppCacheInfo used to query info about the
188907c5763bb700c705108948328c8892500e59185Andrei Popescu     * appcache file.
189907c5763bb700c705108948328c8892500e59185Andrei Popescu     */
190907c5763bb700c705108948328c8892500e59185Andrei Popescu    public WebStorageSizeManager(Context ctx, DiskInfo diskInfo,
191907c5763bb700c705108948328c8892500e59185Andrei Popescu            AppCacheInfo appCacheInfo) {
192914c5591baeb86bf30a5bc28930071442a822d60Ben Murdoch        mContext = ctx.getApplicationContext();
193907c5763bb700c705108948328c8892500e59185Andrei Popescu        mDiskInfo = diskInfo;
194907c5763bb700c705108948328c8892500e59185Andrei Popescu        mGlobalLimit = getGlobalLimit();
195907c5763bb700c705108948328c8892500e59185Andrei Popescu        // The initial max size of the app cache is either 25% of the global
196907c5763bb700c705108948328c8892500e59185Andrei Popescu        // limit or the current size of the app cache file, whichever is bigger.
197907c5763bb700c705108948328c8892500e59185Andrei Popescu        mAppCacheMaxSize = Math.max(mGlobalLimit / 4,
198907c5763bb700c705108948328c8892500e59185Andrei Popescu                appCacheInfo.getAppCacheSizeBytes());
19979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    }
20079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
20179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    /**
20279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * Returns the maximum size of the application cache.
20379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     */
20479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    public long getAppCacheMaxSize() {
20579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        return mAppCacheMaxSize;
20679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    }
20779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
20879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    /**
20979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * The origin has exceeded its database quota.
21079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param url the URL that exceeded the quota
21179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param databaseIdentifier the identifier of the database on
21279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     *     which the transaction that caused the quota overflow was run
21379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param currentQuota the current quota for the origin.
21434b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch     * @param estimatedSize the estimated size of a new database, or 0 if
21534b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch     *     this has been invoked in response to an existing database
21634b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch     *     overflowing its quota.
21779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param totalUsedQuota is the sum of all origins' quota.
21879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param quotaUpdater The callback to run when a decision to allow or
21979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     *     deny quota has been made. Don't forget to call this!
22079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     */
22179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    public void onExceededDatabaseQuota(String url,
22225a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch        String databaseIdentifier, long currentQuota, long estimatedSize,
22325a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch        long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
22479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        if(LOGV_ENABLED) {
22579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            Log.v(LOGTAG,
22679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  "Received onExceededDatabaseQuota for "
22779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  + url
22879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  + ":"
22979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  + databaseIdentifier
23079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  + "(current quota: "
23179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  + currentQuota
232907c5763bb700c705108948328c8892500e59185Andrei Popescu                  + ", total used quota: "
233907c5763bb700c705108948328c8892500e59185Andrei Popescu                  + totalUsedQuota
23479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  + ")");
23579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
23679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        long totalUnusedQuota = mGlobalLimit - totalUsedQuota - mAppCacheMaxSize;
23779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
238907c5763bb700c705108948328c8892500e59185Andrei Popescu        if (totalUnusedQuota <= 0) {
23979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            // There definitely isn't any more space. Fire notifications
24086943777260a399df282517241085d0c5e10ee88Andrei Popescu            // if needed and exit.
24186943777260a399df282517241085d0c5e10ee88Andrei Popescu            if (totalUsedQuota > 0) {
24286943777260a399df282517241085d0c5e10ee88Andrei Popescu                // We only fire the notification if there are some other websites
24386943777260a399df282517241085d0c5e10ee88Andrei Popescu                // using some of the quota. This avoids the degenerate case where
24486943777260a399df282517241085d0c5e10ee88Andrei Popescu                // the first ever website to use Web storage tries to use more
24586943777260a399df282517241085d0c5e10ee88Andrei Popescu                // data than it is actually available. In such a case, showing
24686943777260a399df282517241085d0c5e10ee88Andrei Popescu                // the notification would not help at all since there is nothing
24786943777260a399df282517241085d0c5e10ee88Andrei Popescu                // the user can do.
24886943777260a399df282517241085d0c5e10ee88Andrei Popescu                scheduleOutOfSpaceNotification();
24986943777260a399df282517241085d0c5e10ee88Andrei Popescu            }
25079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            quotaUpdater.updateQuota(currentQuota);
25179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            if(LOGV_ENABLED) {
25279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                Log.v(LOGTAG, "onExceededDatabaseQuota: out of space.");
25379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            }
25479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            return;
25579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
25634b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch
25734b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch        // We have some space inside mGlobalLimit.
25879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        long newOriginQuota = currentQuota;
25979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        if (newOriginQuota == 0) {
26025a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch            // This is a new origin, give it the size it asked for if possible.
26125a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch            // If we cannot satisfy the estimatedSize, we should return 0 as
26225a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch            // returning a value less that what the site requested will lead
26325a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch            // to webcore not creating the database.
26425a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch            if (totalUnusedQuota >= estimatedSize) {
26525a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch                newOriginQuota = estimatedSize;
26625a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch            } else {
26725a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch                if (LOGV_ENABLED) {
26825a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch                    Log.v(LOGTAG,
26934b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                            "onExceededDatabaseQuota: Unable to satisfy" +
27034b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                            " estimatedSize for the new database " +
27134b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                            " (estimatedSize: " + estimatedSize +
27234b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                            ", unused quota: " + totalUnusedQuota);
27325a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch                }
27425a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch                newOriginQuota = 0;
27525a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch            }
27679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        } else {
27779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            // This is an origin we have seen before. It wants a quota
27834b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            // increase. There are two circumstances: either the origin
27934b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            // is creating a new database or it has overflowed an existing database.
28034b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch
28134b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            // Increase the quota. If estimatedSize == 0, then this is a quota overflow
28234b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            // rather than the creation of a new database.
28334b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            long quotaIncrease = estimatedSize == 0 ?
28434b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                    Math.min(QUOTA_INCREASE_STEP, totalUnusedQuota) :
28534b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                    estimatedSize;
28634b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            newOriginQuota += quotaIncrease;
28734b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch
28834b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            if (quotaIncrease > totalUnusedQuota) {
28934b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                // We can't fit, so deny quota.
29034b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch                newOriginQuota = currentQuota;
29134b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch            }
29279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
29334b89ef113c7e5e365f75ec692a98a5484834ca0Ben Murdoch
29479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        quotaUpdater.updateQuota(newOriginQuota);
29579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
29679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        if(LOGV_ENABLED) {
29779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            Log.v(LOGTAG, "onExceededDatabaseQuota set new quota to "
29879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                    + newOriginQuota);
29979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
30079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    }
30179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
30279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    /**
30379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * The Application Cache has exceeded its max size.
30479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param spaceNeeded is the amount of disk space that would be needed
30579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * in order for the last appcache operation to succeed.
30679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param totalUsedQuota is the sum of all origins' quota.
30779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * @param quotaUpdater A callback to inform the WebCore thread that a new
30879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * app cache size is available. This callback must always be executed at
30979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     * some point to ensure that the sleeping WebCore thread is woken up.
31079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu     */
31179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
31279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            WebStorage.QuotaUpdater quotaUpdater) {
31379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        if(LOGV_ENABLED) {
31479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            Log.v(LOGTAG, "Received onReachedMaxAppCacheSize with spaceNeeded "
31579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                  + spaceNeeded + " bytes.");
31679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
31779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
31879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        long totalUnusedQuota = mGlobalLimit - totalUsedQuota - mAppCacheMaxSize;
31979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
32086943777260a399df282517241085d0c5e10ee88Andrei Popescu        if (totalUnusedQuota < spaceNeeded + APPCACHE_MAXSIZE_PADDING) {
32179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            // There definitely isn't any more space. Fire notifications
32286943777260a399df282517241085d0c5e10ee88Andrei Popescu            // if needed and exit.
32386943777260a399df282517241085d0c5e10ee88Andrei Popescu            if (totalUsedQuota > 0) {
32486943777260a399df282517241085d0c5e10ee88Andrei Popescu                // We only fire the notification if there are some other websites
32586943777260a399df282517241085d0c5e10ee88Andrei Popescu                // using some of the quota. This avoids the degenerate case where
32686943777260a399df282517241085d0c5e10ee88Andrei Popescu                // the first ever website to use Web storage tries to use more
32786943777260a399df282517241085d0c5e10ee88Andrei Popescu                // data than it is actually available. In such a case, showing
32886943777260a399df282517241085d0c5e10ee88Andrei Popescu                // the notification would not help at all since there is nothing
32986943777260a399df282517241085d0c5e10ee88Andrei Popescu                // the user can do.
33086943777260a399df282517241085d0c5e10ee88Andrei Popescu                scheduleOutOfSpaceNotification();
33186943777260a399df282517241085d0c5e10ee88Andrei Popescu            }
33279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            quotaUpdater.updateQuota(0);
33379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            if(LOGV_ENABLED) {
33479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                Log.v(LOGTAG, "onReachedMaxAppCacheSize: out of space.");
33579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            }
33679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            return;
33779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
33879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        // There is enough space to accommodate spaceNeeded bytes.
33986943777260a399df282517241085d0c5e10ee88Andrei Popescu        mAppCacheMaxSize += spaceNeeded + APPCACHE_MAXSIZE_PADDING;
34079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        quotaUpdater.updateQuota(mAppCacheMaxSize);
34179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
34279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        if(LOGV_ENABLED) {
34379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            Log.v(LOGTAG, "onReachedMaxAppCacheSize set new max size to "
34479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                    + mAppCacheMaxSize);
34579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
34679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    }
34779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
348eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    // Reset the notification time; we use this iff the user
349eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    // use clear all; we reset it to some time in the future instead
350eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    // of just setting it to -1, as the clear all method is asynchronous
35110200795554ffeb16bf5662fd66ae6442c6f5cf0John Reck    public static void resetLastOutOfSpaceNotificationTime() {
352eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard        mLastOutOfSpaceNotificationTime = System.currentTimeMillis() -
353eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            NOTIFICATION_INTERVAL + RESET_NOTIFICATION_INTERVAL;
354eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard    }
355eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard
35679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // Computes the global limit as a function of the size of the data
35779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // partition and the amount of free space on that partition.
358907c5763bb700c705108948328c8892500e59185Andrei Popescu    private long getGlobalLimit() {
359907c5763bb700c705108948328c8892500e59185Andrei Popescu        long freeSpace = mDiskInfo.getFreeSpaceSizeBytes();
360907c5763bb700c705108948328c8892500e59185Andrei Popescu        long fileSystemSize = mDiskInfo.getTotalSizeBytes();
36179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        return calculateGlobalLimit(fileSystemSize, freeSpace);
36279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    }
36379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
36479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    /*package*/ static long calculateGlobalLimit(long fileSystemSizeBytes,
36579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            long freeSpaceBytes) {
36679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        if (fileSystemSizeBytes <= 0
36779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                || freeSpaceBytes <= 0
36879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                || freeSpaceBytes > fileSystemSizeBytes) {
36979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            return 0;
37079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
37179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
37279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        long fileSystemSizeRatio =
37379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            2 << ((int) Math.floor(Math.log10(
37479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                    fileSystemSizeBytes / (1024 * 1024))));
37579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        long maxSizeBytes = (long) Math.min(Math.floor(
37679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                fileSystemSizeBytes / fileSystemSizeRatio),
37779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                Math.floor(freeSpaceBytes / 2));
37879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        // Round maxSizeBytes up to a multiple of 1024KB (but only if
37979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        // maxSizeBytes > 1MB).
38079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        long maxSizeStepBytes = 1024 * 1024;
38179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        if (maxSizeBytes < maxSizeStepBytes) {
38279e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu            return 0;
38379e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        }
38479e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        long roundingExtra = maxSizeBytes % maxSizeStepBytes == 0 ? 0 : 1;
38579e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu        return (maxSizeStepBytes
38679e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu                * ((maxSizeBytes / maxSizeStepBytes) + roundingExtra));
38779e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    }
38879e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu
38979e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // Schedules a system notification that takes the user to the WebSettings
39079e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    // activity when clicked.
39179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    private void scheduleOutOfSpaceNotification() {
39286943777260a399df282517241085d0c5e10ee88Andrei Popescu        if(LOGV_ENABLED) {
39386943777260a399df282517241085d0c5e10ee88Andrei Popescu            Log.v(LOGTAG, "scheduleOutOfSpaceNotification called.");
39486943777260a399df282517241085d0c5e10ee88Andrei Popescu        }
395eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard        if ((mLastOutOfSpaceNotificationTime == -1) ||
396eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            (System.currentTimeMillis() - mLastOutOfSpaceNotificationTime > NOTIFICATION_INTERVAL)) {
397eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            // setup the notification boilerplate.
398eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            int icon = android.R.drawable.stat_sys_warning;
399eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            CharSequence title = mContext.getString(
400eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard                    R.string.webstorage_outofspace_notification_title);
401eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            CharSequence text = mContext.getString(
402eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard                    R.string.webstorage_outofspace_notification_text);
403eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            long when = System.currentTimeMillis();
40410200795554ffeb16bf5662fd66ae6442c6f5cf0John Reck            Intent intent = new Intent(mContext, BrowserPreferencesPage.class);
40510200795554ffeb16bf5662fd66ae6442c6f5cf0John Reck            intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT,
40610200795554ffeb16bf5662fd66ae6442c6f5cf0John Reck                    WebsiteSettingsFragment.class.getName());
407eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            PendingIntent contentIntent =
408eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard                PendingIntent.getActivity(mContext, 0, intent, 0);
409eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            Notification notification = new Notification(icon, title, when);
410eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            notification.setLatestEventInfo(mContext, title, text, contentIntent);
411eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            notification.flags |= Notification.FLAG_AUTO_CANCEL;
412eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            // Fire away.
413eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            String ns = Context.NOTIFICATION_SERVICE;
414eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            NotificationManager mgr =
415eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard                (NotificationManager) mContext.getSystemService(ns);
416eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            if (mgr != null) {
417eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard                mLastOutOfSpaceNotificationTime = System.currentTimeMillis();
418eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard                mgr.notify(OUT_OF_SPACE_ID, notification);
419eb9032c67077aa5aa6ddf928bf14a45534f24776Nicolas Roard            }
42086943777260a399df282517241085d0c5e10ee88Andrei Popescu        }
42179e82b7ba72a7278911edf0dd7b03c65c4ec0e9dAndrei Popescu    }
42225a1523642bead2f7e7f929ba9d6d1143dce06a0Ben Murdoch}
423