1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_DATABASE_H_
#define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_DATABASE_H_
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted_memory.h"
#include "base/sequence_checker.h"
#include "base/strings/cstring_view.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/extensions/activity_log/activity_actions.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension.h"
#include "sql/database.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
namespace base {
class FilePath;
}
namespace extensions {
// Encapsulates the SQL connection for the activity log database. This class
// holds the database connection and has methods for writing. All of the
// methods except the constructor need to be called on the DB thread.
//
// Object ownership and lifetime is a bit complicated for ActivityLog,
// ActivityLogPolicy, and ActivityDatabase:
//
// ActivityLog ----> ActivityLogPolicy ----> ActivityDatabase
// ^ |
// | |
// \--(ActivityDatabase::Delegate)-/
//
// The ActivityLogPolicy object contains a pointer to the ActivityDatabase, and
// the ActivityDatabase contains a pointer back to the ActivityLogPolicy object
// (as an instance of ActivityDatabase::Delegate).
//
// Since some cleanup must happen on the database thread, deletion should occur
// as follows:
// 1. ActivityLog calls ActivityLogPolicy::Close()
// 2. ActivityLogPolicy should call ActivityDatabase::Close() on the database
// thread.
// 3. ActivityDatabase::Close() shuts down the database, then calls
// ActivityDatabase::Delegate::OnDatabaseClose().
// 4. ActivityDatabase::Delegate::OnDatabaseClose() should delete the
// ActivityLogPolicy object.
// 5. ActivityDatabase::Close() finishes running by deleting the
// ActivityDatabase object.
//
// (This assumes the common case that the ActivityLogPolicy uses an
// ActivityDatabase and implements the ActivityDatabase::Delegate interface.
// It is also possible for an ActivityLogPolicy to not use a database at all,
// in which case ActivityLogPolicy::Close() should directly delete itself.)
class ActivityDatabase {
public:
// Interface defining calls that the ActivityDatabase can make into a
// ActivityLogPolicy instance to implement policy-specific behavior. Methods
// are always invoked on the database thread. Classes other than
// ActivityDatabase should not call these methods.
class Delegate {
protected:
friend class ActivityDatabase;
// A Delegate is never directly deleted; it should instead delete itself
// after any final cleanup when OnDatabaseClose() is invoked.
virtual ~Delegate() = default;
// Initializes the database schema; this gives a policy a chance to create
// or update database tables as needed. Should return true on success.
// Will be called from within a database transaction.
virtual bool InitDatabase(sql::Database* db) = 0;
// Requests that the policy flush any pending actions to the database.
// Should return true on success or false on a database error. Will not be
// called from a transaction (the implementation may wish to use a
// transaction for the flush).
virtual bool FlushDatabase(sql::Database* db) = 0;
// Called if the database encounters a permanent error; the policy should
// not expect to make any future writes to the database and may want to
// discard any queued data.
virtual void OnDatabaseFailure() = 0;
// Called by ActivityDatabase just before the ActivityDatabase object is
// deleted. The database will make no further callbacks after invoking
// this method, so it is an appropriate time for the policy to delete
// itself.
virtual void OnDatabaseClose() = 0;
};
// Value to be passed to AdviseFlush below to force a database flush.
static const int kFlushImmediately = -1;
// Need to call Init to actually use the ActivityDatabase. The Delegate
// provides hooks for an ActivityLogPolicy to control the database schema and
// reads/writes.
explicit ActivityDatabase(Delegate* delegate);
ActivityDatabase(const ActivityDatabase&) = delete;
ActivityDatabase& operator=(const ActivityDatabase&) = delete;
// Opens the DB. This invokes OnDatabaseInit in the delegate to create or
// update the database schema if needed.
void Init(const base::FilePath& db_name);
// An ActivityLogPolicy should call this to kill the ActivityDatabase.
void Close();
// Inform the database that there may be additional data which could be
// written out. The size parameter should indicate (approximately) how many
// records are queued to be written; the database may use this information to
// schedule a flush early if too much data is queueing up. A value of
// kFlushImmediately will force an immediate call into
// Delegate::FlushDatabase(); otherwise, it is up to the database to
// determine when to flush.
void AdviseFlush(int size);
// Turns off batch I/O writing mode. This should only be used in unit tests,
// browser tests, or in our special --enable-extension-activity-log-testing
// policy state.
void SetBatchModeForTesting(bool batch_mode);
bool is_db_valid() const { return valid_db_; }
// A helper method for initializing or upgrading a database table. The
// content_fields array should list the names of all of the columns in the
// database. The field_types should specify the types of the corresponding
// columns (e.g., INTEGER or LONGVARCHAR). There should be the same number of
// field_types as content_fields, since the two arrays should correspond.
static bool InitializeTable(
sql::Database* db,
base::cstring_view table_name,
base::span<const base::cstring_view> content_fields,
base::span<const base::cstring_view> field_types);
private:
// This should never be invoked by another class. Use Close() to order a
// suicide.
virtual ~ActivityDatabase();
// Used by the Init() method as a convenience for handling a failed database
// initialization attempt. Prints an error and puts us in the soft failure
// state.
void LogInitFailure();
// When we're in batched mode (which is on by default), we write to the db
// every X minutes instead of on every API call. This prevents the annoyance
// of writing to disk multiple times a second.
void RecordBatchedActions();
// If an error is unrecoverable or occurred while we were trying to close
// the database properly, we take "emergency" actions: break any outstanding
// transactions, raze the database, and close. When next opened, the
// database will be empty.
void HardFailureClose();
// Doesn't actually close the DB, but changes bools to prevent further writes
// or changes to it.
void SoftFailureClose();
// Handle errors in database writes. For a serious & permanent error, it
// invokes HardFailureClose(); for a less serious/permanent error, it invokes
// SoftFailureClose().
void DatabaseErrorCallback(int error, sql::Statement* stmt);
// For unit testing only.
void RecordBatchedActionsWhileTesting();
void SetTimerForTesting(int milliseconds);
// Retrieve a handle to the raw SQL database. This is only intended to be
// used by ActivityLogDatabasePolicy::GetDatabaseConnection(), and should
// only be called on the database thread.
sql::Database* GetSqlConnection();
SEQUENCE_CHECKER(sequence_checker_);
// A reference a Delegate for policy-specific database behavior. See the
// top-level comment for ActivityDatabase for comments on cleanup.
raw_ptr<Delegate, DanglingUntriaged> delegate_;
sql::Database db_;
bool valid_db_;
bool batch_mode_;
base::TimeDelta batching_period_;
base::RepeatingTimer timer_;
bool already_closed_;
bool did_init_;
friend class ActivityLogDatabasePolicy;
FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest, BatchModeOff);
FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest, BatchModeOn);
FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest, BatchModeFlush);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_DATABASE_H_
|