1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,136 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.background.healthreport.prune; 1.9 + 1.10 +import org.mozilla.gecko.background.common.log.Logger; 1.11 +import org.mozilla.gecko.background.healthreport.Environment; 1.12 +import org.mozilla.gecko.background.healthreport.EnvironmentBuilder; 1.13 +import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage; 1.14 +import org.mozilla.gecko.background.healthreport.ProfileInformationCache; 1.15 + 1.16 +import android.content.ContentProviderClient; 1.17 +import android.content.Context; 1.18 + 1.19 +/** 1.20 + * Abstracts over the Storage instance behind the PrunePolicy. The underlying storage instance is 1.21 + * a {@link HealthReportDatabaseStorage} instance. Since our cleanup routine vacuums, auto_vacuum 1.22 + * can be disabled. It is enabled by default, however, turning it off requires an expensive vacuum 1.23 + * so we wait until our first {@link cleanup} call since we are vacuuming anyway. 1.24 + */ 1.25 +public class PrunePolicyDatabaseStorage implements PrunePolicyStorage { 1.26 + public static final String LOG_TAG = PrunePolicyDatabaseStorage.class.getSimpleName(); 1.27 + 1.28 + private final Context context; 1.29 + private final String profilePath; 1.30 + 1.31 + private ContentProviderClient client; 1.32 + private HealthReportDatabaseStorage storage; 1.33 + 1.34 + private int currentEnvironmentID; // So we don't prune the current environment. 1.35 + 1.36 + public PrunePolicyDatabaseStorage(final Context context, final String profilePath) { 1.37 + this.context = context; 1.38 + this.profilePath = profilePath; 1.39 + 1.40 + this.currentEnvironmentID = -1; 1.41 + } 1.42 + 1.43 + public void pruneEvents(final int count) { 1.44 + getStorage().pruneEvents(count); 1.45 + } 1.46 + 1.47 + public void pruneEnvironments(final int count) { 1.48 + getStorage().pruneEnvironments(count); 1.49 + 1.50 + // Re-populate the DB and environment cache with the current environment in the unlikely event 1.51 + // that it was deleted. 1.52 + this.currentEnvironmentID = -1; 1.53 + getCurrentEnvironmentID(); 1.54 + } 1.55 + 1.56 + /** 1.57 + * Deletes data recorded before the given time. Note that if this method fails to retrieve the 1.58 + * current environment from the profile cache, it will not delete data so be sure to prune by 1.59 + * other methods (e.g. {@link pruneEvents}) as well. 1.60 + */ 1.61 + public int deleteDataBefore(final long time) { 1.62 + return getStorage().deleteDataBefore(time, getCurrentEnvironmentID()); 1.63 + } 1.64 + 1.65 + public void cleanup() { 1.66 + final HealthReportDatabaseStorage storage = getStorage(); 1.67 + // The change to auto_vacuum will only take affect after a vacuum. 1.68 + storage.disableAutoVacuuming(); 1.69 + storage.vacuum(); 1.70 + } 1.71 + 1.72 + public int getEventCount() { 1.73 + return getStorage().getEventCount(); 1.74 + } 1.75 + 1.76 + public int getEnvironmentCount() { 1.77 + return getStorage().getEnvironmentCount(); 1.78 + } 1.79 + 1.80 + public void close() { 1.81 + if (client != null) { 1.82 + client.release(); 1.83 + client = null; 1.84 + } 1.85 + } 1.86 + 1.87 + /** 1.88 + * Retrieves the {@link HealthReportDatabaseStorage} associated with the profile of the policy. 1.89 + * For efficiency, the underlying {@link ContentProviderClient} and 1.90 + * {@link HealthReportDatabaseStorage} are cached for later invocations. However, this means a 1.91 + * call to this method MUST be accompanied by a call to {@link close}. Throws 1.92 + * {@link IllegalStateException} if the storage instance could not be retrieved - note that the 1.93 + * {@link ContentProviderClient} instance will not be closed in this case and 1.94 + * {@link releaseClient} should still be called. 1.95 + */ 1.96 + protected HealthReportDatabaseStorage getStorage() { 1.97 + if (storage != null) { 1.98 + return storage; 1.99 + } 1.100 + 1.101 + client = EnvironmentBuilder.getContentProviderClient(context); 1.102 + if (client == null) { 1.103 + // TODO: Record prune failures and submit as part of FHR upload. 1.104 + Logger.warn(LOG_TAG, "Unable to get ContentProviderClient - throwing."); 1.105 + throw new IllegalStateException("Unable to get ContentProviderClient."); 1.106 + } 1.107 + 1.108 + try { 1.109 + storage = EnvironmentBuilder.getStorage(client, profilePath); 1.110 + if (storage == null) { 1.111 + // TODO: Record prune failures and submit as part of FHR upload. 1.112 + Logger.warn(LOG_TAG,"Unable to get HealthReportDatabaseStorage for " + profilePath + 1.113 + " - throwing."); 1.114 + throw new IllegalStateException("Unable to get HealthReportDatabaseStorage for " + 1.115 + profilePath + " (== null)."); 1.116 + } 1.117 + } catch (ClassCastException ex) { 1.118 + // TODO: Record prune failures and submit as part of FHR upload. 1.119 + Logger.warn(LOG_TAG,"Unable to get HealthReportDatabaseStorage for " + profilePath + 1.120 + profilePath + " (ClassCastException)."); 1.121 + throw new IllegalStateException("Unable to get HealthReportDatabaseStorage for " + 1.122 + profilePath + ".", ex); 1.123 + } 1.124 + 1.125 + return storage; 1.126 + } 1.127 + 1.128 + protected int getCurrentEnvironmentID() { 1.129 + if (currentEnvironmentID < 0) { 1.130 + final ProfileInformationCache cache = new ProfileInformationCache(profilePath); 1.131 + if (!cache.restoreUnlessInitialized()) { 1.132 + throw new IllegalStateException("Current environment unknown."); 1.133 + } 1.134 + final Environment env = EnvironmentBuilder.getCurrentEnvironment(cache); 1.135 + currentEnvironmentID = env.register(); 1.136 + } 1.137 + return currentEnvironmentID; 1.138 + } 1.139 +}