Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 package org.mozilla.gecko.background.healthreport.prune;
7 import org.mozilla.gecko.background.common.log.Logger;
8 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
10 import android.content.SharedPreferences;
12 /**
13 * Manages scheduling of the pruning of old Firefox Health Report data.
14 *
15 * There are three main actions that take place:
16 * 1) Excessive storage pruning: The recorded data is taking up an unreasonable amount of space.
17 * 2) Expired data pruning: Data that is kept around longer than is useful.
18 * 3) Cleanup: To deal with storage maintenance (e.g. bloat and fragmentation)
19 *
20 * (1) and (2) are performed periodically on their own schedules. (3) will activate after a
21 * certain duration but only after (1) or (2) is performed.
22 */
23 public class PrunePolicy {
24 public static final String LOG_TAG = PrunePolicy.class.getSimpleName();
26 protected final PrunePolicyStorage storage;
27 protected final SharedPreferences sharedPreferences;
28 protected final Editor editor;
30 public PrunePolicy(final PrunePolicyStorage storage, final SharedPreferences sharedPrefs) {
31 this.storage = storage;
32 this.sharedPreferences = sharedPrefs;
33 this.editor = new Editor(this.sharedPreferences.edit());
34 }
36 protected SharedPreferences getSharedPreferences() {
37 return this.sharedPreferences;
38 }
40 public void tick(final long time) {
41 try {
42 try {
43 boolean pruned = attemptPruneBySize(time);
44 pruned = attemptExpiration(time) ? true : pruned;
45 // We only need to cleanup after a large pruning.
46 if (pruned) {
47 attemptStorageCleanup(time);
48 }
49 } catch (Exception e) {
50 // While catching Exception is ordinarily bad form, this Service runs in the same process
51 // as Fennec so if we crash, it crashes. Additionally, this Service runs regularly so
52 // these crashes could be regular. Thus, we choose to quietly fail instead.
53 Logger.error(LOG_TAG, "Got exception pruning document.", e);
54 } finally {
55 editor.commit();
56 }
57 } catch (Exception e) {
58 Logger.error(LOG_TAG, "Got exception committing to SharedPreferences.", e);
59 } finally {
60 storage.close();
61 }
62 }
64 protected boolean attemptPruneBySize(final long time) {
65 final long nextPrune = getNextPruneBySizeTime();
66 if (nextPrune < 0) {
67 Logger.debug(LOG_TAG, "Initializing prune-by-size time.");
68 editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
69 return false;
70 }
72 // If the system clock is skewed into the past, making the time between prunes too long, reset
73 // the clock.
74 if (nextPrune > getMinimumTimeBetweenPruneBySizeChecks() + time) {
75 Logger.debug(LOG_TAG, "Clock skew detected - resetting prune-by-size time.");
76 editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
77 return false;
78 }
80 if (nextPrune > time) {
81 Logger.debug(LOG_TAG, "Skipping prune-by-size - wait period has not yet elapsed.");
82 return false;
83 }
85 Logger.debug(LOG_TAG, "Attempting prune-by-size.");
87 // Prune environments first because their cascading deletions may delete some events. These
88 // environments are pruned in order of least-recently used first. Note that orphaned
89 // environments are ignored here and should be removed elsewhere.
90 final int environmentCount = storage.getEnvironmentCount();
91 if (environmentCount > getMaxEnvironmentCount()) {
92 final int environmentPruneCount = environmentCount - getEnvironmentCountAfterPrune();
93 Logger.debug(LOG_TAG, "Pruning " + environmentPruneCount + " environments.");
94 storage.pruneEnvironments(environmentPruneCount);
95 }
97 final int eventCount = storage.getEventCount();
98 if (eventCount > getMaxEventCount()) {
99 final int eventPruneCount = eventCount - getEventCountAfterPrune();
100 Logger.debug(LOG_TAG, "Pruning up to " + eventPruneCount + " events.");
101 storage.pruneEvents(eventPruneCount);
102 }
103 editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
104 return true;
105 }
107 protected boolean attemptExpiration(final long time) {
108 final long nextPrune = getNextExpirationTime();
109 if (nextPrune < 0) {
110 Logger.debug(LOG_TAG, "Initializing expiration time.");
111 editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
112 return false;
113 }
115 // If the system clock is skewed into the past, making the time between prunes too long, reset
116 // the clock.
117 if (nextPrune > getMinimumTimeBetweenExpirationChecks() + time) {
118 Logger.debug(LOG_TAG, "Clock skew detected - resetting expiration time.");
119 editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
120 return false;
121 }
123 if (nextPrune > time) {
124 Logger.debug(LOG_TAG, "Skipping expiration - wait period has not yet elapsed.");
125 return false;
126 }
128 final long oldEventTime = time - getEventExistenceDuration();
129 Logger.debug(LOG_TAG, "Pruning data older than " + oldEventTime + ".");
130 storage.deleteDataBefore(oldEventTime);
131 editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
132 return true;
133 }
135 protected boolean attemptStorageCleanup(final long time) {
136 // Cleanup if max duration since last cleanup is exceeded.
137 final long nextCleanup = getNextCleanupTime();
138 if (nextCleanup < 0) {
139 Logger.debug(LOG_TAG, "Initializing cleanup time.");
140 editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
141 return false;
142 }
144 // If the system clock is skewed into the past, making the time between cleanups too long,
145 // reset the clock.
146 if (nextCleanup > getMinimumTimeBetweenCleanupChecks() + time) {
147 Logger.debug(LOG_TAG, "Clock skew detected - resetting cleanup time.");
148 editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
149 return false;
150 }
152 if (nextCleanup > time) {
153 Logger.debug(LOG_TAG, "Skipping cleanup - wait period has not yet elapsed.");
154 return false;
155 }
157 editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
158 Logger.debug(LOG_TAG, "Cleaning up storage.");
159 storage.cleanup();
160 return true;
161 }
163 protected static class Editor {
164 protected final SharedPreferences.Editor editor;
166 public Editor(final SharedPreferences.Editor editor) {
167 this.editor = editor;
168 }
170 public void commit() {
171 editor.commit();
172 }
174 public Editor setNextExpirationTime(final long time) {
175 editor.putLong(HealthReportConstants.PREF_EXPIRATION_TIME, time);
176 return this;
177 }
179 public Editor setNextPruneBySizeTime(final long time) {
180 editor.putLong(HealthReportConstants.PREF_PRUNE_BY_SIZE_TIME, time);
181 return this;
182 }
184 public Editor setNextCleanupTime(final long time) {
185 editor.putLong(HealthReportConstants.PREF_CLEANUP_TIME, time);
186 return this;
187 }
188 }
190 private long getNextExpirationTime() {
191 return getSharedPreferences().getLong(HealthReportConstants.PREF_EXPIRATION_TIME, -1L);
192 }
194 private long getEventExistenceDuration() {
195 return HealthReportConstants.EVENT_EXISTENCE_DURATION;
196 }
198 private long getMinimumTimeBetweenExpirationChecks() {
199 return HealthReportConstants.MINIMUM_TIME_BETWEEN_EXPIRATION_CHECKS_MILLIS;
200 }
202 private long getNextPruneBySizeTime() {
203 return getSharedPreferences().getLong(HealthReportConstants.PREF_PRUNE_BY_SIZE_TIME, -1L);
204 }
206 private long getMinimumTimeBetweenPruneBySizeChecks() {
207 return HealthReportConstants.MINIMUM_TIME_BETWEEN_PRUNE_BY_SIZE_CHECKS_MILLIS;
208 }
210 private int getMaxEnvironmentCount() {
211 return HealthReportConstants.MAX_ENVIRONMENT_COUNT;
212 }
214 private int getEnvironmentCountAfterPrune() {
215 return HealthReportConstants.ENVIRONMENT_COUNT_AFTER_PRUNE;
216 }
218 private int getMaxEventCount() {
219 return HealthReportConstants.MAX_EVENT_COUNT;
220 }
222 private int getEventCountAfterPrune() {
223 return HealthReportConstants.EVENT_COUNT_AFTER_PRUNE;
224 }
226 private long getNextCleanupTime() {
227 return getSharedPreferences().getLong(HealthReportConstants.PREF_CLEANUP_TIME, -1L);
228 }
230 private long getMinimumTimeBetweenCleanupChecks() {
231 return HealthReportConstants.MINIMUM_TIME_BETWEEN_CLEANUP_CHECKS_MILLIS;
232 }
233 }