summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/activity_log/activity_database.h
blob: 2011f0ffbb3c04a8d57b21aff905285d84a2f070 (plain)
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
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// 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 <string>
#include <vector>

#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/synchronization/lock.h"
#include "base/timer/timer.h"
#include "chrome/browser/extensions/activity_log/activity_actions.h"
#include "extensions/common/extension.h"
#include "sql/connection.h"

namespace base {
class Clock;
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() {}

    // 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::Connection* 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::Connection* 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);

  // 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::Connection* db,
                              const char* table_name,
                              const char* const content_fields[],
                              const char* const field_types[],
                              const int num_content_fields);

  // Runs the given callback, passing it a handle to the database connection.
  // If the database is not valid, the callback is run (to allow it to do any
  // needed cleanup) but passed a NULL value.
  void RunOnDatabase(const base::Callback<void(sql::Connection*)>& callback);

 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::Connection* GetSqlConnection();

  // A reference a Delegate for policy-specific database behavior.  See the
  // top-level comment for ActivityDatabase for comments on cleanup.
  Delegate* delegate_;

  sql::Connection 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);
  DISALLOW_COPY_AND_ASSIGN(ActivityDatabase);
};

}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_DATABASE_H_