blob: e4748baa81ee50d30deab08d404565af74ecaf40 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include <base/files/file_path.h>
#include <base/optional.h>
#include <base/time/time.h>
#include <sqlite3.h>
namespace apk_cache {
// Database for opaque files support in ARC++ APK cache.
// Design doc: go/arc-apk-cache-opaque-files
// |Session| objects stored in |sessions| table.
// A session represents a file streaming session, which is used to avoid race
// condition between play store and cache cleaner. For example, while play store
// is streaming a file, cache cleaner will treat related file entry as invalid
// and delete it because of size mismatch. When play store wants to push a
// package, a session is created. The same session |id| is used through the
// whole streaming process. The session is marked as closed once the streaming
// process finishes. Cache cleaner will also create a session before cleaning,
// in case play store starts pushing packages during cleaning. |source| is used
// to indicate which component created the session. Sessions have maximum age
// limits. Expired sessions must be removed. |timestamp| stores creation time of
// the session. |status| is the status code of the session (open, closed, etc.).
struct Session {
int64_t id;
std::string source;
base::Time timestamp;
int32_t status;
// |FileEntry| objects stored in |file_entries| table.
// A file entry represents a file (base APK or other types) stored in APK cache.
// |id| is used to uniquely identify them. Physical files stored on the disk are
// named with their |id| in hexadecimal. |package_name| and |version_code|
// represent which specific package is the file in. |type| represents what type
// the file is. This is managed by play store. Every package must contain only
// one file of type base APK. Other types are not restricted. |attributes| is
// used to store additional info about the file, for example OBB version. |size|
// is size of the file, this must match physical file on the disk. |hash| is
// SHA-256 digest encoded in Base64, which is the same hashing method as play
// store. |access_time| stored access time of the file. |priority| is cache
// priority of the package. |session_id| is a foreign key to ||, which
// represents the session the file entry is from.
struct FileEntry {
int64_t id;
std::string package_name;
int64_t version_code;
std::string type;
base::Optional<std::string> attributes;
int64_t size;
base::Optional<std::string> hash;
base::Time access_time;
int32_t priority;
int64_t session_id;
// Escapes string in SQL. Replaces ' with ''.
std::string EscapeSQLString(const std::string& string_to_escape);
// Provides access to APK cache database.
class ApkCacheDatabase {
// Creates an instance to talk to the database file at |db_path|. Init() must
// be called to establish connection.
explicit ApkCacheDatabase(const base::FilePath& db_path);
// Not copyable or movable.
ApkCacheDatabase(const ApkCacheDatabase&) = delete;
ApkCacheDatabase& operator=(const ApkCacheDatabase&) = delete;
// Initializes database connection. Must be called before any other queries.
// Returns |SQLITE_OK| if no error ocurred.
int Init();
// Returns true if the database connection is open.
bool IsOpen();
// Closes database connection. Returns |SQLITE_OK| if no error occurred.
// Otherwise SQLite error code is returned.
int Close();
// Runs SQLite built-in integrity check. Returns true if no error is found.
bool CheckIntegrity() const;
// Returns true if sessions table exists.
bool SessionsTableExists() const;
// Inserts session into database. Returns |id|. Returns 0 if error occurred.
int64_t InsertSession(const Session& session) const;
// Gets all sessions. Returns nullopt if any error occurs.
base::Optional<std::vector<Session>> GetSessions() const;
// Gets all file entries. Returns nullopt if any error occurs.
base::Optional<std::vector<FileEntry>> GetFileEntries() const;
// Deletes |session| from database. Any file entries referencing this session
// will also be removed. Returns true if no error occurred.
bool DeleteSession(int64_t session_id) const;
// Deletes sessions without any file entries. Do not delete |current_session|.
// Returns number of rows affected. Returns -1 if error occurred.
int DeleteSessionsWithoutFileEntries(int64_t current_session) const;
// Deletes |file_entry| from database. Returns true if no error occurred.
bool DeleteFileEntry(int64_t file_id) const;
// Deletes all file entries in a package. Returns number of rows affected.
// Returns -1 if error occurred
int DeletePackage(const std::string& name, int64_t version) const;
// Updates session status. Returns true if successful.
bool UpdateSessionStatus(int64_t id, int32_t status) const;
using SqliteCallback = int (*)(void*, int, char**, char**);
struct ExecResult {
int code;
std::string error_msg;
// Enables foreign key support in SQLite library. By default foreign key is
// not enabled, which results in foreign key constraints not being checked
// during insert, update and delete.
int EnableForeignKey() const;
// Execute SQL.
ExecResult ExecSQL(const std::string& sql) const;
ExecResult ExecSQL(const std::string& sql,
SqliteCallback callback,
void* data) const;
// Executes SQL that deletes rows. Returns number of rows affected. Returns -1
// if error occurs.
int ExecDeleteSQL(const std::string& sql) const;
base::FilePath db_path_;
std::unique_ptr<sqlite3, decltype(&sqlite3_close)> db_;
} // namespace apk_cache