[Chromeshine] Persist token mappings to LevelDB.

Implement the Set- and GetAllTokenMappings functions for UsageStatsDatabase.

Bug: 902570
Change-Id: I619f1bf152566792f6046001ea14022b69e1aad5
Reviewed-on: https://chromium-review.googlesource.com/c/1423622
Commit-Queue: Natalie Chouinard <chouinard@chromium.org>
Reviewed-by: Patrick Noland <pnoland@chromium.org>
Cr-Commit-Position: refs/heads/master@{#624876}
diff --git a/chrome/browser/android/usage_stats/usage_stats_database.cc b/chrome/browser/android/usage_stats/usage_stats_database.cc
index 3ca66fe..46d50db9 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_database.cc
@@ -16,8 +16,8 @@
 const char kNamespace[] = "usage_stats";
 const char kTypePrefix[] = "usage_stats";
 
-const char kKeySeparator = '.';
-const char kSuspensionPrefix[] = "suspension";
+const char kSuspensionPrefix[] = "suspension_";
+const char kTokenMappingPrefix[] = "token_mapping_";
 
 UsageStatsDatabase::UsageStatsDatabase(Profile* profile)
     : weak_ptr_factory_(this) {
@@ -71,7 +71,7 @@
 
   for (std::string domain : domains) {
     // Prepend prefix to form key.
-    std::string key = std::string(kSuspensionPrefix) + kKeySeparator + domain;
+    std::string key = kSuspensionPrefix + domain;
 
     keys.emplace_back(key);
 
@@ -88,10 +88,55 @@
   proto_db_->UpdateEntriesWithRemoveFilter(
       std::move(entries),
       base::BindRepeating(&DoesNotContainFilter, std::move(key_set)),
-      base::BindOnce(&UsageStatsDatabase::OnUpdateEntriesForSetSuspensions,
+      base::BindOnce(&UsageStatsDatabase::OnUpdateEntries,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void UsageStatsDatabase::GetAllTokenMappings(TokenMappingsCallback callback) {
+  // Load all UsageStats with the token mapping prefix.
+  proto_db_->LoadEntriesWithFilter(
+      leveldb_proto::KeyFilter(), leveldb::ReadOptions(), kTokenMappingPrefix,
+      base::BindOnce(&UsageStatsDatabase::OnLoadEntriesForGetAllTokenMappings,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void UsageStatsDatabase::SetTokenMappings(TokenMap mappings,
+                                          StatusCallback callback) {
+  std::vector<std::string> keys;
+  keys.reserve(mappings.size());
+
+  auto entries = std::make_unique<
+      leveldb_proto::ProtoDatabase<UsageStat>::KeyEntryVector>();
+
+  for (const auto& mapping : mappings) {
+    // Prepend prefix to token to form key.
+    std::string key = kTokenMappingPrefix + mapping.first;
+
+    keys.emplace_back(key);
+
+    UsageStat value;
+    TokenMapping* token_mapping = value.mutable_token_mapping();
+    token_mapping->set_token(mapping.first);
+    token_mapping->set_fqdn(mapping.second);
+
+    entries->emplace_back(key, value);
+  }
+
+  auto key_set = base::flat_set<std::string>(keys);
+
+  // Add all entries created from map, remove all entries not in the map.
+  proto_db_->UpdateEntriesWithRemoveFilter(
+      std::move(entries),
+      base::BindRepeating(&DoesNotContainFilter, std::move(key_set)),
+      base::BindOnce(&UsageStatsDatabase::OnUpdateEntries,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void UsageStatsDatabase::OnUpdateEntries(StatusCallback callback,
+                                         bool success) {
+  std::move(callback).Run(ToError(success));
+}
+
 void UsageStatsDatabase::OnLoadEntriesForGetAllSuspensions(
     SuspensionCallback callback,
     bool success,
@@ -108,10 +153,20 @@
   std::move(callback).Run(ToError(success), std::move(results));
 }
 
-void UsageStatsDatabase::OnUpdateEntriesForSetSuspensions(
-    StatusCallback callback,
-    bool success) {
-  std::move(callback).Run(ToError(success));
+void UsageStatsDatabase::OnLoadEntriesForGetAllTokenMappings(
+    TokenMappingsCallback callback,
+    bool success,
+    std::unique_ptr<std::vector<UsageStat>> stats) {
+  TokenMap results;
+
+  if (stats) {
+    for (UsageStat stat : *stats) {
+      TokenMapping mapping = stat.token_mapping();
+      results.emplace(mapping.token(), mapping.fqdn());
+    }
+  }
+
+  std::move(callback).Run(ToError(success), std::move(results));
 }
 
 }  // namespace usage_stats
diff --git a/chrome/browser/android/usage_stats/usage_stats_database.h b/chrome/browser/android/usage_stats/usage_stats_database.h
index 35dbf3d..76390f7 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database.h
+++ b/chrome/browser/android/usage_stats/usage_stats_database.h
@@ -31,8 +31,8 @@
   using SuspensionCallback =
       base::OnceCallback<void(Error, std::vector<std::string>)>;
 
-  using TokenMappingsCallback =
-      base::OnceCallback<void(Error, std::map<std::string, std::string>)>;
+  using TokenMap = std::map<std::string, std::string>;
+  using TokenMappingsCallback = base::OnceCallback<void(Error, TokenMap)>;
 
   using StatusCallback = base::OnceCallback<void(Error)>;
 
@@ -73,16 +73,20 @@
 
   // Persists all the mappings in |mappings| and deletes any mappings *not* in
   // |mappings|. The map's key is the token, and its value is the FQDN.
-  void SetTokenMappings(std::map<std::string, std::string> mappings,
-                        StatusCallback callback);
+  void SetTokenMappings(TokenMap mappings, StatusCallback callback);
 
  private:
+  void OnUpdateEntries(StatusCallback callback, bool success);
+
   void OnLoadEntriesForGetAllSuspensions(
       SuspensionCallback callback,
       bool success,
-      std::unique_ptr<std::vector<UsageStat>> suspensions);
+      std::unique_ptr<std::vector<UsageStat>> stats);
 
-  void OnUpdateEntriesForSetSuspensions(StatusCallback callback, bool success);
+  void OnLoadEntriesForGetAllTokenMappings(
+      TokenMappingsCallback callback,
+      bool success,
+      std::unique_ptr<std::vector<UsageStat>> stats);
 
   std::unique_ptr<leveldb_proto::ProtoDatabase<UsageStat>> proto_db_;
 
diff --git a/chrome/browser/android/usage_stats/usage_stats_database_unittest.cc b/chrome/browser/android/usage_stats/usage_stats_database_unittest.cc
index 144e1239..5086ad7 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database_unittest.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_database_unittest.cc
@@ -19,6 +19,9 @@
 const char kFqdn1[] = "foo.com";
 const char kFqdn2[] = "bar.org";
 
+const char kToken1[] = "token1";
+const char kToken2[] = "token2";
+
 }  // namespace
 
 class UsageStatsDatabaseTest : public testing::Test {
@@ -39,9 +42,11 @@
 
   FakeDB<UsageStat>* fake_db() { return fake_db_unowned_; }
 
-  MOCK_METHOD1(OnSetSuspensionsDone, void(UsageStatsDatabase::Error));
+  MOCK_METHOD1(OnUpdateDone, void(UsageStatsDatabase::Error));
   MOCK_METHOD2(OnGetAllSuspensionsDone,
                void(UsageStatsDatabase::Error, std::vector<std::string>));
+  MOCK_METHOD2(OnGetAllTokenMappingsDone,
+               void(UsageStatsDatabase::Error, UsageStatsDatabase::TokenMap));
 
  private:
   std::map<std::string, UsageStat> fake_db_unowned_store_;
@@ -56,13 +61,15 @@
   ASSERT_NE(nullptr, fake_db());
 }
 
+// Suspension Tests
+
 TEST_F(UsageStatsDatabaseTest, SetSuspensionsSuccess) {
   base::flat_set<std::string> domains({kFqdn1, kFqdn2});
 
-  EXPECT_CALL(*this, OnSetSuspensionsDone(UsageStatsDatabase::Error::kNoError));
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
 
   usage_stats_database()->SetSuspensions(
-      domains, base::BindOnce(&UsageStatsDatabaseTest::OnSetSuspensionsDone,
+      domains, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
                               base::Unretained(this)));
 
   fake_db()->UpdateCallback(true);
@@ -71,11 +78,10 @@
 TEST_F(UsageStatsDatabaseTest, SetSuspensionsFailure) {
   base::flat_set<std::string> domains({kFqdn1, kFqdn2});
 
-  EXPECT_CALL(*this,
-              OnSetSuspensionsDone(UsageStatsDatabase::Error::kUnknownError));
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kUnknownError));
 
   usage_stats_database()->SetSuspensions(
-      domains, base::BindOnce(&UsageStatsDatabaseTest::OnSetSuspensionsDone,
+      domains, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
                               base::Unretained(this)));
 
   fake_db()->UpdateCallback(false);
@@ -111,10 +117,10 @@
   // Insert 1 suspension.
   base::flat_set<std::string> domains({kFqdn1});
 
-  EXPECT_CALL(*this, OnSetSuspensionsDone(UsageStatsDatabase::Error::kNoError));
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
 
   usage_stats_database()->SetSuspensions(
-      domains, base::BindOnce(&UsageStatsDatabaseTest::OnSetSuspensionsDone,
+      domains, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
                               base::Unretained(this)));
 
   fake_db()->UpdateCallback(true);
@@ -136,10 +142,10 @@
   // Insert 2 suspensions.
   base::flat_set<std::string> domains1({kFqdn1, kFqdn2});
 
-  EXPECT_CALL(*this, OnSetSuspensionsDone(UsageStatsDatabase::Error::kNoError));
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
 
   usage_stats_database()->SetSuspensions(
-      domains1, base::BindOnce(&UsageStatsDatabaseTest::OnSetSuspensionsDone,
+      domains1, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
                                base::Unretained(this)));
 
   fake_db()->UpdateCallback(true);
@@ -147,10 +153,10 @@
   // Insert 1 suspension, and remove the other.
   base::flat_set<std::string> domains2({kFqdn1});
 
-  EXPECT_CALL(*this, OnSetSuspensionsDone(UsageStatsDatabase::Error::kNoError));
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
 
   usage_stats_database()->SetSuspensions(
-      domains2, base::BindOnce(&UsageStatsDatabaseTest::OnSetSuspensionsDone,
+      domains2, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
                                base::Unretained(this)));
 
   fake_db()->UpdateCallback(true);
@@ -168,4 +174,113 @@
   fake_db()->LoadCallback(true);
 }
 
+// Token Mapping Tests
+
+TEST_F(UsageStatsDatabaseTest, SetTokenMappingsSuccess) {
+  UsageStatsDatabase::TokenMap mappings({{kToken1, kFqdn1}, {kToken2, kFqdn2}});
+
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
+
+  usage_stats_database()->SetTokenMappings(
+      mappings, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
+                               base::Unretained(this)));
+
+  fake_db()->UpdateCallback(true);
+}
+
+TEST_F(UsageStatsDatabaseTest, SetTokenMappingsFailure) {
+  UsageStatsDatabase::TokenMap mappings({{kToken1, kFqdn1}, {kToken2, kFqdn2}});
+
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kUnknownError));
+
+  usage_stats_database()->SetTokenMappings(
+      mappings, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
+                               base::Unretained(this)));
+
+  fake_db()->UpdateCallback(false);
+}
+
+TEST_F(UsageStatsDatabaseTest, GetAllTokenMappingsSuccess) {
+  UsageStatsDatabase::TokenMap expected;
+
+  EXPECT_CALL(*this, OnGetAllTokenMappingsDone(
+                         UsageStatsDatabase::Error::kNoError, expected));
+
+  usage_stats_database()->GetAllTokenMappings(
+      base::BindOnce(&UsageStatsDatabaseTest::OnGetAllTokenMappingsDone,
+                     base::Unretained(this)));
+
+  fake_db()->LoadCallback(true);
+}
+
+TEST_F(UsageStatsDatabaseTest, GetAllTokenMappingsFailure) {
+  UsageStatsDatabase::TokenMap expected;
+
+  EXPECT_CALL(*this, OnGetAllTokenMappingsDone(
+                         UsageStatsDatabase::Error::kUnknownError, expected));
+
+  usage_stats_database()->GetAllTokenMappings(
+      base::BindOnce(&UsageStatsDatabaseTest::OnGetAllTokenMappingsDone,
+                     base::Unretained(this)));
+
+  fake_db()->LoadCallback(false);
+}
+
+TEST_F(UsageStatsDatabaseTest, SetAndGetTokenMapping) {
+  UsageStatsDatabase::TokenMap mapping({{kToken1, kFqdn1}});
+
+  // Insert 1 token mapping.
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
+
+  usage_stats_database()->SetTokenMappings(
+      mapping, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
+                              base::Unretained(this)));
+
+  fake_db()->UpdateCallback(true);
+
+  // Get 1 token mapping.
+  EXPECT_CALL(*this, OnGetAllTokenMappingsDone(
+                         UsageStatsDatabase::Error::kNoError, mapping));
+
+  usage_stats_database()->GetAllTokenMappings(
+      base::BindOnce(&UsageStatsDatabaseTest::OnGetAllTokenMappingsDone,
+                     base::Unretained(this)));
+
+  fake_db()->LoadCallback(true);
+}
+
+TEST_F(UsageStatsDatabaseTest, SetRemoveAndGetTokenMapping) {
+  // Insert 2 token mappings.
+  UsageStatsDatabase::TokenMap mappings1(
+      {{kToken1, kFqdn1}, {kToken2, kFqdn2}});
+
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
+
+  usage_stats_database()->SetTokenMappings(
+      mappings1, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
+                                base::Unretained(this)));
+
+  fake_db()->UpdateCallback(true);
+
+  // Re-insert 1 token mapping, and remove the other.apping) {
+  UsageStatsDatabase::TokenMap mappings2({{kToken1, kFqdn1}});
+
+  EXPECT_CALL(*this, OnUpdateDone(UsageStatsDatabase::Error::kNoError));
+
+  usage_stats_database()->SetTokenMappings(
+      mappings2, base::BindOnce(&UsageStatsDatabaseTest::OnUpdateDone,
+                                base::Unretained(this)));
+
+  fake_db()->UpdateCallback(true);
+
+  // Get 1 remaining token mapping.
+  EXPECT_CALL(*this, OnGetAllTokenMappingsDone(
+                         UsageStatsDatabase::Error::kNoError, mappings2));
+
+  usage_stats_database()->GetAllTokenMappings(
+      base::BindOnce(&UsageStatsDatabaseTest::OnGetAllTokenMappingsDone,
+                     base::Unretained(this)));
+
+  fake_db()->LoadCallback(true);
+}
 }  // namespace usage_stats