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