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