Merge "Migrate existing jarjar plugin users to shadow plugin" into androidx-master-dev
diff --git a/icing/file/file-backed-proto-log.h b/icing/file/file-backed-proto-log.h
index aa5a031..763c93b 100644
--- a/icing/file/file-backed-proto-log.h
+++ b/icing/file/file-backed-proto-log.h
@@ -72,6 +72,7 @@
 #include "icing/legacy/core/icing-string-util.h"
 #include "icing/portable/zlib.h"
 #include "icing/util/crc32.h"
+#include "icing/util/data-loss.h"
 #include "icing/util/logging.h"
 #include "icing/util/status-macros.h"
 
@@ -172,10 +173,10 @@
     // happen if the file is corrupted or some previously added data was
     // unpersisted. This may be used to signal that any derived data off of the
     // proto log may need to be regenerated.
-    enum DataStatus { NO_DATA_LOSS, PARTIAL_LOSS, COMPLETE_LOSS } data_status;
+    DataLoss data_loss;
 
     bool has_data_loss() {
-      return data_status == PARTIAL_LOSS || data_status == COMPLETE_LOSS;
+      return data_loss == DataLoss::PARTIAL || data_loss == DataLoss::COMPLETE;
     }
   };
 
@@ -186,11 +187,11 @@
   // added data was unpersisted, the log will rewind to the last-good state. The
   // log saves these checkpointed "good" states when PersistToDisk() is called
   // or the log is safely destructed. If the log rewinds successfully to the
-  // last-good state, then the returned CreateResult.data_status indicates
+  // last-good state, then the returned CreateResult.data_loss indicates
   // whether it has a data loss and what kind of data loss it is (partial or
   // complete) so that any derived data may know that it needs to be updated. If
   // the log re-initializes successfully without any data loss,
-  // CreateResult.data_status will be NO_DATA_LOSS.
+  // CreateResult.data_loss will be NONE.
   //
   // Params:
   //   filesystem: Handles system level calls
@@ -370,7 +371,7 @@
   // Initializes a new proto log.
   //
   // Returns:
-  //   std::unique_ptr<FileBackedProtoLog> that can be used immediately
+  //   std::unique_ptr<CreateResult> on success
   //   INTERNAL_ERROR on IO error
   static libtextclassifier3::StatusOr<CreateResult> InitializeNewFile(
       const Filesystem* filesystem, const std::string& file_path,
@@ -381,7 +382,7 @@
   // content will be lost.
   //
   // Returns:
-  //   std::unique_ptr<FileBackedProtoLog> that can be used immediately
+  //   std::unique_ptr<CreateResult> on success
   //   INTERNAL_ERROR on IO error or internal inconsistencies in the file
   //   INVALID_ARGUMENT_ERROR if options aren't consistent with previous
   //     instances
@@ -516,7 +517,7 @@
       std::unique_ptr<FileBackedProtoLog<ProtoT>>(
           new FileBackedProtoLog<ProtoT>(filesystem, file_path,
                                          std::move(header))),
-      /*data_status=*/CreateResult::NO_DATA_LOSS};
+      /*data_loss=*/DataLoss::NONE};
 
   return create_result;
 }
@@ -566,7 +567,7 @@
   }
   header->max_proto_size = options.max_proto_size;
 
-  typename CreateResult::DataStatus data_status = CreateResult::NO_DATA_LOSS;
+  DataLoss data_loss = DataLoss::NONE;
   ICING_ASSIGN_OR_RETURN(Crc32 calculated_log_checksum,
                          ComputeChecksum(filesystem, file_path, Crc32(),
                                          sizeof(Header), file_size));
@@ -589,12 +590,12 @@
       // Check if it matches our last rewind state. If so, this becomes our last
       // good state and we can safely truncate and recover from here.
       last_known_good = header->rewind_offset;
-      data_status = CreateResult::PARTIAL_LOSS;
+      data_loss = DataLoss::PARTIAL;
     } else {
       // Otherwise, we're going to truncate the entire log and this resets the
       // checksum to an empty log state.
       header->log_checksum = 0;
-      data_status = CreateResult::COMPLETE_LOSS;
+      data_loss = DataLoss::COMPLETE;
     }
 
     if (!filesystem->Truncate(file_path.c_str(), last_known_good)) {
@@ -610,7 +611,7 @@
       std::unique_ptr<FileBackedProtoLog<ProtoT>>(
           new FileBackedProtoLog<ProtoT>(filesystem, file_path,
                                          std::move(header))),
-      data_status};
+      data_loss};
 
   return create_result;
 }
diff --git a/icing/file/file-backed-proto-log_test.cc b/icing/file/file-backed-proto-log_test.cc
index 7410d2b..d429277 100644
--- a/icing/file/file-backed-proto-log_test.cc
+++ b/icing/file/file-backed-proto-log_test.cc
@@ -339,6 +339,7 @@
                                                        max_proto_size_)));
     auto proto_log = std::move(create_result.proto_log);
     ASSERT_TRUE(create_result.has_data_loss());
+    ASSERT_THAT(create_result.data_loss, Eq(DataLoss::COMPLETE));
 
     // Lost everything in the log since the rewind position doesn't help if
     // there's been data corruption within the persisted region
@@ -408,6 +409,7 @@
                                                        max_proto_size_)));
     auto proto_log = std::move(create_result.proto_log);
     ASSERT_TRUE(create_result.has_data_loss());
+    ASSERT_THAT(create_result.data_loss, Eq(DataLoss::PARTIAL));
 
     // Check that everything was persisted across instances
     ASSERT_THAT(proto_log->ReadProto(document1_offset),
diff --git a/icing/file/file-backed-proto.h b/icing/file/file-backed-proto.h
index aede8de..15a1953 100644
--- a/icing/file/file-backed-proto.h
+++ b/icing/file/file-backed-proto.h
@@ -83,7 +83,7 @@
   //
   // TODO(cassiewang) The implementation today loses old data if Write() fails.
   // We should write to a tmp file first and rename the file to fix this.
-  // TODO(samzheng) Change to Write(ProtoT&& proto)
+  // TODO(cassiewang) Change to Write(ProtoT&& proto)
   libtextclassifier3::Status Write(std::unique_ptr<ProtoT> proto)
       ICING_LOCKS_EXCLUDED(mutex_);
 
diff --git a/icing/helpers/icu/icu-data-file-helper.cc b/icing/helpers/icu/icu-data-file-helper.cc
index 5cf6a1d..6607c40 100644
--- a/icing/helpers/icu/icu-data-file-helper.cc
+++ b/icing/helpers/icu/icu-data-file-helper.cc
@@ -49,8 +49,6 @@
     return absl_ports::InternalError("Unable to open file at provided path");
   }
 
-  // TODO(samzheng): figure out why icing::MemoryMappedFile causes
-  // segmentation fault here.
   const void* data =
       mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd.get(), 0);
 
diff --git a/icing/icing-search-engine.cc b/icing/icing-search-engine.cc
index 08ceafd..c40bac9 100644
--- a/icing/icing-search-engine.cc
+++ b/icing/icing-search-engine.cc
@@ -60,7 +60,6 @@
 #include "icing/util/crc32.h"
 #include "icing/util/logging.h"
 #include "icing/util/status-macros.h"
-#include "icing/util/timer.h"
 #include "unicode/uloc.h"
 
 namespace icing {
@@ -264,7 +263,7 @@
                 << options_.base_dir();
 
   // Measure the latency of the initialization process.
-  Timer initialize_timer;
+  std::unique_ptr<Timer> initialize_timer = clock_->GetNewTimer();
 
   InitializeResultProto result_proto;
   StatusProto* result_status = result_proto.mutable_status();
@@ -273,7 +272,8 @@
   if (initialized_) {
     // Already initialized.
     result_status->set_code(StatusProto::OK);
-    initialize_stats->set_latency_ms(initialize_timer.GetElapsedMilliseconds());
+    initialize_stats->set_latency_ms(
+        initialize_timer->GetElapsedMilliseconds());
     initialize_stats->set_num_documents(document_store_->num_documents());
     return result_proto;
   }
@@ -284,7 +284,8 @@
   libtextclassifier3::Status status = InitializeMembers(initialize_stats);
   if (!status.ok()) {
     TransformStatus(status, result_status);
-    initialize_stats->set_latency_ms(initialize_timer.GetElapsedMilliseconds());
+    initialize_stats->set_latency_ms(
+        initialize_timer->GetElapsedMilliseconds());
     return result_proto;
   }
 
@@ -336,10 +337,10 @@
         // index.
         initialize_stats->set_index_restoration_cause(
             NativeInitializeStats::INCONSISTENT_WITH_GROUND_TRUTH);
-        Timer index_restore_timer;
+        std::unique_ptr<Timer> index_restore_timer = clock_->GetNewTimer();
         status = RestoreIndexIfNeeded();
         initialize_stats->set_index_restoration_latency_ms(
-            index_restore_timer.GetElapsedMilliseconds());
+            index_restore_timer->GetElapsedMilliseconds());
       }
     }
   }
@@ -348,7 +349,7 @@
     initialized_ = true;
   }
   TransformStatus(status, result_status);
-  initialize_stats->set_latency_ms(initialize_timer.GetElapsedMilliseconds());
+  initialize_stats->set_latency_ms(initialize_timer->GetElapsedMilliseconds());
   return result_proto;
 }
 
@@ -398,7 +399,7 @@
   }
   ICING_ASSIGN_OR_RETURN(
       schema_store_, SchemaStore::Create(filesystem_.get(), schema_store_dir,
-                                         initialize_stats));
+                                         clock_.get(), initialize_stats));
 
   return libtextclassifier3::Status::OK;
 }
@@ -415,9 +416,10 @@
         absl_ports::StrCat("Could not create directory: ", document_dir));
   }
   ICING_ASSIGN_OR_RETURN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(filesystem_.get(), document_dir, clock_.get(),
                             schema_store_.get(), initialize_stats));
+  document_store_ = std::move(create_result.document_store);
 
   return libtextclassifier3::Status::OK;
 }
@@ -451,10 +453,10 @@
                            Index::Create(index_options, filesystem_.get(),
                                          icing_filesystem_.get()));
 
-    Timer restore_timer;
+    std::unique_ptr<Timer> restore_timer = clock_->GetNewTimer();
     ICING_RETURN_IF_ERROR(RestoreIndexIfNeeded());
     initialize_stats->set_index_restoration_latency_ms(
-        restore_timer.GetElapsedMilliseconds());
+        restore_timer->GetElapsedMilliseconds());
   } else {
     // Index was created fine.
     index_ = std::move(index_or).ValueOrDie();
@@ -497,20 +499,20 @@
     NativeInitializeStats* initialize_stats, bool log_document_store_stats) {
   // Measure the latency of the data recovery. The cause of the recovery should
   // be logged by the caller.
-  Timer timer;
+  std::unique_ptr<Timer> timer = clock_->GetNewTimer();
   ICING_RETURN_IF_ERROR(
       document_store_->UpdateSchemaStore(schema_store_.get()));
   if (initialize_stats != nullptr && log_document_store_stats) {
     initialize_stats->set_document_store_recovery_latency_ms(
-        timer.GetElapsedMilliseconds());
+        timer->GetElapsedMilliseconds());
   }
   // Restart timer.
-  timer = Timer();
+  timer = clock_->GetNewTimer();
   ICING_RETURN_IF_ERROR(index_->Reset());
   ICING_RETURN_IF_ERROR(RestoreIndexIfNeeded());
   if (initialize_stats != nullptr) {
     initialize_stats->set_index_restoration_latency_ms(
-        timer.GetElapsedMilliseconds());
+        timer->GetElapsedMilliseconds());
   }
 
   const std::string header_file =
@@ -673,7 +675,7 @@
 PutResultProto IcingSearchEngine::Put(DocumentProto&& document) {
   ICING_VLOG(1) << "Writing document to document store";
 
-  Timer put_timer;
+  std::unique_ptr<Timer> put_timer = clock_->GetNewTimer();
 
   PutResultProto result_proto;
   StatusProto* result_status = result_proto.mutable_status();
@@ -687,24 +689,24 @@
   if (!initialized_) {
     result_status->set_code(StatusProto::FAILED_PRECONDITION);
     result_status->set_message("IcingSearchEngine has not been initialized!");
-    put_document_stats->set_latency_ms(put_timer.GetElapsedMilliseconds());
+    put_document_stats->set_latency_ms(put_timer->GetElapsedMilliseconds());
     return result_proto;
   }
 
   auto document_id_or = document_store_->Put(document, put_document_stats);
   if (!document_id_or.ok()) {
     TransformStatus(document_id_or.status(), result_status);
-    put_document_stats->set_latency_ms(put_timer.GetElapsedMilliseconds());
+    put_document_stats->set_latency_ms(put_timer->GetElapsedMilliseconds());
     return result_proto;
   }
   DocumentId document_id = document_id_or.ValueOrDie();
 
   auto index_processor_or = IndexProcessor::Create(
       schema_store_.get(), language_segmenter_.get(), normalizer_.get(),
-      index_.get(), CreateIndexProcessorOptions(options_));
+      index_.get(), CreateIndexProcessorOptions(options_), clock_.get());
   if (!index_processor_or.ok()) {
     TransformStatus(index_processor_or.status(), result_status);
-    put_document_stats->set_latency_ms(put_timer.GetElapsedMilliseconds());
+    put_document_stats->set_latency_ms(put_timer->GetElapsedMilliseconds());
     return result_proto;
   }
   std::unique_ptr<IndexProcessor> index_processor =
@@ -714,7 +716,7 @@
       index_processor->IndexDocument(document, document_id, put_document_stats);
 
   TransformStatus(status, result_status);
-  put_document_stats->set_latency_ms(put_timer.GetElapsedMilliseconds());
+  put_document_stats->set_latency_ms(put_timer->GetElapsedMilliseconds());
   return result_proto;
 }
 
@@ -1357,21 +1359,21 @@
 
     // Tries to rebuild document store if swapping fails, to avoid leaving the
     // system in the broken state for future operations.
-    auto document_store_or =
+    auto create_result_or =
         DocumentStore::Create(filesystem_.get(), current_document_dir,
                               clock_.get(), schema_store_.get());
     // TODO(b/144458732): Implement a more robust version of
     // TC_ASSIGN_OR_RETURN that can support error logging.
-    if (!document_store_or.ok()) {
+    if (!create_result_or.ok()) {
       // Unable to create DocumentStore from the old file. Mark as uninitialized
       // and return INTERNAL.
       initialized_ = false;
       ICING_LOG(ERROR) << "Failed to create document store instance";
       return absl_ports::Annotate(
           absl_ports::InternalError("Failed to create document store instance"),
-          document_store_or.status().error_message());
+          create_result_or.status().error_message());
     }
-    document_store_ = std::move(document_store_or).ValueOrDie();
+    document_store_ = std::move(create_result_or.ValueOrDie().document_store);
 
     // Potential data loss
     // TODO(b/147373249): Find a way to detect true data loss error
@@ -1380,10 +1382,10 @@
   }
 
   // Recreates the doc store instance
-  auto document_store_or =
+  auto create_result_or =
       DocumentStore::Create(filesystem_.get(), current_document_dir,
                             clock_.get(), schema_store_.get());
-  if (!document_store_or.ok()) {
+  if (!create_result_or.ok()) {
     // Unable to create DocumentStore from the new file. Mark as uninitialized
     // and return INTERNAL.
     initialized_ = false;
@@ -1391,7 +1393,7 @@
         "Document store has been optimized, but a valid document store "
         "instance can't be created");
   }
-  document_store_ = std::move(document_store_or).ValueOrDie();
+  document_store_ = std::move(create_result_or.ValueOrDie().document_store);
 
   // Deletes tmp directory
   if (!filesystem_->DeleteDirectoryRecursively(
@@ -1432,9 +1434,9 @@
 
   ICING_ASSIGN_OR_RETURN(
       std::unique_ptr<IndexProcessor> index_processor,
-      IndexProcessor::Create(schema_store_.get(), language_segmenter_.get(),
-                             normalizer_.get(), index_.get(),
-                             CreateIndexProcessorOptions(options_)));
+      IndexProcessor::Create(
+          schema_store_.get(), language_segmenter_.get(), normalizer_.get(),
+          index_.get(), CreateIndexProcessorOptions(options_), clock_.get()));
 
   ICING_VLOG(1) << "Restoring index by replaying documents from document id "
                 << first_document_to_reindex << " to document id "
diff --git a/icing/icing-search-engine_test.cc b/icing/icing-search-engine_test.cc
index b642a94..ab05646 100644
--- a/icing/icing-search-engine_test.cc
+++ b/icing/icing-search-engine_test.cc
@@ -41,6 +41,7 @@
 #include "icing/testing/common-matchers.h"
 #include "icing/testing/fake-clock.h"
 #include "icing/testing/jni-test-helpers.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/random-string.h"
 #include "icing/testing/snippet-helpers.h"
 #include "icing/testing/test-data.h"
@@ -99,17 +100,19 @@
 class IcingSearchEngineTest : public testing::Test {
  protected:
   void SetUp() override {
-#ifndef ICING_REVERSE_JNI_SEGMENTATION
-    // If we've specified using the reverse-JNI method for segmentation (i.e.
-    // not ICU), then we won't have the ICU data file included to set up.
-    // Technically, we could choose to use reverse-JNI for segmentation AND
-    // include an ICU data file, but that seems unlikely and our current BUILD
-    // setup doesn't do this.
-    // File generated via icu_data_file rule in //icing/BUILD.
-    std::string icu_data_file_path =
-        GetTestFilePath("icing/icu.dat");
-    ICING_ASSERT_OK(icu_data_file_helper::SetUpICUDataFile(icu_data_file_path));
-#endif  // ICING_REVERSE_JNI_SEGMENTATION
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      // If we've specified using the reverse-JNI method for segmentation (i.e.
+      // not ICU), then we won't have the ICU data file included to set up.
+      // Technically, we could choose to use reverse-JNI for segmentation AND
+      // include an ICU data file, but that seems unlikely and our current BUILD
+      // setup doesn't do this.
+      // File generated via icu_data_file rule in //icing/BUILD.
+      std::string icu_data_file_path =
+          GetTestFilePath("icing/icu.dat");
+      ICING_ASSERT_OK(
+          icu_data_file_helper::SetUpICUDataFile(icu_data_file_path));
+    }
+
     filesystem_.CreateDirectoryRecursively(GetTestBaseDir().c_str());
   }
 
@@ -2930,9 +2933,10 @@
     property->mutable_string_indexing_config()->set_tokenizer_type(
         StringIndexingConfig::TokenizerType::PLAIN);
 
+    FakeClock fake_clock;
     ICING_ASSERT_OK_AND_ASSIGN(
         std::unique_ptr<SchemaStore> schema_store,
-        SchemaStore::Create(filesystem(), GetSchemaDir()));
+        SchemaStore::Create(filesystem(), GetSchemaDir(), &fake_clock));
     ICING_EXPECT_OK(schema_store->SetSchema(new_schema));
   }  // Will persist new schema
 
@@ -2989,17 +2993,20 @@
   }  // This should shut down IcingSearchEngine and persist anything it needs to
 
   {
+    FakeClock fake_clock;
     ICING_ASSERT_OK_AND_ASSIGN(
         std::unique_ptr<SchemaStore> schema_store,
-        SchemaStore::Create(filesystem(), GetSchemaDir()));
+        SchemaStore::Create(filesystem(), GetSchemaDir(), &fake_clock));
     ICING_EXPECT_OK(schema_store->SetSchema(CreateMessageSchema()));
 
     // Puts a second document into DocumentStore but doesn't index it.
-    FakeClock fake_clock;
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> document_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(filesystem(), GetDocumentDir(), &fake_clock,
                               schema_store.get()));
+    std::unique_ptr<DocumentStore> document_store =
+        std::move(create_result.document_store);
+
     ICING_EXPECT_OK(document_store->Put(document2));
   }
 
@@ -4524,11 +4531,16 @@
 }
 
 TEST_F(IcingSearchEngineTest, InitializeShouldLogFunctionLatency) {
-  IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
+  TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                              std::make_unique<Filesystem>(),
+                              std::make_unique<IcingFilesystem>(),
+                              std::move(fake_clock), GetTestJniCache());
   InitializeResultProto initialize_result_proto = icing.Initialize();
   EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
   EXPECT_THAT(initialize_result_proto.native_initialize_stats().latency_ms(),
-              Gt(0));
+              Eq(10));
 }
 
 TEST_F(IcingSearchEngineTest, InitializeShouldLogNumberOfDocuments) {
@@ -4580,7 +4592,14 @@
 
 TEST_F(IcingSearchEngineTest,
        InitializeShouldNotLogRecoveryCauseForFirstTimeInitialize) {
-  IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+  // Even though the fake timer will return 10, all the latency numbers related
+  // to recovery / restoration should be 0 during the first-time initialization.
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
+  TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                              std::make_unique<Filesystem>(),
+                              std::make_unique<IcingFilesystem>(),
+                              std::move(fake_clock), GetTestJniCache());
   InitializeResultProto initialize_result_proto = icing.Initialize();
   EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
   EXPECT_THAT(initialize_result_proto.native_initialize_stats()
@@ -4637,7 +4656,12 @@
   {
     // Document store will rewind to previous checkpoint. The cause should be
     // DATA_LOSS and the data status should be PARTIAL_LOSS.
-    IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+    auto fake_clock = std::make_unique<FakeClock>();
+    fake_clock->SetTimerElapsedMilliseconds(10);
+    TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                                std::make_unique<Filesystem>(),
+                                std::make_unique<IcingFilesystem>(),
+                                std::move(fake_clock), GetTestJniCache());
     InitializeResultProto initialize_result_proto = icing.Initialize();
     EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
@@ -4645,7 +4669,7 @@
                 Eq(NativeInitializeStats::DATA_LOSS));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_recovery_latency_ms(),
-                Gt(0));
+                Eq(10));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_data_status(),
                 Eq(NativeInitializeStats::PARTIAL_LOSS));
@@ -4669,21 +4693,14 @@
   DocumentProto document1 = DocumentBuilder()
                                 .SetKey("icing", "fake_type/1")
                                 .SetSchema("Message")
-                                .AddStringProperty("body", kIpsumText)
+                                .AddStringProperty("body", "message body")
                                 .Build();
-  DocumentProto document2 = DocumentBuilder()
-                                .SetKey("icing", "fake_type/2")
-                                .SetSchema("Message")
-                                .AddStringProperty("body", kIpsumText)
-                                .Build();
-
   {
     // Initialize and put a document.
     IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
     ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
     ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
     EXPECT_THAT(icing.Put(document1).status(), ProtoIsOk());
-    EXPECT_THAT(icing.Put(document2).status(), ProtoIsOk());
   }
 
   {
@@ -4708,7 +4725,12 @@
   {
     // Document store will completely rewind. The cause should be DATA_LOSS and
     // the data status should be COMPLETE_LOSS.
-    IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+    auto fake_clock = std::make_unique<FakeClock>();
+    fake_clock->SetTimerElapsedMilliseconds(10);
+    TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                                std::make_unique<Filesystem>(),
+                                std::make_unique<IcingFilesystem>(),
+                                std::move(fake_clock), GetTestJniCache());
     InitializeResultProto initialize_result_proto = icing.Initialize();
     EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
@@ -4716,7 +4738,7 @@
                 Eq(NativeInitializeStats::DATA_LOSS));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_recovery_latency_ms(),
-                Gt(0));
+                Eq(10));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_data_status(),
                 Eq(NativeInitializeStats::COMPLETE_LOSS));
@@ -4725,9 +4747,9 @@
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .index_restoration_cause(),
                 Eq(NativeInitializeStats::TOTAL_CHECKSUM_MISMATCH));
-    // Here we don't check index_restoration_latency_ms because the index
-    // restoration is super fast when document store is emtpy. We won't get a
-    // latency that is greater than 1 ms.
+    EXPECT_THAT(initialize_result_proto.native_initialize_stats()
+                    .index_restoration_latency_ms(),
+                Eq(10));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .schema_store_recovery_cause(),
                 Eq(NativeInitializeStats::NONE));
@@ -4761,7 +4783,12 @@
   {
     // Index is empty but ground truth is not. Index should be restored due to
     // the inconsistency.
-    IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+    auto fake_clock = std::make_unique<FakeClock>();
+    fake_clock->SetTimerElapsedMilliseconds(10);
+    TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                                std::make_unique<Filesystem>(),
+                                std::make_unique<IcingFilesystem>(),
+                                std::move(fake_clock), GetTestJniCache());
     InitializeResultProto initialize_result_proto = icing.Initialize();
     EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
@@ -4769,7 +4796,7 @@
                 Eq(NativeInitializeStats::INCONSISTENT_WITH_GROUND_TRUTH));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .index_restoration_latency_ms(),
-                Gt(0));
+                Eq(10));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_recovery_cause(),
                 Eq(NativeInitializeStats::NONE));
@@ -4790,23 +4817,17 @@
 
 TEST_F(IcingSearchEngineTest,
        InitializeShouldLogRecoveryCauseTotalChecksumMismatch) {
+  DocumentProto document = DocumentBuilder()
+                               .SetKey("icing", "fake_type/0")
+                               .SetSchema("Message")
+                               .AddStringProperty("body", "message body")
+                               .Build();
   {
-    // Initialize and index some documents.
+    // Initialize and put one document.
     IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
     ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
     ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
-
-    // We need to index enough documents to make
-    // DocumentStore::UpdateSchemaStore() run longer than 1 ms.
-    for (int i = 0; i < 50; ++i) {
-      DocumentProto document =
-          DocumentBuilder()
-              .SetKey("icing", "fake_type/" + std::to_string(i))
-              .SetSchema("Message")
-              .AddStringProperty("body", "message body")
-              .Build();
-      ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
-    }
+    ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
   }
 
   {
@@ -4819,7 +4840,12 @@
 
   {
     // Both document store and index should be recovered from checksum mismatch.
-    IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+    auto fake_clock = std::make_unique<FakeClock>();
+    fake_clock->SetTimerElapsedMilliseconds(10);
+    TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                                std::make_unique<Filesystem>(),
+                                std::make_unique<IcingFilesystem>(),
+                                std::move(fake_clock), GetTestJniCache());
     InitializeResultProto initialize_result_proto = icing.Initialize();
     EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
@@ -4827,13 +4853,13 @@
                 Eq(NativeInitializeStats::TOTAL_CHECKSUM_MISMATCH));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .index_restoration_latency_ms(),
-                Gt(0));
+                Eq(10));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_recovery_cause(),
                 Eq(NativeInitializeStats::TOTAL_CHECKSUM_MISMATCH));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_recovery_latency_ms(),
-                Gt(0));
+                Eq(10));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_data_status(),
                 Eq(NativeInitializeStats::NO_DATA_LOSS));
@@ -4847,23 +4873,17 @@
 }
 
 TEST_F(IcingSearchEngineTest, InitializeShouldLogRecoveryCauseIndexIOError) {
+  DocumentProto document = DocumentBuilder()
+                               .SetKey("icing", "fake_type/0")
+                               .SetSchema("Message")
+                               .AddStringProperty("body", "message body")
+                               .Build();
   {
-    // Initialize and index some documents.
+    // Initialize and put one document.
     IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
     ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
     ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
-
-    // We need to index enough documents to make RestoreIndexIfNeeded() run
-    // longer than 1 ms.
-    for (int i = 0; i < 50; ++i) {
-      DocumentProto document =
-          DocumentBuilder()
-              .SetKey("icing", "fake_type/" + std::to_string(i))
-              .SetSchema("Message")
-              .AddStringProperty("body", "message body")
-              .Build();
-      ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
-    }
+    ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
   }
 
   // lambda to fail OpenForWrite on lite index hit buffer once.
@@ -4884,10 +4904,12 @@
   ON_CALL(*mock_icing_filesystem, OpenForWrite)
       .WillByDefault(open_write_lambda);
 
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
   TestIcingSearchEngine icing(GetDefaultIcingOptions(),
                               std::make_unique<Filesystem>(),
                               std::move(mock_icing_filesystem),
-                              std::make_unique<FakeClock>(), GetTestJniCache());
+                              std::move(fake_clock), GetTestJniCache());
 
   InitializeResultProto initialize_result_proto = icing.Initialize();
   EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
@@ -4896,7 +4918,7 @@
               Eq(NativeInitializeStats::IO_ERROR));
   EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                   .index_restoration_latency_ms(),
-              Gt(0));
+              Eq(10));
   EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                   .document_store_recovery_cause(),
               Eq(NativeInitializeStats::NONE));
@@ -4915,23 +4937,17 @@
 }
 
 TEST_F(IcingSearchEngineTest, InitializeShouldLogRecoveryCauseDocStoreIOError) {
+  DocumentProto document = DocumentBuilder()
+                               .SetKey("icing", "fake_type/0")
+                               .SetSchema("Message")
+                               .AddStringProperty("body", "message body")
+                               .Build();
   {
-    // Initialize and index some documents.
+    // Initialize and put one document.
     IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
     ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
     ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
-
-    // We need to index enough documents to make RestoreIndexIfNeeded() run
-    // longer than 1 ms.
-    for (int i = 0; i < 50; ++i) {
-      DocumentProto document =
-          DocumentBuilder()
-              .SetKey("icing", "fake_type/" + std::to_string(i))
-              .SetSchema("Message")
-              .AddStringProperty("body", "message body")
-              .Build();
-      ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
-    }
+    ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
   }
 
   // lambda to fail Read on document store header once.
@@ -4954,10 +4970,12 @@
   ON_CALL(*mock_filesystem, Read(A<const char*>(), _, _))
       .WillByDefault(read_lambda);
 
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
   TestIcingSearchEngine icing(GetDefaultIcingOptions(),
                               std::move(mock_filesystem),
                               std::make_unique<IcingFilesystem>(),
-                              std::make_unique<FakeClock>(), GetTestJniCache());
+                              std::move(fake_clock), GetTestJniCache());
 
   InitializeResultProto initialize_result_proto = icing.Initialize();
   EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
@@ -4966,7 +4984,7 @@
               Eq(NativeInitializeStats::IO_ERROR));
   EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                   .document_store_recovery_latency_ms(),
-              Gt(0));
+              Eq(10));
   EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                   .document_store_data_status(),
               Eq(NativeInitializeStats::NO_DATA_LOSS));
@@ -5000,7 +5018,12 @@
   }
 
   {
-    IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+    auto fake_clock = std::make_unique<FakeClock>();
+    fake_clock->SetTimerElapsedMilliseconds(10);
+    TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                                std::make_unique<Filesystem>(),
+                                std::make_unique<IcingFilesystem>(),
+                                std::move(fake_clock), GetTestJniCache());
     InitializeResultProto initialize_result_proto = icing.Initialize();
     EXPECT_THAT(initialize_result_proto.status(), ProtoIsOk());
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
@@ -5008,7 +5031,7 @@
                 Eq(NativeInitializeStats::IO_ERROR));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .schema_store_recovery_latency_ms(),
-                Gt(0));
+                Eq(10));
     EXPECT_THAT(initialize_result_proto.native_initialize_stats()
                     .document_store_recovery_cause(),
                 Eq(NativeInitializeStats::NONE));
@@ -5085,28 +5108,34 @@
                                .AddStringProperty("body", "message body")
                                .Build();
 
-  IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
+  TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                              std::make_unique<Filesystem>(),
+                              std::make_unique<IcingFilesystem>(),
+                              std::move(fake_clock), GetTestJniCache());
   ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
   ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
 
   PutResultProto put_result_proto = icing.Put(document);
   EXPECT_THAT(put_result_proto.status(), ProtoIsOk());
-  EXPECT_THAT(put_result_proto.native_put_document_stats().latency_ms(), Gt(0));
+  EXPECT_THAT(put_result_proto.native_put_document_stats().latency_ms(),
+              Eq(10));
 }
 
 TEST_F(IcingSearchEngineTest, PutDocumentShouldLogDocumentStoreStats) {
-  // Create a large enough document so that document_store_latency_ms can be
-  // longer than 1 ms.
-  std::default_random_engine random;
-  std::string random_string_10000 =
-      RandomString(kAlNumAlphabet, /*len=*/10000, &random);
   DocumentProto document = DocumentBuilder()
                                .SetKey("icing", "fake_type/0")
                                .SetSchema("Message")
-                               .AddStringProperty("body", random_string_10000)
+                               .AddStringProperty("body", "message body")
                                .Build();
 
-  IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
+  TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                              std::make_unique<Filesystem>(),
+                              std::make_unique<IcingFilesystem>(),
+                              std::move(fake_clock), GetTestJniCache());
   ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
   ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
 
@@ -5114,28 +5143,31 @@
   EXPECT_THAT(put_result_proto.status(), ProtoIsOk());
   EXPECT_THAT(
       put_result_proto.native_put_document_stats().document_store_latency_ms(),
-      Gt(0));
+      Eq(10));
   EXPECT_THAT(put_result_proto.native_put_document_stats().document_size(),
               Eq(document.ByteSizeLong()));
 }
 
 TEST_F(IcingSearchEngineTest, PutDocumentShouldLogIndexingStats) {
-  // Create a large enough document so that index_latency_ms can be longer than
-  // 1 ms.
   DocumentProto document = DocumentBuilder()
                                .SetKey("icing", "fake_type/0")
                                .SetSchema("Message")
-                               .AddStringProperty("body", kIpsumText)
+                               .AddStringProperty("body", "message body")
                                .Build();
 
-  IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
+  TestIcingSearchEngine icing(GetDefaultIcingOptions(),
+                              std::make_unique<Filesystem>(),
+                              std::make_unique<IcingFilesystem>(),
+                              std::move(fake_clock), GetTestJniCache());
   ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
   ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
 
   PutResultProto put_result_proto = icing.Put(document);
   EXPECT_THAT(put_result_proto.status(), ProtoIsOk());
   EXPECT_THAT(put_result_proto.native_put_document_stats().index_latency_ms(),
-              Gt(0));
+              Eq(10));
   // No merge should happen.
   EXPECT_THAT(
       put_result_proto.native_put_document_stats().index_merge_latency_ms(),
@@ -5144,11 +5176,11 @@
   EXPECT_FALSE(put_result_proto.native_put_document_stats()
                    .tokenization_stats()
                    .exceeded_max_token_num());
-  // kIpsumText has 137 tokens.
+  // The input document has 2 tokens.
   EXPECT_THAT(put_result_proto.native_put_document_stats()
                   .tokenization_stats()
                   .num_tokens_indexed(),
-              Eq(137));
+              Eq(2));
 }
 
 TEST_F(IcingSearchEngineTest, PutDocumentShouldLogWhetherNumTokensExceeds) {
@@ -5179,8 +5211,6 @@
 }
 
 TEST_F(IcingSearchEngineTest, PutDocumentShouldLogIndexMergeLatency) {
-  // Create 2 large enough documents so that index_merge_latency_ms can be
-  // longer than 1 ms.
   DocumentProto document1 = DocumentBuilder()
                                 .SetKey("icing", "fake_type/1")
                                 .SetSchema("Message")
@@ -5195,7 +5225,12 @@
   // Create an icing instance with index_merge_size = document1's size.
   IcingSearchEngineOptions icing_options = GetDefaultIcingOptions();
   icing_options.set_index_merge_size(document1.ByteSizeLong());
-  IcingSearchEngine icing(icing_options, GetTestJniCache());
+
+  auto fake_clock = std::make_unique<FakeClock>();
+  fake_clock->SetTimerElapsedMilliseconds(10);
+  TestIcingSearchEngine icing(icing_options, std::make_unique<Filesystem>(),
+                              std::make_unique<IcingFilesystem>(),
+                              std::move(fake_clock), GetTestJniCache());
   ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
   ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
   EXPECT_THAT(icing.Put(document1).status(), ProtoIsOk());
@@ -5205,7 +5240,7 @@
   EXPECT_THAT(put_result_proto.status(), ProtoIsOk());
   EXPECT_THAT(
       put_result_proto.native_put_document_stats().index_merge_latency_ms(),
-      Gt(0));
+      Eq(10));
 }
 
 }  // namespace
diff --git a/icing/index/index-processor.cc b/icing/index/index-processor.cc
index 9e57993..9df9b87 100644
--- a/icing/index/index-processor.cc
+++ b/icing/index/index-processor.cc
@@ -37,7 +37,6 @@
 #include "icing/tokenization/tokenizer.h"
 #include "icing/transform/normalizer.h"
 #include "icing/util/status-macros.h"
-#include "icing/util/timer.h"
 
 namespace icing {
 namespace lib {
@@ -46,20 +45,22 @@
 IndexProcessor::Create(const SchemaStore* schema_store,
                        const LanguageSegmenter* lang_segmenter,
                        const Normalizer* normalizer, Index* index,
-                       const IndexProcessor::Options& options) {
+                       const IndexProcessor::Options& options,
+                       const Clock* clock) {
   ICING_RETURN_ERROR_IF_NULL(schema_store);
   ICING_RETURN_ERROR_IF_NULL(lang_segmenter);
   ICING_RETURN_ERROR_IF_NULL(normalizer);
   ICING_RETURN_ERROR_IF_NULL(index);
+  ICING_RETURN_ERROR_IF_NULL(clock);
 
   return std::unique_ptr<IndexProcessor>(new IndexProcessor(
-      schema_store, lang_segmenter, normalizer, index, options));
+      schema_store, lang_segmenter, normalizer, index, options, clock));
 }
 
 libtextclassifier3::Status IndexProcessor::IndexDocument(
     const DocumentProto& document, DocumentId document_id,
     NativePutDocumentStats* put_document_stats) {
-  Timer index_timer;
+  std::unique_ptr<Timer> index_timer = clock_.GetNewTimer();
 
   if (index_->last_added_document_id() != kInvalidDocumentId &&
       document_id <= index_->last_added_document_id()) {
@@ -118,7 +119,7 @@
 
   if (put_document_stats != nullptr) {
     put_document_stats->set_index_latency_ms(
-        index_timer.GetElapsedMilliseconds());
+        index_timer->GetElapsedMilliseconds());
     put_document_stats->mutable_tokenization_stats()->set_num_tokens_indexed(
         num_tokens);
   }
@@ -127,7 +128,7 @@
   if (overall_status.ok() && index_->WantsMerge()) {
     ICING_VLOG(1) << "Merging the index at docid " << document_id << ".";
 
-    Timer merge_timer;
+    std::unique_ptr<Timer> merge_timer = clock_.GetNewTimer();
     libtextclassifier3::Status merge_status = index_->Merge();
 
     if (!merge_status.ok()) {
@@ -146,7 +147,7 @@
 
     if (put_document_stats != nullptr) {
       put_document_stats->set_index_merge_latency_ms(
-          merge_timer.GetElapsedMilliseconds());
+          merge_timer->GetElapsedMilliseconds());
     }
   }
 
diff --git a/icing/index/index-processor.h b/icing/index/index-processor.h
index 91719d0..2eb4ad8 100644
--- a/icing/index/index-processor.h
+++ b/icing/index/index-processor.h
@@ -59,7 +59,8 @@
   //   FAILED_PRECONDITION if any of the pointers is null.
   static libtextclassifier3::StatusOr<std::unique_ptr<IndexProcessor>> Create(
       const SchemaStore* schema_store, const LanguageSegmenter* lang_segmenter,
-      const Normalizer* normalizer, Index* index, const Options& options);
+      const Normalizer* normalizer, Index* index, const Options& options,
+      const Clock* clock);
 
   // Add document to the index, associated with document_id. If the number of
   // tokens in the document exceeds max_tokens_per_document, then only the first
@@ -88,12 +89,13 @@
   IndexProcessor(const SchemaStore* schema_store,
                  const LanguageSegmenter* lang_segmenter,
                  const Normalizer* normalizer, Index* index,
-                 const Options& options)
+                 const Options& options, const Clock* clock)
       : schema_store_(*schema_store),
         lang_segmenter_(*lang_segmenter),
         normalizer_(*normalizer),
         index_(index),
-        options_(options) {}
+        options_(options),
+        clock_(*clock) {}
 
   std::string NormalizeToken(const Token& token);
 
@@ -102,6 +104,7 @@
   const Normalizer& normalizer_;
   Index* const index_;
   const Options options_;
+  const Clock& clock_;
 };
 
 }  // namespace lib
diff --git a/icing/index/index-processor_benchmark.cc b/icing/index/index-processor_benchmark.cc
index 584cb9b..96a390b 100644
--- a/icing/index/index-processor_benchmark.cc
+++ b/icing/index/index-processor_benchmark.cc
@@ -147,10 +147,10 @@
       .ValueOrDie();
 }
 
-std::unique_ptr<SchemaStore> CreateSchemaStore() {
+std::unique_ptr<SchemaStore> CreateSchemaStore(const Clock* clock) {
   Filesystem filesystem;
   std::unique_ptr<SchemaStore> schema_store =
-      SchemaStore::Create(&filesystem, GetTestTempDir()).ValueOrDie();
+      SchemaStore::Create(&filesystem, GetTestTempDir(), clock).ValueOrDie();
 
   SchemaProto schema;
   CreateFakeTypeConfig(schema.add_types());
@@ -170,14 +170,14 @@
 std::unique_ptr<IndexProcessor> CreateIndexProcessor(
     const SchemaStore* schema_store,
     const LanguageSegmenter* language_segmenter, const Normalizer* normalizer,
-    Index* index) {
+    Index* index, const Clock* clock) {
   IndexProcessor::Options processor_options{};
   processor_options.max_tokens_per_document = 1024 * 1024 * 10;
   processor_options.token_limit_behavior =
       IndexProcessor::Options::TokenLimitBehavior::kReturnError;
 
   return IndexProcessor::Create(schema_store, language_segmenter, normalizer,
-                                index, processor_options)
+                                index, processor_options, clock)
       .ValueOrDie();
 }
 
@@ -200,10 +200,11 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore();
+  Clock clock;
+  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore(&clock);
   std::unique_ptr<IndexProcessor> index_processor =
       CreateIndexProcessor(schema_store.get(), language_segmenter.get(),
-                           normalizer.get(), index.get());
+                           normalizer.get(), index.get(), &clock);
 
   DocumentProto input_document = CreateDocumentWithOneProperty(state.range(0));
 
@@ -250,10 +251,11 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore();
+  Clock clock;
+  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore(&clock);
   std::unique_ptr<IndexProcessor> index_processor =
       CreateIndexProcessor(schema_store.get(), language_segmenter.get(),
-                           normalizer.get(), index.get());
+                           normalizer.get(), index.get(), &clock);
 
   DocumentProto input_document =
       CreateDocumentWithTenProperties(state.range(0));
@@ -301,10 +303,11 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore();
+  Clock clock;
+  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore(&clock);
   std::unique_ptr<IndexProcessor> index_processor =
       CreateIndexProcessor(schema_store.get(), language_segmenter.get(),
-                           normalizer.get(), index.get());
+                           normalizer.get(), index.get(), &clock);
 
   DocumentProto input_document =
       CreateDocumentWithDiacriticLetters(state.range(0));
@@ -352,10 +355,11 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore();
+  Clock clock;
+  std::unique_ptr<SchemaStore> schema_store = CreateSchemaStore(&clock);
   std::unique_ptr<IndexProcessor> index_processor =
       CreateIndexProcessor(schema_store.get(), language_segmenter.get(),
-                           normalizer.get(), index.get());
+                           normalizer.get(), index.get(), &clock);
 
   DocumentProto input_document = CreateDocumentWithHiragana(state.range(0));
 
diff --git a/icing/index/index-processor_test.cc b/icing/index/index-processor_test.cc
index e193842..ff11e60 100644
--- a/icing/index/index-processor_test.cc
+++ b/icing/index/index-processor_test.cc
@@ -44,6 +44,8 @@
 #include "icing/schema/section.h"
 #include "icing/store/document-id.h"
 #include "icing/testing/common-matchers.h"
+#include "icing/testing/fake-clock.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/test-data.h"
 #include "icing/testing/tmp-directory.h"
 #include "icing/tokenization/language-segmenter-factory.h"
@@ -102,10 +104,12 @@
 class IndexProcessorTest : public Test {
  protected:
   void SetUp() override {
-    ICING_ASSERT_OK(
-        // File generated via icu_data_file rule in //icing/BUILD.
-        icu_data_file_helper::SetUpICUDataFile(
-            GetTestFilePath("icing/icu.dat")));
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      ICING_ASSERT_OK(
+          // File generated via icu_data_file rule in //icing/BUILD.
+          icu_data_file_helper::SetUpICUDataFile(
+              GetTestFilePath("icing/icu.dat")));
+    }
 
     index_dir_ = GetTestTempDir() + "/index_test";
     Index::Options options(index_dir_, /*index_merge_size=*/1024 * 1024);
@@ -120,11 +124,11 @@
     ICING_ASSERT_OK_AND_ASSIGN(
         normalizer_,
         normalizer_factory::Create(
-
             /*max_term_byte_size=*/std::numeric_limits<int32_t>::max()));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        schema_store_, SchemaStore::Create(&filesystem_, GetTestTempDir()));
+        schema_store_,
+        SchemaStore::Create(&filesystem_, GetTestTempDir(), &fake_clock_));
     SchemaProto schema = CreateFakeSchema();
     ICING_ASSERT_OK(schema_store_->SetSchema(schema));
 
@@ -137,7 +141,7 @@
         index_processor_,
         IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
                                normalizer_.get(), index_.get(),
-                               processor_options));
+                               processor_options, &fake_clock_));
     mock_icing_filesystem_ = std::make_unique<IcingMockFilesystem>();
   }
 
@@ -149,6 +153,7 @@
 
   Filesystem filesystem_;
   IcingFilesystem icing_filesystem_;
+  FakeClock fake_clock_;
   std::string index_dir_;
 
   std::unique_ptr<LanguageSegmenter> lang_segmenter_;
@@ -238,24 +243,26 @@
   processor_options.token_limit_behavior =
       IndexProcessor::Options::TokenLimitBehavior::kReturnError;
 
-  EXPECT_THAT(IndexProcessor::Create(/*schema_store=*/nullptr,
-                                     lang_segmenter_.get(), normalizer_.get(),
-                                     index_.get(), processor_options),
-              StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
+  EXPECT_THAT(
+      IndexProcessor::Create(/*schema_store=*/nullptr, lang_segmenter_.get(),
+                             normalizer_.get(), index_.get(), processor_options,
+                             &fake_clock_),
+      StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
 
-  EXPECT_THAT(IndexProcessor::Create(
-                  schema_store_.get(), /*lang_segmenter=*/nullptr,
-                  normalizer_.get(), index_.get(), processor_options),
-              StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
+  EXPECT_THAT(
+      IndexProcessor::Create(schema_store_.get(), /*lang_segmenter=*/nullptr,
+                             normalizer_.get(), index_.get(), processor_options,
+                             &fake_clock_),
+      StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
 
   EXPECT_THAT(IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
                                      /*normalizer=*/nullptr, index_.get(),
-                                     processor_options),
+                                     processor_options, &fake_clock_),
               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
 
   EXPECT_THAT(IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
                                      normalizer_.get(), /*index=*/nullptr,
-                                     processor_options),
+                                     processor_options, &fake_clock_),
               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
 }
 
@@ -389,7 +396,8 @@
   ICING_ASSERT_OK_AND_ASSIGN(
       index_processor_,
       IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
-                             normalizer_.get(), index_.get(), options));
+                             normalizer_.get(), index_.get(), options,
+                             &fake_clock_));
 
   DocumentProto document =
       DocumentBuilder()
@@ -428,7 +436,8 @@
   ICING_ASSERT_OK_AND_ASSIGN(
       index_processor_,
       IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
-                             normalizer_.get(), index_.get(), options));
+                             normalizer_.get(), index_.get(), options,
+                             &fake_clock_));
 
   DocumentProto document =
       DocumentBuilder()
@@ -468,7 +477,8 @@
   ICING_ASSERT_OK_AND_ASSIGN(
       index_processor_,
       IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
-                             normalizer.get(), index_.get(), options));
+                             normalizer.get(), index_.get(), options,
+                             &fake_clock_));
 
   DocumentProto document =
       DocumentBuilder()
@@ -590,6 +600,23 @@
 }
 
 TEST_F(IndexProcessorTest, NonAsciiIndexing) {
+  language_segmenter_factory::SegmenterOptions segmenter_options(
+      ULOC_SIMPLIFIED_CHINESE);
+  ICING_ASSERT_OK_AND_ASSIGN(
+      lang_segmenter_,
+      language_segmenter_factory::Create(std::move(segmenter_options)));
+
+  IndexProcessor::Options processor_options;
+  processor_options.max_tokens_per_document = 1000;
+  processor_options.token_limit_behavior =
+      IndexProcessor::Options::TokenLimitBehavior::kReturnError;
+
+  ICING_ASSERT_OK_AND_ASSIGN(
+      index_processor_,
+      IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
+                             normalizer_.get(), index_.get(),
+                             processor_options, &fake_clock_));
+
   DocumentProto document =
       DocumentBuilder()
           .SetKey("icing", "fake_type/1")
@@ -618,8 +645,8 @@
   ICING_ASSERT_OK_AND_ASSIGN(
       index_processor_,
       IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
-                             normalizer_.get(), index_.get(),
-                             processor_options));
+                             normalizer_.get(), index_.get(), processor_options,
+                             &fake_clock_));
 
   // This is the maximum token length that an empty lexicon constructed for a
   // lite index with merge size of 1MiB can support.
@@ -679,8 +706,8 @@
   ICING_ASSERT_OK_AND_ASSIGN(
       index_processor_,
       IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
-                             normalizer_.get(), index_.get(),
-                             processor_options));
+                             normalizer_.get(), index_.get(), processor_options,
+                             &fake_clock_));
   DocumentId doc_id = 0;
   // Have determined experimentally that indexing 3373 documents with this text
   // will cause the LiteIndex to fill up. Further indexing will fail unless the
@@ -736,8 +763,8 @@
   ICING_ASSERT_OK_AND_ASSIGN(
       index_processor_,
       IndexProcessor::Create(schema_store_.get(), lang_segmenter_.get(),
-                             normalizer_.get(), index_.get(),
-                             processor_options));
+                             normalizer_.get(), index_.get(), processor_options,
+                             &fake_clock_));
 
   // 3. Index one document. This should fit in the LiteIndex without requiring a
   // merge.
diff --git a/icing/index/index.cc b/icing/index/index.cc
index 1fb0dc0..1b808f8 100644
--- a/icing/index/index.cc
+++ b/icing/index/index.cc
@@ -71,7 +71,7 @@
 }
 
 // Helper function to check if a term is in the given namespaces.
-// TODO(samzheng): Implement a method PropertyReadersAll.HasAnyProperty().
+// TODO(tjbarron): Implement a method PropertyReadersAll.HasAnyProperty().
 bool IsTermInNamespaces(
     const IcingDynamicTrie::PropertyReadersAll& property_reader,
     uint32_t value_index, const std::vector<NamespaceId>& namespace_ids) {
diff --git a/icing/index/iterator/doc-hit-info-iterator-and.cc b/icing/index/iterator/doc-hit-info-iterator-and.cc
index f224583..66f87bd 100644
--- a/icing/index/iterator/doc-hit-info-iterator-and.cc
+++ b/icing/index/iterator/doc-hit-info-iterator-and.cc
@@ -38,8 +38,6 @@
 
 // When combining ANDed iterators, n-ary operator has better performance when
 // number of operands > 3 according to benchmark cl/243720660
-// TODO (samzheng): Tune this number when it's necessary, e.g. implementation
-// changes.
 inline constexpr int kBinaryAndIteratorPerformanceThreshold = 3;
 
 // The minimum number of iterators needed to construct a And iterator. The And
diff --git a/icing/index/iterator/doc-hit-info-iterator-filter_test.cc b/icing/index/iterator/doc-hit-info-iterator-filter_test.cc
index 9eb147a..e0a8cd0 100644
--- a/icing/index/iterator/doc-hit-info-iterator-filter_test.cc
+++ b/icing/index/iterator/doc-hit-info-iterator-filter_test.cc
@@ -63,14 +63,16 @@
     auto type_config = schema.add_types();
     type_config->set_schema_type("email");
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     ICING_ASSERT_OK(schema_store_->SetSchema(schema));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
   }
 
   void TearDown() override {
@@ -228,14 +230,16 @@
     auto type_config = schema.add_types();
     type_config->set_schema_type("email");
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     ICING_ASSERT_OK(schema_store_->SetSchema(schema));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
   }
 
   void TearDown() override {
@@ -383,14 +387,16 @@
     type_config = schema.add_types();
     type_config->set_schema_type(schema3_);
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     ICING_ASSERT_OK(schema_store_->SetSchema(schema));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
   }
 
   void TearDown() override {
@@ -521,14 +527,16 @@
     auto type_config = schema.add_types();
     type_config->set_schema_type(email_schema_);
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     ICING_ASSERT_OK(schema_store_->SetSchema(schema));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
   }
 
   void TearDown() override {
@@ -711,14 +719,16 @@
     type_config = schema.add_types();
     type_config->set_schema_type(schema2_);
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     ICING_ASSERT_OK(schema_store_->SetSchema(schema));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
   }
 
   void TearDown() override {
diff --git a/icing/index/iterator/doc-hit-info-iterator-or.cc b/icing/index/iterator/doc-hit-info-iterator-or.cc
index 9d18753..8f00f88 100644
--- a/icing/index/iterator/doc-hit-info-iterator-or.cc
+++ b/icing/index/iterator/doc-hit-info-iterator-or.cc
@@ -29,8 +29,6 @@
 
 // When combining Or iterators, n-ary operator has better performance when
 // number of operands > 2 according to benchmark cl/243321264
-// TODO (samzheng): Tune this number when it's necessary, e.g. implementation
-// changes.
 constexpr int kBinaryOrIteratorPerformanceThreshold = 2;
 
 }  // namespace
diff --git a/icing/index/iterator/doc-hit-info-iterator-section-restrict_test.cc b/icing/index/iterator/doc-hit-info-iterator-section-restrict_test.cc
index b29217c..91e0cbe 100644
--- a/icing/index/iterator/doc-hit-info-iterator-section-restrict_test.cc
+++ b/icing/index/iterator/doc-hit-info-iterator-section-restrict_test.cc
@@ -72,14 +72,16 @@
     // First and only indexed property, so it gets the first id of 0
     indexed_section_id_ = 0;
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     ICING_ASSERT_OK(schema_store_->SetSchema(schema_));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
   }
 
   void TearDown() override {
diff --git a/icing/index/main/main-index.cc b/icing/index/main/main-index.cc
index 2a5ba83..ff1c47a 100644
--- a/icing/index/main/main-index.cc
+++ b/icing/index/main/main-index.cc
@@ -173,11 +173,12 @@
   ICING_ASSIGN_OR_RETURN(PostingListAccessor pl_accessor,
                          PostingListAccessor::CreateFromExisting(
                              flash_index_storage_.get(), posting_list_id));
-  GetPrefixAccessorResult result = {std::make_unique<PostingListAccessor>(std::move(pl_accessor)), exact};
+  GetPrefixAccessorResult result = {
+      std::make_unique<PostingListAccessor>(std::move(pl_accessor)), exact};
   return result;
 }
 
-// TODO(samzheng): Implement a method PropertyReadersAll.HasAnyProperty().
+// TODO(tjbarron): Implement a method PropertyReadersAll.HasAnyProperty().
 bool IsTermInNamespaces(
     const IcingDynamicTrie::PropertyReadersAll& property_reader,
     uint32_t value_index, const std::vector<NamespaceId>& namespace_ids) {
diff --git a/icing/jni/icing-search-engine-jni.cc b/icing/jni/icing-search-engine-jni.cc
index 71752dd..1e20340 100644
--- a/icing/jni/icing-search-engine-jni.cc
+++ b/icing/jni/icing-search-engine-jni.cc
@@ -27,6 +27,7 @@
 #include "icing/proto/schema.pb.h"
 #include "icing/proto/scoring.pb.h"
 #include "icing/proto/search.pb.h"
+#include "icing/proto/usage.pb.h"
 #include "icing/util/status-macros.h"
 
 namespace {
@@ -96,6 +97,14 @@
   return reinterpret_cast<jlong>(icing);
 }
 
+JNIEXPORT void JNICALL
+Java_com_google_android_icing_IcingSearchEngine_nativeDestroy(
+    JNIEnv* env, jclass clazz, jlong native_pointer) {
+  icing::lib::IcingSearchEngine* icing =
+      GetIcingSearchEnginePointer(native_pointer);
+  delete icing;
+}
+
 JNIEXPORT jbyteArray JNICALL
 Java_com_google_android_icing_IcingSearchEngine_nativeInitialize(
     JNIEnv* env, jclass clazz, jlong native_pointer) {
@@ -188,6 +197,25 @@
 }
 
 JNIEXPORT jbyteArray JNICALL
+Java_com_google_android_icing_IcingSearchEngine_nativeReportUsage(
+    JNIEnv* env, jclass clazz, jlong native_pointer,
+    jbyteArray usage_report_bytes) {
+  icing::lib::IcingSearchEngine* icing =
+      GetIcingSearchEnginePointer(native_pointer);
+
+  icing::lib::UsageReport usage_report;
+  if (!ParseProtoFromJniByteArray(env, usage_report_bytes, &usage_report)) {
+    ICING_LOG(ERROR) << "Failed to parse UsageReport in nativeReportUsage";
+    return nullptr;
+  }
+
+  icing::lib::ReportUsageResultProto report_usage_result_proto =
+      icing->ReportUsage(usage_report);
+
+  return SerializeProtoToJniByteArray(env, report_usage_result_proto);
+}
+
+JNIEXPORT jbyteArray JNICALL
 Java_com_google_android_icing_IcingSearchEngine_nativeGetAllNamespaces(
     JNIEnv* env, jclass clazz, jlong native_pointer) {
   icing::lib::IcingSearchEngine* icing =
@@ -201,8 +229,7 @@
 
 JNIEXPORT jbyteArray JNICALL
 Java_com_google_android_icing_IcingSearchEngine_nativeGetNextPage(
-    JNIEnv* env, jclass clazz, jlong native_pointer,
-    jlong next_page_token) {
+    JNIEnv* env, jclass clazz, jlong native_pointer, jlong next_page_token) {
   icing::lib::IcingSearchEngine* icing =
       GetIcingSearchEnginePointer(native_pointer);
 
@@ -214,8 +241,7 @@
 
 JNIEXPORT void JNICALL
 Java_com_google_android_icing_IcingSearchEngine_nativeInvalidateNextPageToken(
-    JNIEnv* env, jclass clazz, jlong native_pointer,
-    jlong next_page_token) {
+    JNIEnv* env, jclass clazz, jlong native_pointer, jlong next_page_token) {
   icing::lib::IcingSearchEngine* icing =
       GetIcingSearchEnginePointer(native_pointer);
 
diff --git a/icing/query/query-processor_benchmark.cc b/icing/query/query-processor_benchmark.cc
index 900cce5..9dc5c07 100644
--- a/icing/query/query-processor_benchmark.cc
+++ b/icing/query/query-processor_benchmark.cc
@@ -24,11 +24,11 @@
 #include "icing/schema/section.h"
 #include "icing/store/document-id.h"
 #include "icing/testing/common-matchers.h"
-#include "icing/testing/fake-clock.h"
 #include "icing/testing/test-data.h"
 #include "icing/testing/tmp-directory.h"
 #include "icing/tokenization/language-segmenter-factory.h"
 #include "icing/transform/normalizer-factory.h"
+#include "icing/util/clock.h"
 #include "icing/util/logging.h"
 #include "unicode/uloc.h"
 
@@ -114,19 +114,22 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  FakeClock fake_clock;
 
   SchemaProto schema;
   auto type_config = schema.add_types();
   type_config->set_schema_type("type1");
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem, schema_dir));
+  Clock clock;
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem, schema_dir, &clock));
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
-  std::unique_ptr<DocumentStore> document_store =
-      DocumentStore::Create(&filesystem, doc_store_dir, &fake_clock,
+  DocumentStore::CreateResult create_result =
+      DocumentStore::Create(&filesystem, doc_store_dir, &clock,
                             schema_store.get())
           .ValueOrDie();
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   DocumentId document_id = document_store
                                ->Put(DocumentBuilder()
@@ -143,7 +146,7 @@
       std::unique_ptr<QueryProcessor> query_processor,
       QueryProcessor::Create(index.get(), language_segmenter.get(),
                              normalizer.get(), document_store.get(),
-                             schema_store.get(), &fake_clock));
+                             schema_store.get(), &clock));
 
   SearchSpecProto search_spec;
   search_spec.set_query(input_string);
@@ -228,19 +231,22 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  FakeClock fake_clock;
 
   SchemaProto schema;
   auto type_config = schema.add_types();
   type_config->set_schema_type("type1");
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem, schema_dir));
+  Clock clock;
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem, schema_dir, &clock));
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
-  std::unique_ptr<DocumentStore> document_store =
-      DocumentStore::Create(&filesystem, doc_store_dir, &fake_clock,
+  DocumentStore::CreateResult create_result =
+      DocumentStore::Create(&filesystem, doc_store_dir, &clock,
                             schema_store.get())
           .ValueOrDie();
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   DocumentId document_id = document_store
                                ->Put(DocumentBuilder()
@@ -271,7 +277,7 @@
       std::unique_ptr<QueryProcessor> query_processor,
       QueryProcessor::Create(index.get(), language_segmenter.get(),
                              normalizer.get(), document_store.get(),
-                             schema_store.get(), &fake_clock));
+                             schema_store.get(), &clock));
 
   const std::string query_string = absl_ports::StrCat(
       input_string_a, " ", input_string_b, " ", input_string_c, " ",
@@ -360,19 +366,22 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  FakeClock fake_clock;
 
   SchemaProto schema;
   auto type_config = schema.add_types();
   type_config->set_schema_type("type1");
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem, schema_dir));
+  Clock clock;
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem, schema_dir, &clock));
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
-  std::unique_ptr<DocumentStore> document_store =
-      DocumentStore::Create(&filesystem, doc_store_dir, &fake_clock,
+  DocumentStore::CreateResult create_result =
+      DocumentStore::Create(&filesystem, doc_store_dir, &clock,
                             schema_store.get())
           .ValueOrDie();
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   DocumentId document_id = document_store
                                ->Put(DocumentBuilder()
@@ -392,7 +401,7 @@
       std::unique_ptr<QueryProcessor> query_processor,
       QueryProcessor::Create(index.get(), language_segmenter.get(),
                              normalizer.get(), document_store.get(),
-                             schema_store.get(), &fake_clock));
+                             schema_store.get(), &clock));
 
   SearchSpecProto search_spec;
   search_spec.set_query(input_string);
@@ -477,19 +486,22 @@
   std::unique_ptr<LanguageSegmenter> language_segmenter =
       language_segmenter_factory::Create(std::move(options)).ValueOrDie();
   std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
-  FakeClock fake_clock;
 
   SchemaProto schema;
   auto type_config = schema.add_types();
   type_config->set_schema_type("type1");
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem, schema_dir));
+  Clock clock;
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem, schema_dir, &clock));
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
-  std::unique_ptr<DocumentStore> document_store =
-      DocumentStore::Create(&filesystem, doc_store_dir, &fake_clock,
+  DocumentStore::CreateResult create_result =
+      DocumentStore::Create(&filesystem, doc_store_dir, &clock,
                             schema_store.get())
           .ValueOrDie();
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   DocumentId document_id = document_store
                                ->Put(DocumentBuilder()
@@ -509,7 +521,7 @@
       std::unique_ptr<QueryProcessor> query_processor,
       QueryProcessor::Create(index.get(), language_segmenter.get(),
                              normalizer.get(), document_store.get(),
-                             schema_store.get(), &fake_clock));
+                             schema_store.get(), &clock));
 
   SearchSpecProto search_spec;
   search_spec.set_query(input_string);
diff --git a/icing/query/query-processor_test.cc b/icing/query/query-processor_test.cc
index 16bd120..8c46736 100644
--- a/icing/query/query-processor_test.cc
+++ b/icing/query/query-processor_test.cc
@@ -39,6 +39,7 @@
 #include "icing/testing/common-matchers.h"
 #include "icing/testing/fake-clock.h"
 #include "icing/testing/jni-test-helpers.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/test-data.h"
 #include "icing/testing/tmp-directory.h"
 #include "icing/tokenization/language-segmenter-factory.h"
@@ -95,17 +96,17 @@
     filesystem_.CreateDirectoryRecursively(index_dir_.c_str());
     filesystem_.CreateDirectoryRecursively(store_dir_.c_str());
 
-#ifndef ICING_REVERSE_JNI_SEGMENTATION
-    // If we've specified using the reverse-JNI method for segmentation (i.e.
-    // not ICU), then we won't have the ICU data file included to set up.
-    // Technically, we could choose to use reverse-JNI for segmentation AND
-    // include an ICU data file, but that seems unlikely and our current BUILD
-    // setup doesn't do this.
-    ICING_ASSERT_OK(
-        // File generated via icu_data_file rule in //icing/BUILD.
-        icu_data_file_helper::SetUpICUDataFile(
-            GetTestFilePath("icing/icu.dat")));
-#endif  // ICING_REVERSE_JNI_SEGMENTATION
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      // If we've specified using the reverse-JNI method for segmentation (i.e.
+      // not ICU), then we won't have the ICU data file included to set up.
+      // Technically, we could choose to use reverse-JNI for segmentation AND
+      // include an ICU data file, but that seems unlikely and our current BUILD
+      // setup doesn't do this.
+      ICING_ASSERT_OK(
+          // File generated via icu_data_file rule in //icing/BUILD.
+          icu_data_file_helper::SetUpICUDataFile(
+              GetTestFilePath("icing/icu.dat")));
+    }
 
     Index::Options options(index_dir_,
                            /*index_merge_size=*/1024 * 1024);
@@ -188,14 +189,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
@@ -235,14 +238,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
@@ -282,14 +287,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -338,14 +345,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -391,14 +400,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -444,18 +455,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -500,18 +513,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -556,18 +571,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -612,18 +629,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -674,18 +693,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -736,18 +757,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -797,18 +820,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -891,8 +916,8 @@
   }
 
   {
-    // OR gets precedence over AND, this is parsed as (kitten AND ((foo OR bar)
-    // OR cat))
+    // OR gets precedence over AND, this is parsed as (kitten AND ((foo OR
+    // bar) OR cat))
     SearchSpecProto search_spec;
     search_spec.set_query("kitten foo OR bar OR cat");
     search_spec.set_term_match_type(term_match_type);
@@ -914,18 +939,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -985,18 +1012,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -1057,18 +1086,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -1128,18 +1159,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that the DocHitInfoIterators will see that the
-  // document exists and not filter out the DocumentId as deleted.
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that the DocHitInfoIterators will see
+  // that the document exists and not filter out the DocumentId as deleted.
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -1199,18 +1232,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that they'll bump the last_added_document_id,
-  // which will give us the proper exclusion results
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that they'll bump the
+  // last_added_document_id, which will give us the proper exclusion results
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -1247,9 +1282,9 @@
   ICING_ASSERT_OK_AND_ASSIGN(QueryProcessor::QueryResults results,
                              query_processor->ParseSearch(search_spec));
 
-  // We don't know have the section mask to indicate what section "world" came.
-  // It doesn't matter which section it was in since the query doesn't care.  It
-  // just wanted documents that didn't have "hello"
+  // We don't know have the section mask to indicate what section "world"
+  // came. It doesn't matter which section it was in since the query doesn't
+  // care.  It just wanted documents that didn't have "hello"
   EXPECT_THAT(GetDocHitInfos(results.root_iterator.get()),
               ElementsAre(DocHitInfo(document_id2, kSectionIdMaskNone)));
   EXPECT_THAT(results.query_terms, IsEmpty());
@@ -1260,18 +1295,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that they'll bump the last_added_document_id,
-  // which will give us the proper exclusion results
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that they'll bump the
+  // last_added_document_id, which will give us the proper exclusion results
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -1319,18 +1356,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that they'll bump the last_added_document_id,
-  // which will give us the proper exclusion results
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that they'll bump the
+  // last_added_document_id, which will give us the proper exclusion results
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -1375,8 +1414,8 @@
     ICING_ASSERT_OK_AND_ASSIGN(QueryProcessor::QueryResults results,
                                query_processor->ParseSearch(search_spec));
 
-    // The query is interpreted as "exclude all documents that have animal, and
-    // exclude all documents that have cat". Since both documents contain
+    // The query is interpreted as "exclude all documents that have animal,
+    // and exclude all documents that have cat". Since both documents contain
     // animal, there are no results.
     EXPECT_THAT(GetDocHitInfos(results.root_iterator.get()), IsEmpty());
     EXPECT_THAT(results.query_terms, IsEmpty());
@@ -1390,8 +1429,8 @@
     ICING_ASSERT_OK_AND_ASSIGN(QueryProcessor::QueryResults results,
                                query_processor->ParseSearch(search_spec));
 
-    // The query is interpreted as "exclude all documents that have animal, and
-    // include all documents that have cat". Since both documents contain
+    // The query is interpreted as "exclude all documents that have animal,
+    // and include all documents that have cat". Since both documents contain
     // animal, there are no results.
     EXPECT_THAT(GetDocHitInfos(results.root_iterator.get()), IsEmpty());
     EXPECT_THAT(results.query_terms, SizeIs(1));
@@ -1404,18 +1443,20 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
-  // These documents don't actually match to the tokens in the index. We're just
-  // inserting the documents so that they'll bump the last_added_document_id,
-  // which will give us the proper exclusion results
+  // These documents don't actually match to the tokens in the index. We're
+  // just inserting the documents so that they'll bump the
+  // last_added_document_id, which will give us the proper exclusion results
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              document_store_->Put(DocumentBuilder()
                                                       .SetKey("namespace", "1")
@@ -1491,14 +1532,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -1560,14 +1603,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -1630,14 +1675,16 @@
   AddSchemaType(&schema, "email");
   AddSchemaType(&schema, "message");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -1699,14 +1746,16 @@
   AddIndexedProperty(email_type, "subject");
   int subject_section_id = 0;
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -1752,24 +1801,28 @@
   // Create the schema and document store
   SchemaProto schema;
   SchemaTypeConfigProto* email_type = AddSchemaType(&schema, "email");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(email_type, "a");  // Section "a" would get sectionId 0
   AddIndexedProperty(email_type, "foo");
   int email_foo_section_id = 1;
 
   SchemaTypeConfigProto* message_type = AddSchemaType(&schema, "message");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(message_type, "foo");
   int message_foo_section_id = 0;
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -1829,23 +1882,27 @@
   // Create the schema and document store
   SchemaProto schema;
   SchemaTypeConfigProto* email_type = AddSchemaType(&schema, "email");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(email_type, "foo");
   int email_foo_section_id = 0;
 
   SchemaTypeConfigProto* message_type = AddSchemaType(&schema, "message");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(message_type, "foo");
   int message_foo_section_id = 0;
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -1883,8 +1940,8 @@
                              schema_store_.get(), &fake_clock_));
 
   SearchSpecProto search_spec;
-  // Create a section filter '<section name>:<query term>', but only look within
-  // documents of email schema
+  // Create a section filter '<section name>:<query term>', but only look
+  // within documents of email schema
   search_spec.set_query("foo:animal");
   search_spec.add_schema_type_filters("email");
   search_spec.set_term_match_type(term_match_type);
@@ -1905,23 +1962,27 @@
   // Create the schema and document store
   SchemaProto schema;
   SchemaTypeConfigProto* email_type = AddSchemaType(&schema, "email");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(email_type, "foo");
   int email_foo_section_id = 0;
 
   SchemaTypeConfigProto* message_type = AddSchemaType(&schema, "message");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(message_type, "bar");
   int message_foo_section_id = 0;
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -1946,9 +2007,9 @@
                               term_match_type, "animal"),
               IsOk());
 
-  // Message document has content "animal", but put in in the same section id as
-  // the indexed email section id, the same id as indexed property "foo" in the
-  // message type
+  // Message document has content "animal", but put in in the same section id
+  // as the indexed email section id, the same id as indexed property "foo" in
+  // the message type
   ASSERT_THAT(AddTokenToIndex(message_document_id, message_foo_section_id,
                               term_match_type, "animal"),
               IsOk());
@@ -1961,8 +2022,8 @@
                              schema_store_.get(), &fake_clock_));
 
   SearchSpecProto search_spec;
-  // Create a section filter '<section name>:<query term>', but only look within
-  // documents of email schema
+  // Create a section filter '<section name>:<query term>', but only look
+  // within documents of email schema
   search_spec.set_query("foo:animal");
   search_spec.set_term_match_type(term_match_type);
 
@@ -1983,14 +2044,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -2017,8 +2080,8 @@
                              schema_store_.get(), &fake_clock_));
 
   SearchSpecProto search_spec;
-  // Create a section filter '<section name>:<query term>', but only look within
-  // documents of email schema
+  // Create a section filter '<section name>:<query term>', but only look
+  // within documents of email schema
   search_spec.set_query("nonexistent:animal");
   search_spec.set_term_match_type(term_match_type);
 
@@ -2039,14 +2102,16 @@
   SchemaTypeConfigProto* email_type = AddSchemaType(&schema, "email");
   AddUnindexedProperty(email_type, "foo");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -2073,8 +2138,8 @@
                              schema_store_.get(), &fake_clock_));
 
   SearchSpecProto search_spec;
-  // Create a section filter '<section name>:<query term>', but only look within
-  // documents of email schema
+  // Create a section filter '<section name>:<query term>', but only look
+  // within documents of email schema
   search_spec.set_query("foo:animal");
   search_spec.set_term_match_type(term_match_type);
 
@@ -2092,23 +2157,27 @@
   // Create the schema and document store
   SchemaProto schema;
   SchemaTypeConfigProto* email_type = AddSchemaType(&schema, "email");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(email_type, "foo");
   int email_foo_section_id = 0;
 
   SchemaTypeConfigProto* message_type = AddSchemaType(&schema, "message");
-  // SectionIds are assigned in ascending order per schema type, alphabetically.
+  // SectionIds are assigned in ascending order per schema type,
+  // alphabetically.
   AddIndexedProperty(message_type, "foo");
   int message_foo_section_id = 0;
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   // These documents don't actually match to the tokens in the index. We're
   // inserting the documents to get the appropriate number of documents and
@@ -2172,14 +2241,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              document_store_->Put(DocumentBuilder()
@@ -2226,14 +2297,16 @@
   SchemaProto schema;
   AddSchemaType(&schema, "email");
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store_,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store_,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, store_dir_, &fake_clock_,
                             schema_store_.get()));
+  document_store_ = std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              document_store_->Put(DocumentBuilder()
diff --git a/icing/result/result-retriever_test.cc b/icing/result/result-retriever_test.cc
index 1d1f824..1078c5a 100644
--- a/icing/result/result-retriever_test.cc
+++ b/icing/result/result-retriever_test.cc
@@ -30,6 +30,7 @@
 #include "icing/store/document-id.h"
 #include "icing/testing/common-matchers.h"
 #include "icing/testing/fake-clock.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/snippet-helpers.h"
 #include "icing/testing/test-data.h"
 #include "icing/testing/tmp-directory.h"
@@ -56,17 +57,20 @@
   }
 
   void SetUp() override {
-    ICING_ASSERT_OK(
-        // File generated via icu_data_file rule in //icing/BUILD.
-        icu_data_file_helper::SetUpICUDataFile(
-            GetTestFilePath("icing/icu.dat")));
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      ICING_ASSERT_OK(
+          // File generated via icu_data_file rule in //icing/BUILD.
+          icu_data_file_helper::SetUpICUDataFile(
+              GetTestFilePath("icing/icu.dat")));
+    }
     language_segmenter_factory::SegmenterOptions options(ULOC_US);
     ICING_ASSERT_OK_AND_ASSIGN(
         language_segmenter_,
         language_segmenter_factory::Create(std::move(options)));
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     ICING_ASSERT_OK_AND_ASSIGN(normalizer_, normalizer_factory::Create(
                                                 /*max_term_byte_size=*/10000));
 
@@ -129,9 +133,11 @@
       StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   EXPECT_THAT(
       ResultRetriever::Create(doc_store.get(), /*schema_store=*/nullptr,
@@ -149,9 +155,12 @@
 
 TEST_F(ResultRetrieverTest, ShouldRetrieveSimpleResults) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -190,9 +199,12 @@
 
 TEST_F(ResultRetrieverTest, IgnoreErrors) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -228,9 +240,12 @@
 
 TEST_F(ResultRetrieverTest, NotIgnoreErrors) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -272,9 +287,12 @@
   ON_CALL(mock_filesystem, OpenForRead(_)).WillByDefault(Return(false));
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&mock_filesystem, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -303,9 +321,12 @@
 
 TEST_F(ResultRetrieverTest, DefaultSnippetSpecShouldDisableSnippeting) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -343,9 +364,12 @@
 
 TEST_F(ResultRetrieverTest, SimpleSnippeted) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -408,9 +432,12 @@
 
 TEST_F(ResultRetrieverTest, OnlyOneDocumentSnippeted) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -461,9 +488,12 @@
 
 TEST_F(ResultRetrieverTest, ShouldSnippetAllResults) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -503,9 +533,12 @@
 
 TEST_F(ResultRetrieverTest, ShouldSnippetSomeResults) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -544,9 +577,12 @@
 
 TEST_F(ResultRetrieverTest, ShouldNotSnippetAnyResults) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(CreateDocument(/*id=*/1)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
diff --git a/icing/result/snippet-retriever_test.cc b/icing/result/snippet-retriever_test.cc
index e552cf2..ecda400 100644
--- a/icing/result/snippet-retriever_test.cc
+++ b/icing/result/snippet-retriever_test.cc
@@ -33,6 +33,8 @@
 #include "icing/store/document-id.h"
 #include "icing/store/key-mapper.h"
 #include "icing/testing/common-matchers.h"
+#include "icing/testing/fake-clock.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/snippet-helpers.h"
 #include "icing/testing/test-data.h"
 #include "icing/testing/tmp-directory.h"
@@ -57,18 +59,22 @@
     test_dir_ = GetTestTempDir() + "/icing";
     filesystem_.CreateDirectoryRecursively(test_dir_.c_str());
 
-    ICING_ASSERT_OK(
-        // File generated via icu_data_file rule in //icing/BUILD.
-        icu_data_file_helper::SetUpICUDataFile(
-            GetTestFilePath("icing/icu.dat")));
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      ICING_ASSERT_OK(
+          // File generated via icu_data_file rule in //icing/BUILD.
+          icu_data_file_helper::SetUpICUDataFile(
+              GetTestFilePath("icing/icu.dat")));
+    }
+
     language_segmenter_factory::SegmenterOptions options(ULOC_US);
     ICING_ASSERT_OK_AND_ASSIGN(
         language_segmenter_,
         language_segmenter_factory::Create(std::move(options)));
 
     // Setup the schema
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
     SchemaProto schema;
     SchemaTypeConfigProto* type_config = schema.add_types();
     type_config->set_schema_type("email");
@@ -110,6 +116,7 @@
   }
 
   Filesystem filesystem_;
+  FakeClock fake_clock_;
   std::unique_ptr<SchemaStore> schema_store_;
   std::unique_ptr<LanguageSegmenter> language_segmenter_;
   std::unique_ptr<SnippetRetriever> snippet_retriever_;
diff --git a/icing/schema/schema-store.cc b/icing/schema/schema-store.cc
index 9173031..e54cc0c 100644
--- a/icing/schema/schema-store.cc
+++ b/icing/schema/schema-store.cc
@@ -39,7 +39,6 @@
 #include "icing/util/crc32.h"
 #include "icing/util/logging.h"
 #include "icing/util/status-macros.h"
-#include "icing/util/timer.h"
 
 namespace icing {
 namespace lib {
@@ -105,18 +104,21 @@
 
 libtextclassifier3::StatusOr<std::unique_ptr<SchemaStore>> SchemaStore::Create(
     const Filesystem* filesystem, const std::string& base_dir,
-    NativeInitializeStats* initialize_stats) {
+    const Clock* clock, NativeInitializeStats* initialize_stats) {
   ICING_RETURN_ERROR_IF_NULL(filesystem);
+  ICING_RETURN_ERROR_IF_NULL(clock);
 
-  std::unique_ptr<SchemaStore> schema_store =
-      std::unique_ptr<SchemaStore>(new SchemaStore(filesystem, base_dir));
+  std::unique_ptr<SchemaStore> schema_store = std::unique_ptr<SchemaStore>(
+      new SchemaStore(filesystem, base_dir, clock));
   ICING_RETURN_IF_ERROR(schema_store->Initialize(initialize_stats));
   return schema_store;
 }
 
-SchemaStore::SchemaStore(const Filesystem* filesystem, std::string base_dir)
+SchemaStore::SchemaStore(const Filesystem* filesystem, std::string base_dir,
+                         const Clock* clock)
     : filesystem_(*filesystem),
       base_dir_(std::move(base_dir)),
+      clock_(*clock),
       schema_file_(*filesystem, MakeSchemaFilename(base_dir_)) {}
 
 SchemaStore::~SchemaStore() {
@@ -142,7 +144,7 @@
     ICING_VLOG(3)
         << "Couldn't find derived files or failed to initialize them, "
            "regenerating derived files for SchemaStore.";
-    Timer regenerate_timer;
+    std::unique_ptr<Timer> regenerate_timer = clock_.GetNewTimer();
     if (initialize_stats != nullptr) {
       initialize_stats->set_schema_store_recovery_cause(
           NativeInitializeStats::IO_ERROR);
@@ -150,7 +152,7 @@
     ICING_RETURN_IF_ERROR(RegenerateDerivedFiles());
     if (initialize_stats != nullptr) {
       initialize_stats->set_schema_store_recovery_latency_ms(
-          regenerate_timer.GetElapsedMilliseconds());
+          regenerate_timer->GetElapsedMilliseconds());
     }
   }
 
diff --git a/icing/schema/schema-store.h b/icing/schema/schema-store.h
index 76f36b4..cff7abd 100644
--- a/icing/schema/schema-store.h
+++ b/icing/schema/schema-store.h
@@ -34,6 +34,7 @@
 #include "icing/schema/section.h"
 #include "icing/store/document-filter-data.h"
 #include "icing/store/key-mapper.h"
+#include "icing/util/clock.h"
 #include "icing/util/crc32.h"
 
 namespace icing {
@@ -114,7 +115,7 @@
   //   INTERNAL_ERROR on any IO errors
   static libtextclassifier3::StatusOr<std::unique_ptr<SchemaStore>> Create(
       const Filesystem* filesystem, const std::string& base_dir,
-      NativeInitializeStats* initialize_stats = nullptr);
+      const Clock* clock, NativeInitializeStats* initialize_stats = nullptr);
 
   // Not copyable
   SchemaStore(const SchemaStore&) = delete;
@@ -227,7 +228,8 @@
 
  private:
   // Use SchemaStore::Create instead.
-  explicit SchemaStore(const Filesystem* filesystem, std::string base_dir);
+  explicit SchemaStore(const Filesystem* filesystem, std::string base_dir,
+                       const Clock* clock);
 
   // Handles initializing the SchemaStore and regenerating any data if needed.
   //
@@ -273,6 +275,7 @@
 
   const Filesystem& filesystem_;
   const std::string base_dir_;
+  const Clock& clock_;
 
   // Used internally to indicate whether the class has been initialized. This is
   // to guard against cases where the object has been created, but Initialize
diff --git a/icing/schema/schema-store_test.cc b/icing/schema/schema-store_test.cc
index 4a458b2..7df3dd9 100644
--- a/icing/schema/schema-store_test.cc
+++ b/icing/schema/schema-store_test.cc
@@ -31,6 +31,7 @@
 #include "icing/store/document-filter-data.h"
 #include "icing/testing/common-matchers.h"
 #include "icing/testing/tmp-directory.h"
+#include "icing/testing/fake-clock.h"
 
 namespace icing {
 namespace lib {
@@ -69,17 +70,19 @@
   const Filesystem filesystem_;
   const std::string test_dir_;
   SchemaProto schema_;
+  const FakeClock fake_clock_;
 };
 
 TEST_F(SchemaStoreTest, CreationWithNullPointerShouldFail) {
-  EXPECT_THAT(SchemaStore::Create(/*filesystem=*/nullptr, test_dir_),
+  EXPECT_THAT(SchemaStore::Create(/*filesystem=*/nullptr, test_dir_, &fake_clock_),
               StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
 }
 
 TEST_F(SchemaStoreTest, CorruptSchemaError) {
   {
-    ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        std::unique_ptr<SchemaStore> schema_store,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
     // Set it for the first time
     SchemaStore::SetSchemaResult result;
@@ -105,14 +108,15 @@
                     serialized_schema.size());
 
   // If ground truth was corrupted, we won't know what to do
-  EXPECT_THAT(SchemaStore::Create(&filesystem_, test_dir_),
+  EXPECT_THAT(SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_),
               StatusIs(libtextclassifier3::StatusCode::INTERNAL));
 }
 
 TEST_F(SchemaStoreTest, RecoverCorruptDerivedFileOk) {
   {
-    ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        std::unique_ptr<SchemaStore> schema_store,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
     // Set it for the first time
     SchemaStore::SetSchemaResult result;
@@ -134,8 +138,9 @@
       absl_ports::StrCat(test_dir_, "/schema_type_mapper");
   filesystem_.DeleteDirectoryRecursively(schema_type_mapper_dir.c_str());
 
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   // Everything looks fine, ground truth and derived data
   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
@@ -146,8 +151,9 @@
 
 TEST_F(SchemaStoreTest, RecoverBadChecksumOk) {
   {
-    ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        std::unique_ptr<SchemaStore> schema_store,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
     // Set it for the first time
     SchemaStore::SetSchemaResult result;
@@ -172,8 +178,9 @@
   filesystem_.DeleteFile(header_file.c_str());
   filesystem_.Write(header_file.c_str(), &header, sizeof(header));
 
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   // Everything looks fine, ground truth and derived data
   ICING_ASSERT_OK_AND_ASSIGN(const SchemaProto* actual_schema,
@@ -183,12 +190,13 @@
 }
 
 TEST_F(SchemaStoreTest, CreateNoPreviousSchemaOk) {
-  EXPECT_THAT(SchemaStore::Create(&filesystem_, test_dir_), IsOk());
+  EXPECT_THAT(SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_), IsOk());
 }
 
 TEST_F(SchemaStoreTest, CreateWithPreviousSchemaOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaStore::SetSchemaResult result;
   result.success = true;
@@ -196,7 +204,7 @@
               IsOkAndHolds(EqualsSetSchemaResult(result)));
 
   schema_store.reset();
-  EXPECT_THAT(SchemaStore::Create(&filesystem_, test_dir_), IsOk());
+  EXPECT_THAT(SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_), IsOk());
 }
 
 TEST_F(SchemaStoreTest, MultipleCreateOk) {
@@ -206,8 +214,9 @@
   properties->set_name("subject");
   properties->add_string_values("subject_content");
 
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaStore::SetSchemaResult result;
   result.success = true;
@@ -225,8 +234,8 @@
   EXPECT_THAT(schema_store->GetSchemaTypeId("email"), IsOkAndHolds(0));
 
   schema_store.reset();
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store, SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   // Verify that our in-memory structures are ok
   EXPECT_THAT(schema_store->GetSchemaTypeConfig("email"),
@@ -240,8 +249,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetNewSchemaOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   // Set it for the first time
   SchemaStore::SetSchemaResult result;
@@ -254,8 +264,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetSameSchemaOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   // Set it for the first time
   SchemaStore::SetSchemaResult result;
@@ -274,8 +285,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetIncompatibleSchemaOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   // Set it for the first time
   SchemaStore::SetSchemaResult result;
@@ -298,8 +310,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetSchemaWithAddedTypeOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto schema;
   auto type = schema.add_types();
@@ -326,8 +339,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetSchemaWithDeletedTypeOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto schema;
   auto type = schema.add_types();
@@ -381,8 +395,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetSchemaWithReorderedTypesOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto schema;
   auto type = schema.add_types();
@@ -420,8 +435,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetSchemaThatRequiresReindexingOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto schema;
   auto type = schema.add_types();
@@ -460,8 +476,9 @@
 }
 
 TEST_F(SchemaStoreTest, SetSchemaWithIncompatibleTypesOk) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto schema;
   auto type = schema.add_types();
@@ -514,8 +531,9 @@
 }
 
 TEST_F(SchemaStoreTest, GetSchemaTypeId) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   schema_.clear_types();
 
@@ -539,16 +557,18 @@
 }
 
 TEST_F(SchemaStoreTest, ComputeChecksumDefaultOnEmptySchemaStore) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   Crc32 default_checksum;
   EXPECT_THAT(schema_store->ComputeChecksum(), IsOkAndHolds(default_checksum));
 }
 
 TEST_F(SchemaStoreTest, ComputeChecksumSameBetweenCalls) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto foo_schema;
   auto type_config = foo_schema.add_types();
@@ -563,8 +583,9 @@
 }
 
 TEST_F(SchemaStoreTest, ComputeChecksumSameAcrossInstances) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto foo_schema;
   auto type_config = foo_schema.add_types();
@@ -577,14 +598,15 @@
   // Destroy the previous instance and recreate SchemaStore
   schema_store.reset();
 
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store, SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   EXPECT_THAT(schema_store->ComputeChecksum(), IsOkAndHolds(checksum));
 }
 
 TEST_F(SchemaStoreTest, ComputeChecksumChangesOnModification) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto foo_schema;
   auto type_config = foo_schema.add_types();
@@ -607,16 +629,18 @@
 }
 
 TEST_F(SchemaStoreTest, PersistToDiskFineForEmptySchemaStore) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   // Persisting is fine and shouldn't affect anything
   ICING_EXPECT_OK(schema_store->PersistToDisk());
 }
 
 TEST_F(SchemaStoreTest, PersistToDiskPreservesAcrossInstances) {
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
   SchemaProto schema;
   auto type_config = schema.add_types();
@@ -640,8 +664,8 @@
   schema_store.reset();
 
   // And we get the same schema back on reinitialization
-  ICING_ASSERT_OK_AND_ASSIGN(schema_store,
-                             SchemaStore::Create(&filesystem_, test_dir_));
+  ICING_ASSERT_OK_AND_ASSIGN(
+      schema_store, SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
   ICING_ASSERT_OK_AND_ASSIGN(actual_schema, schema_store->GetSchema());
   EXPECT_THAT(*actual_schema, EqualsProto(schema));
 }
diff --git a/icing/schema/schema-util.cc b/icing/schema/schema-util.cc
index a755e88..9626a0b 100644
--- a/icing/schema/schema-util.cc
+++ b/icing/schema/schema-util.cc
@@ -272,7 +272,7 @@
     const SchemaTypeConfigProto& type_config) {
   ParsedPropertyConfigs parsed_property_configs;
 
-  // TODO(samzheng): consider caching property_config_map for some properties,
+  // TODO(cassiewang): consider caching property_config_map for some properties,
   // e.g. using LRU cache. Or changing schema.proto to use go/protomap.
   for (const PropertyConfigProto& property_config : type_config.properties()) {
     parsed_property_configs.property_config_map.emplace(
diff --git a/icing/schema/section.h b/icing/schema/section.h
index 7669c97..058f261 100644
--- a/icing/schema/section.h
+++ b/icing/schema/section.h
@@ -45,7 +45,6 @@
     kMaxSectionId < 8 * sizeof(SectionIdMask),
     "SectionIdMask is not large enough to represent all section values!");
 
-// TODO(samzheng): add more metadata when needed, e.g. tokenizer type,
 struct SectionMetadata {
   // Dot-joined property names, representing the location of section inside an
   // document. E.g. "property1.property2"
diff --git a/icing/scoring/score-and-rank_benchmark.cc b/icing/scoring/score-and-rank_benchmark.cc
index c3ed40a..e940e98 100644
--- a/icing/scoring/score-and-rank_benchmark.cc
+++ b/icing/scoring/score-and-rank_benchmark.cc
@@ -99,14 +99,17 @@
   filesystem.CreateDirectoryRecursively(document_store_dir.c_str());
   filesystem.CreateDirectoryRecursively(schema_store_dir.c_str());
 
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem, base_dir));
-
   Clock clock;
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem, base_dir, &clock));
+
+  ICING_ASSERT_OK_AND_ASSIGN(
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem, document_store_dir, &clock,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK(schema_store->SetSchema(CreateSchemaWithEmailType()));
 
@@ -198,14 +201,17 @@
   filesystem.CreateDirectoryRecursively(document_store_dir.c_str());
   filesystem.CreateDirectoryRecursively(schema_store_dir.c_str());
 
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem, base_dir));
-
   Clock clock;
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem, base_dir, &clock));
+
+  ICING_ASSERT_OK_AND_ASSIGN(
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem, document_store_dir, &clock,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK(schema_store->SetSchema(CreateSchemaWithEmailType()));
 
@@ -298,14 +304,17 @@
   filesystem.CreateDirectoryRecursively(document_store_dir.c_str());
   filesystem.CreateDirectoryRecursively(schema_store_dir.c_str());
 
-  ICING_ASSERT_OK_AND_ASSIGN(std::unique_ptr<SchemaStore> schema_store,
-                             SchemaStore::Create(&filesystem, base_dir));
-
   Clock clock;
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      std::unique_ptr<SchemaStore> schema_store,
+      SchemaStore::Create(&filesystem, base_dir, &clock));
+
+  ICING_ASSERT_OK_AND_ASSIGN(
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem, document_store_dir, &clock,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK(schema_store->SetSchema(CreateSchemaWithEmailType()));
 
diff --git a/icing/scoring/scorer_test.cc b/icing/scoring/scorer_test.cc
index 06bf484..b669eb1 100644
--- a/icing/scoring/scorer_test.cc
+++ b/icing/scoring/scorer_test.cc
@@ -53,12 +53,14 @@
     fake_clock2_.SetSystemTimeMilliseconds(1572200000000);
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        schema_store_, SchemaStore::Create(&filesystem_, schema_store_dir_));
+        schema_store_,
+        SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock1_));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, doc_store_dir_, &fake_clock1_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
 
     // Creates a simple email schema
     SchemaProto test_email_schema;
diff --git a/icing/scoring/scoring-processor_test.cc b/icing/scoring/scoring-processor_test.cc
index 0da25f6..14b2a20 100644
--- a/icing/scoring/scoring-processor_test.cc
+++ b/icing/scoring/scoring-processor_test.cc
@@ -49,13 +49,15 @@
     filesystem_.CreateDirectoryRecursively(doc_store_dir_.c_str());
     filesystem_.CreateDirectoryRecursively(schema_store_dir_.c_str());
 
-    ICING_ASSERT_OK_AND_ASSIGN(schema_store_,
-                               SchemaStore::Create(&filesystem_, test_dir_));
+    ICING_ASSERT_OK_AND_ASSIGN(
+        schema_store_,
+        SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        document_store_,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, doc_store_dir_, &fake_clock_,
                               schema_store_.get()));
+    document_store_ = std::move(create_result.document_store);
 
     // Creates a simple email schema
     SchemaProto test_email_schema;
diff --git a/icing/store/document-store.cc b/icing/store/document-store.cc
index 8ddde14..ce41715 100644
--- a/icing/store/document-store.cc
+++ b/icing/store/document-store.cc
@@ -44,9 +44,9 @@
 #include "icing/store/namespace-id.h"
 #include "icing/util/clock.h"
 #include "icing/util/crc32.h"
+#include "icing/util/data-loss.h"
 #include "icing/util/logging.h"
 #include "icing/util/status-macros.h"
-#include "icing/util/timer.h"
 
 namespace icing {
 namespace lib {
@@ -200,21 +200,26 @@
   }
 }
 
-libtextclassifier3::StatusOr<std::unique_ptr<DocumentStore>>
-DocumentStore::Create(const Filesystem* filesystem, const std::string& base_dir,
-                      const Clock* clock, const SchemaStore* schema_store,
-                      NativeInitializeStats* initialize_stats) {
+libtextclassifier3::StatusOr<DocumentStore::CreateResult> DocumentStore::Create(
+    const Filesystem* filesystem, const std::string& base_dir,
+    const Clock* clock, const SchemaStore* schema_store,
+    NativeInitializeStats* initialize_stats) {
   ICING_RETURN_ERROR_IF_NULL(filesystem);
   ICING_RETURN_ERROR_IF_NULL(clock);
   ICING_RETURN_ERROR_IF_NULL(schema_store);
 
   auto document_store = std::unique_ptr<DocumentStore>(
       new DocumentStore(filesystem, base_dir, clock, schema_store));
-  ICING_RETURN_IF_ERROR(document_store->Initialize(initialize_stats));
-  return document_store;
+  ICING_ASSIGN_OR_RETURN(DataLoss data_loss,
+                         document_store->Initialize(initialize_stats));
+
+  CreateResult create_result;
+  create_result.document_store = std::move(document_store);
+  create_result.data_loss = data_loss;
+  return create_result;
 }
 
-libtextclassifier3::Status DocumentStore::Initialize(
+libtextclassifier3::StatusOr<DataLoss> DocumentStore::Initialize(
     NativeInitializeStats* initialize_stats) {
   auto create_result_or = FileBackedProtoLog<DocumentWrapper>::Create(
       filesystem_, MakeDocumentLogFilename(base_dir_),
@@ -238,8 +243,7 @@
       initialize_stats->set_document_store_recovery_cause(
           NativeInitializeStats::DATA_LOSS);
 
-      if (create_result.data_status ==
-          FileBackedProtoLog<DocumentWrapper>::CreateResult::PARTIAL_LOSS) {
+      if (create_result.data_loss == DataLoss::PARTIAL) {
         // Ground truth is partially lost.
         initialize_stats->set_document_store_data_status(
             NativeInitializeStats::PARTIAL_LOSS);
@@ -249,11 +253,11 @@
             NativeInitializeStats::COMPLETE_LOSS);
       }
     }
-    Timer document_recovery_timer;
+    std::unique_ptr<Timer> document_recovery_timer = clock_.GetNewTimer();
     libtextclassifier3::Status status = RegenerateDerivedFiles();
     if (initialize_stats != nullptr) {
       initialize_stats->set_document_store_recovery_latency_ms(
-          document_recovery_timer.GetElapsedMilliseconds());
+          document_recovery_timer->GetElapsedMilliseconds());
     }
     if (!status.ok()) {
       ICING_LOG(ERROR)
@@ -269,11 +273,11 @@
         initialize_stats->set_document_store_recovery_cause(
             NativeInitializeStats::IO_ERROR);
       }
-      Timer document_recovery_timer;
+      std::unique_ptr<Timer> document_recovery_timer = clock_.GetNewTimer();
       libtextclassifier3::Status status = RegenerateDerivedFiles();
       if (initialize_stats != nullptr) {
         initialize_stats->set_document_store_recovery_latency_ms(
-            document_recovery_timer.GetElapsedMilliseconds());
+            document_recovery_timer->GetElapsedMilliseconds());
       }
       if (!status.ok()) {
         ICING_LOG(ERROR)
@@ -288,7 +292,7 @@
     initialize_stats->set_num_documents(document_id_mapper_->num_elements());
   }
 
-  return libtextclassifier3::Status::OK;
+  return create_result.data_loss;
 }
 
 libtextclassifier3::Status DocumentStore::InitializeDerivedFiles() {
@@ -726,7 +730,7 @@
 
 libtextclassifier3::StatusOr<DocumentId> DocumentStore::Put(
     DocumentProto&& document, NativePutDocumentStats* put_document_stats) {
-  Timer put_timer;
+  std::unique_ptr<Timer> put_timer = clock_.GetNewTimer();
   ICING_RETURN_IF_ERROR(document_validator_.Validate(document));
 
   if (put_document_stats != nullptr) {
@@ -808,7 +812,7 @@
 
   if (put_document_stats != nullptr) {
     put_document_stats->set_document_store_latency_ms(
-        put_timer.GetElapsedMilliseconds());
+        put_timer->GetElapsedMilliseconds());
   }
 
   return new_document_id;
@@ -1406,9 +1410,11 @@
         "New directory is the same as the current one.");
   }
 
-  ICING_ASSIGN_OR_RETURN(auto new_doc_store,
+  ICING_ASSIGN_OR_RETURN(auto doc_store_create_result,
                          DocumentStore::Create(filesystem_, new_directory,
                                                &clock_, schema_store_));
+  std::unique_ptr<DocumentStore> new_doc_store =
+      std::move(doc_store_create_result.document_store);
 
   // Writes all valid docs into new document store (new directory)
   int size = document_id_mapper_->num_elements();
diff --git a/icing/store/document-store.h b/icing/store/document-store.h
index d6ffbaa..114fa13 100644
--- a/icing/store/document-store.h
+++ b/icing/store/document-store.h
@@ -38,6 +38,7 @@
 #include "icing/store/usage-store.h"
 #include "icing/util/clock.h"
 #include "icing/util/crc32.h"
+#include "icing/util/data-loss.h"
 #include "icing/util/document-validator.h"
 
 namespace icing {
@@ -70,6 +71,17 @@
     int32_t optimizable_docs = 0;
   };
 
+  struct CreateResult {
+    // A successfully initialized document store.
+    std::unique_ptr<DocumentStore> document_store;
+
+    // The data status after initializing from a previous state. Data loss can
+    // happen if the file is corrupted or some previously added data was
+    // unpersisted. This may be used to signal that any derived data off of the
+    // document store may need to be regenerated.
+    DataLoss data_loss;
+  };
+
   // Not copyable
   DocumentStore(const DocumentStore&) = delete;
   DocumentStore& operator=(const DocumentStore&) = delete;
@@ -92,10 +104,10 @@
   // were regenerated. This may be helpful in logs.
   //
   // Returns:
-  //   A DocumentStore on success
+  //   A DocumentStore::CreateResult on success
   //   FAILED_PRECONDITION on any null pointer input
   //   INTERNAL_ERROR on IO error
-  static libtextclassifier3::StatusOr<std::unique_ptr<DocumentStore>> Create(
+  static libtextclassifier3::StatusOr<DocumentStore::CreateResult> Create(
       const Filesystem* filesystem, const std::string& base_dir,
       const Clock* clock, const SchemaStore* schema_store,
       NativeInitializeStats* initialize_stats = nullptr);
@@ -306,7 +318,7 @@
   //   Disk usage on success
   //   INTERNAL_ERROR on IO error
   //
-  // TODO(samzheng): consider returning a struct which has the breakdown of each
+  // TODO(tjbarron): consider returning a struct which has the breakdown of each
   // component.
   libtextclassifier3::StatusOr<int64_t> GetDiskUsage() const;
 
@@ -438,7 +450,7 @@
   // worry about this field.
   bool initialized_ = false;
 
-  libtextclassifier3::Status Initialize(
+  libtextclassifier3::StatusOr<DataLoss> Initialize(
       NativeInitializeStats* initialize_stats);
 
   // Creates sub-components and verifies the integrity of each sub-component.
diff --git a/icing/store/document-store_test.cc b/icing/store/document-store_test.cc
index d97ec46..4d8ac10 100644
--- a/icing/store/document-store_test.cc
+++ b/icing/store/document-store_test.cc
@@ -126,7 +126,8 @@
         StringIndexingConfig::TokenizerType::PLAIN);
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        schema_store_, SchemaStore::Create(&filesystem_, schema_store_dir_));
+        schema_store_,
+        SchemaStore::Create(&filesystem_, schema_store_dir_, &fake_clock_));
     ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
   }
 
@@ -182,9 +183,11 @@
 
 TEST_F(DocumentStoreTest, PutAndGetInSameNamespaceOk) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   // Both documents have namespace of "icing"
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
@@ -200,9 +203,11 @@
 
 TEST_F(DocumentStoreTest, PutAndGetAcrossNamespacesOk) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   // Can handle different namespaces with same url
   DocumentProto foo_document = DocumentBuilder()
@@ -231,9 +236,11 @@
 // document and old doc ids are not getting reused.
 TEST_F(DocumentStoreTest, PutSameKey) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   // Creates two documents with the same key (namespace + uri)
   DocumentProto document1 = DocumentProto(test_document1_);
@@ -258,9 +265,12 @@
 
 TEST_F(DocumentStoreTest, IsDocumentExisting) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(DocumentProto(test_document1_)));
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
@@ -287,9 +297,12 @@
 
 TEST_F(DocumentStoreTest, GetSoftDeletedDocumentNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(DocumentProto(test_document1_)));
   EXPECT_THAT(
       document_store->Get(test_document1_.namespace_(), test_document1_.uri()),
@@ -305,9 +318,12 @@
 
 TEST_F(DocumentStoreTest, GetHardDeletedDocumentNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(DocumentProto(test_document1_)));
   EXPECT_THAT(
       document_store->Get(test_document1_.namespace_(), test_document1_.uri()),
@@ -330,9 +346,12 @@
                                .Build();
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(document));
   EXPECT_THAT(document_store->Get("namespace", "uri"),
               IsOkAndHolds(EqualsProto(document)));
@@ -355,9 +374,12 @@
 
 TEST_F(DocumentStoreTest, GetInvalidDocumentId) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              doc_store->Put(DocumentProto(test_document1_)));
 
@@ -379,9 +401,11 @@
 
 TEST_F(DocumentStoreTest, DeleteNonexistentDocumentNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Validates that deleting something non-existing won't append anything to
   // ground truth
@@ -399,9 +423,12 @@
 
 TEST_F(DocumentStoreTest, DeleteAlreadyDeletedDocumentNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(test_document1_));
 
   // First time is OK
@@ -416,9 +443,11 @@
 
 TEST_F(DocumentStoreTest, SoftDeleteByNamespaceOk) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   DocumentProto document1 = test_document1_;
   document1.set_namespace_("namespace.1");
@@ -456,9 +485,11 @@
 
 TEST_F(DocumentStoreTest, HardDeleteByNamespaceOk) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   DocumentProto document1 = test_document1_;
   document1.set_namespace_("namespace.1");
@@ -496,9 +527,11 @@
 
 TEST_F(DocumentStoreTest, SoftDeleteByNamespaceNonexistentNamespaceNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   // Validates that deleting something non-existing won't append anything to
   // ground truth
@@ -516,9 +549,11 @@
 
 TEST_F(DocumentStoreTest, HardDeleteByNamespaceNonexistentNamespaceNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   // Validates that deleting something non-existing won't append anything to
   // ground truth
@@ -536,9 +571,12 @@
 
 TEST_F(DocumentStoreTest, SoftDeleteByNamespaceNoExistingDocumentsNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(test_document1_));
   ICING_EXPECT_OK(document_store->Delete(test_document1_.namespace_(),
                                          test_document1_.uri()));
@@ -553,9 +591,12 @@
 
 TEST_F(DocumentStoreTest, HardDeleteByNamespaceNoExistingDocumentsNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(test_document1_));
   ICING_EXPECT_OK(document_store->Delete(test_document1_.namespace_(),
                                          test_document1_.uri()));
@@ -588,9 +629,12 @@
   int64_t ground_truth_size_before;
   {
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> doc_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store_.get()));
+    std::unique_ptr<DocumentStore> doc_store =
+        std::move(create_result.document_store);
+
     ICING_ASSERT_OK(doc_store->Put(document1));
     ICING_ASSERT_OK(doc_store->Put(document2));
     ICING_ASSERT_OK(doc_store->Put(document3));
@@ -617,9 +661,11 @@
 
   // Successfully recover from a corrupt derived file issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   // Make sure we didn't add anything to the ground truth after we recovered.
   int64_t ground_truth_size_after = filesystem_.GetFileSize(
@@ -650,14 +696,16 @@
   filesystem_.CreateDirectoryRecursively(schema_store_dir.c_str());
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
 
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   DocumentProto email_document_1 = DocumentBuilder()
                                        .SetKey("namespace1", "1")
@@ -731,14 +779,16 @@
   filesystem_.CreateDirectoryRecursively(schema_store_dir.c_str());
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
 
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   DocumentProto email_document_1 = DocumentBuilder()
                                        .SetKey("namespace1", "1")
@@ -800,9 +850,11 @@
 
 TEST_F(DocumentStoreTest, SoftDeleteBySchemaTypeNonexistentSchemaTypeNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Validates that deleting something non-existing won't append anything to
   // ground truth
@@ -821,9 +873,11 @@
 
 TEST_F(DocumentStoreTest, HardDeleteBySchemaTypeNonexistentSchemaTypeNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Validates that deleting something non-existing won't append anything to
   // ground truth
@@ -842,9 +896,12 @@
 
 TEST_F(DocumentStoreTest, SoftDeleteBySchemaTypeNoExistingDocumentsNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(test_document1_));
   ICING_EXPECT_OK(document_store->Delete(test_document1_.namespace_(),
                                          test_document1_.uri()));
@@ -856,9 +913,12 @@
 
 TEST_F(DocumentStoreTest, HardDeleteBySchemaTypeNoExistingDocumentsNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_EXPECT_OK(document_store->Put(test_document1_));
   ICING_EXPECT_OK(document_store->Delete(test_document1_.namespace_(),
                                          test_document1_.uri()));
@@ -880,7 +940,7 @@
   filesystem_.CreateDirectoryRecursively(schema_store_dir.c_str());
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
 
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
@@ -901,9 +961,11 @@
   int64_t ground_truth_size_before;
   {
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> document_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store.get()));
+    std::unique_ptr<DocumentStore> document_store =
+        std::move(create_result.document_store);
 
     ICING_ASSERT_OK_AND_ASSIGN(email_document_id,
                                document_store->Put(email_document));
@@ -930,9 +992,11 @@
 
   // Successfully recover from a corrupt derived file issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Make sure we didn't add anything to the ground truth after we recovered.
   int64_t ground_truth_size_after = filesystem_.GetFileSize(
@@ -957,7 +1021,7 @@
   filesystem_.CreateDirectoryRecursively(schema_store_dir.c_str());
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
 
   ICING_ASSERT_OK(schema_store->SetSchema(schema));
 
@@ -978,9 +1042,11 @@
   int64_t ground_truth_size_before;
   {
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> document_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store.get()));
+    std::unique_ptr<DocumentStore> document_store =
+        std::move(create_result.document_store);
 
     ICING_ASSERT_OK_AND_ASSIGN(email_document_id,
                                document_store->Put(email_document));
@@ -1019,9 +1085,11 @@
 
   // Successfully recover from a corrupt derived file issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Make sure we didn't add anything to the ground truth after we recovered.
   int64_t ground_truth_size_after = filesystem_.GetFileSize(
@@ -1036,9 +1104,11 @@
 
 TEST_F(DocumentStoreTest, OptimizeInto) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   DocumentProto document1 = DocumentBuilder()
                                 .SetKey("namespace", "uri1")
@@ -1118,9 +1188,12 @@
   {
     // Can put and delete fine.
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> doc_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store_.get()));
+    std::unique_ptr<DocumentStore> doc_store =
+        std::move(create_result.document_store);
+
     ICING_ASSERT_OK_AND_ASSIGN(document_id1,
                                doc_store->Put(DocumentProto(test_document1_)));
     ICING_ASSERT_OK_AND_ASSIGN(document_id2,
@@ -1150,9 +1223,12 @@
 
   // Successfully recover from a data loss issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   EXPECT_THAT(doc_store->Get(document_id1),
               StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
   EXPECT_THAT(doc_store->Get(document_id2),
@@ -1174,9 +1250,12 @@
   {
     // Can put and delete fine.
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> doc_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store_.get()));
+    std::unique_ptr<DocumentStore> doc_store =
+        std::move(create_result.document_store);
+
     ICING_ASSERT_OK_AND_ASSIGN(document_id1,
                                doc_store->Put(DocumentProto(test_document1_)));
     ICING_ASSERT_OK_AND_ASSIGN(document_id2,
@@ -1209,9 +1288,12 @@
 
   // Successfully recover from a corrupt derived file issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   EXPECT_THAT(doc_store->Get(document_id1),
               StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
   EXPECT_THAT(doc_store->Get(document_id2),
@@ -1233,9 +1315,12 @@
   {
     // Can put and delete fine.
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> doc_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store_.get()));
+    std::unique_ptr<DocumentStore> doc_store =
+        std::move(create_result.document_store);
+
     ICING_ASSERT_OK_AND_ASSIGN(document_id1,
                                doc_store->Put(DocumentProto(test_document1_)));
     ICING_ASSERT_OK_AND_ASSIGN(document_id2,
@@ -1264,9 +1349,12 @@
 
   // Successfully recover from a corrupt derived file issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   EXPECT_THAT(doc_store->Get(document_id1),
               StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
   EXPECT_THAT(doc_store->Get(document_id2),
@@ -1285,9 +1373,12 @@
 
 TEST_F(DocumentStoreTest, GetDiskUsage) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(int64_t empty_doc_store_size,
                              doc_store->GetDiskUsage());
   EXPECT_THAT(empty_doc_store_size, Gt(0));
@@ -1313,18 +1404,23 @@
   ON_CALL(mock_filesystem, GetDiskUsage(A<const char*>()))
       .WillByDefault(Return(Filesystem::kBadFileSize));
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store_with_mock_filesystem,
+      create_result,
       DocumentStore::Create(&mock_filesystem, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store_with_mock_filesystem =
+      std::move(create_result.document_store);
+
   EXPECT_THAT(doc_store_with_mock_filesystem->GetDiskUsage(),
               StatusIs(libtextclassifier3::StatusCode::INTERNAL));
 }
 
 TEST_F(DocumentStoreTest, MaxDocumentId) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   // Since the DocumentStore is empty, we get an invalid DocumentId
   EXPECT_THAT(doc_store->last_added_document_id(), Eq(kInvalidDocumentId));
@@ -1344,9 +1440,11 @@
 
 TEST_F(DocumentStoreTest, GetNamespaceId) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   DocumentProto document_namespace1 =
       DocumentBuilder().SetKey("namespace1", "1").SetSchema("email").Build();
@@ -1369,9 +1467,11 @@
 
 TEST_F(DocumentStoreTest, GetDuplicateNamespaceId) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   DocumentProto document1 =
       DocumentBuilder().SetKey("namespace", "1").SetSchema("email").Build();
@@ -1387,9 +1487,11 @@
 
 TEST_F(DocumentStoreTest, NonexistentNamespaceNotFound) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   EXPECT_THAT(doc_store->GetNamespaceId("nonexistent_namespace"),
               StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
@@ -1397,9 +1499,11 @@
 
 TEST_F(DocumentStoreTest, SoftDeletionDoesNotClearFilterCache) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              doc_store->Put(test_document1_));
@@ -1418,9 +1522,11 @@
 
 TEST_F(DocumentStoreTest, HardDeleteClearsFilterCache) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              doc_store->Put(test_document1_));
@@ -1440,9 +1546,11 @@
 
 TEST_F(DocumentStoreTest, SoftDeletionDoesNotClearScoreCache) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              doc_store->Put(test_document1_));
@@ -1460,9 +1568,11 @@
 
 TEST_F(DocumentStoreTest, HardDeleteClearsScoreCache) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              doc_store->Put(test_document1_));
@@ -1480,9 +1590,11 @@
 
 TEST_F(DocumentStoreTest, SoftDeleteDoesNotClearUsageScores) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              doc_store->Put(test_document1_));
@@ -1508,9 +1620,11 @@
 
 TEST_F(DocumentStoreTest, HardDeleteShouldClearUsageScores) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              doc_store->Put(test_document1_));
@@ -1545,9 +1659,11 @@
                                .Build();
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id, doc_store->Put(document));
 
@@ -1567,9 +1683,11 @@
                                .Build();
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id, doc_store->Put(document));
 
@@ -1591,9 +1709,11 @@
           .Build();
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id, doc_store->Put(document));
 
@@ -1618,9 +1738,11 @@
   int64_t fake_real_time = 100;
   fake_clock_.SetSystemTimeMilliseconds(fake_real_time);
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(
       DocumentId document_id,
@@ -1649,9 +1771,11 @@
                                 .Build();
 
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> doc_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
                              doc_store->Put(document1));
@@ -1669,9 +1793,11 @@
 
 TEST_F(DocumentStoreTest, ComputeChecksumSameBetweenCalls) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   ICING_EXPECT_OK(document_store->Put(test_document1_));
   ICING_ASSERT_OK_AND_ASSIGN(Crc32 checksum, document_store->ComputeChecksum());
@@ -1682,9 +1808,11 @@
 
 TEST_F(DocumentStoreTest, ComputeChecksumSameAcrossInstances) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   ICING_EXPECT_OK(document_store->Put(test_document1_));
   ICING_ASSERT_OK_AND_ASSIGN(Crc32 checksum, document_store->ComputeChecksum());
@@ -1692,17 +1820,20 @@
   // Destroy the previous instance and recreate DocumentStore
   document_store.reset();
   ICING_ASSERT_OK_AND_ASSIGN(
-      document_store, DocumentStore::Create(&filesystem_, document_store_dir_,
-                                            &fake_clock_, schema_store_.get()));
+      create_result, DocumentStore::Create(&filesystem_, document_store_dir_,
+                                           &fake_clock_, schema_store_.get()));
+  document_store = std::move(create_result.document_store);
 
   EXPECT_THAT(document_store->ComputeChecksum(), IsOkAndHolds(checksum));
 }
 
 TEST_F(DocumentStoreTest, ComputeChecksumChangesOnModification) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   ICING_EXPECT_OK(document_store->Put(test_document1_));
   ICING_ASSERT_OK_AND_ASSIGN(Crc32 checksum, document_store->ComputeChecksum());
@@ -1739,7 +1870,7 @@
     filesystem_.CreateDirectoryRecursively(schema_store_dir.c_str());
     ICING_ASSERT_OK_AND_ASSIGN(
         std::unique_ptr<SchemaStore> schema_store,
-        SchemaStore::Create(&filesystem_, schema_store_dir));
+        SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
     SchemaProto schema;
     auto type_config = schema.add_types();
     type_config->set_schema_type("email");
@@ -1753,9 +1884,11 @@
                                schema_store->GetSchemaTypeId("message"));
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> document_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store.get()));
+    std::unique_ptr<DocumentStore> document_store =
+        std::move(create_result.document_store);
 
     // Insert and verify a "email "document
     ICING_ASSERT_OK_AND_ASSIGN(
@@ -1802,7 +1935,7 @@
   filesystem_.CreateDirectoryRecursively(schema_store_dir.c_str());
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
   SchemaProto schema;
   auto type_config = schema.add_types();
   type_config->set_schema_type("email");
@@ -1814,9 +1947,11 @@
   // Successfully recover from a corrupt derived file issue. We don't fail just
   // because the "message" schema type is missing
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // "email" document is fine
   EXPECT_THAT(document_store->Get(email_document_id),
@@ -1857,7 +1992,7 @@
 
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
   ICING_EXPECT_OK(schema_store->SetSchema(schema));
 
   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId old_email_schema_type_id,
@@ -1879,9 +2014,12 @@
 
   // Add the documents and check SchemaTypeIds match
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId email_document_id,
                              document_store->Put(email_document));
   ICING_ASSERT_OK_AND_ASSIGN(
@@ -1948,7 +2086,7 @@
 
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
   ICING_EXPECT_OK(schema_store->SetSchema(schema));
 
   // Add two documents, with and without a subject
@@ -1969,9 +2107,12 @@
 
   // Insert documents and check they're ok
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId email_without_subject_document_id,
                              document_store->Put(email_without_subject));
   EXPECT_THAT(document_store->Get(email_without_subject_document_id),
@@ -2016,7 +2157,7 @@
 
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
   ICING_EXPECT_OK(schema_store->SetSchema(schema));
 
   // Add a "email" and "message" document
@@ -2036,9 +2177,12 @@
 
   // Insert documents and check they're ok
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId email_document_id,
                              document_store->Put(email_document));
   EXPECT_THAT(document_store->Get(email_document_id),
@@ -2082,7 +2226,7 @@
 
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
   ICING_EXPECT_OK(schema_store->SetSchema(schema));
 
   ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId old_email_schema_type_id,
@@ -2104,9 +2248,12 @@
 
   // Add the documents and check SchemaTypeIds match
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId email_document_id,
                              document_store->Put(email_document));
   ICING_ASSERT_OK_AND_ASSIGN(
@@ -2175,7 +2322,7 @@
 
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
   ICING_EXPECT_OK(schema_store->SetSchema(schema));
 
   // Add two documents, with and without a subject
@@ -2196,9 +2343,12 @@
 
   // Insert documents and check they're ok
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId email_without_subject_document_id,
                              document_store->Put(email_without_subject));
   EXPECT_THAT(document_store->Get(email_without_subject_document_id),
@@ -2246,7 +2396,7 @@
 
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, schema_store_dir));
+      SchemaStore::Create(&filesystem_, schema_store_dir, &fake_clock_));
   ICING_EXPECT_OK(schema_store->SetSchema(schema));
 
   // Add a "email" and "message" document
@@ -2266,9 +2416,12 @@
 
   // Insert documents and check they're ok
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId email_document_id,
                              document_store->Put(email_document));
   EXPECT_THAT(document_store->Get(email_document_id),
@@ -2302,9 +2455,11 @@
 
 TEST_F(DocumentStoreTest, GetOptimizeInfo) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Nothing should be optimizable yet
   ICING_ASSERT_OK_AND_ASSIGN(DocumentStore::OptimizeInfo optimize_info,
@@ -2337,9 +2492,10 @@
   ICING_ASSERT_OK(document_store->OptimizeInto(optimized_dir));
   document_store.reset();
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> optimized_document_store,
-      DocumentStore::Create(&filesystem_, optimized_dir, &fake_clock_,
-                            schema_store_.get()));
+      create_result, DocumentStore::Create(&filesystem_, optimized_dir,
+                                           &fake_clock_, schema_store_.get()));
+  std::unique_ptr<DocumentStore> optimized_document_store =
+      std::move(create_result.document_store);
 
   ICING_ASSERT_OK_AND_ASSIGN(optimize_info,
                              optimized_document_store->GetOptimizeInfo());
@@ -2350,9 +2506,11 @@
 
 TEST_F(DocumentStoreTest, GetAllNamespaces) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Empty namespaces to start with
   EXPECT_THAT(document_store->GetAllNamespaces(), IsEmpty());
@@ -2418,9 +2576,12 @@
 
 TEST_F(DocumentStoreTest, ReportUsageWithDifferentTimestampsAndGetUsageScores) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              document_store->Put(test_document1_));
 
@@ -2494,9 +2655,12 @@
 
 TEST_F(DocumentStoreTest, ReportUsageWithDifferentTypesAndGetUsageScores) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
                              document_store->Put(test_document1_));
 
@@ -2537,9 +2701,12 @@
   DocumentId document_id;
   {
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> document_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store_.get()));
+    std::unique_ptr<DocumentStore> document_store =
+        std::move(create_result.document_store);
+
     ICING_ASSERT_OK_AND_ASSIGN(document_id,
                                document_store->Put(test_document1_));
 
@@ -2567,9 +2734,11 @@
 
   // Successfully recover from a corrupt derived file issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Usage scores should be the same.
   ASSERT_THAT(document_store->GetUsageScores(document_id),
@@ -2581,9 +2750,12 @@
   DocumentId document_id;
   {
     ICING_ASSERT_OK_AND_ASSIGN(
-        std::unique_ptr<DocumentStore> document_store,
+        DocumentStore::CreateResult create_result,
         DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                               schema_store_.get()));
+    std::unique_ptr<DocumentStore> document_store =
+        std::move(create_result.document_store);
+
     ICING_ASSERT_OK_AND_ASSIGN(
         document_id, document_store->Put(DocumentProto(test_document1_)));
 
@@ -2612,9 +2784,11 @@
 
   // Successfully recover from a data loss issue.
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
 
   // Usage scores should still be available.
   ASSERT_THAT(document_store->GetUsageScores(document_id),
@@ -2623,9 +2797,12 @@
 
 TEST_F(DocumentStoreTest, UsageScoresShouldBeCopiedOverToUpdatedDocument) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(
       DocumentId document_id,
       document_store->Put(DocumentProto(test_document1_)));
@@ -2656,9 +2833,12 @@
 TEST_F(DocumentStoreTest,
        UsageScoresShouldNotBeCopiedOverFromOldSoftDeletedDocs) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(
       DocumentId document_id,
       document_store->Put(DocumentProto(test_document1_)));
@@ -2691,9 +2871,12 @@
 
 TEST_F(DocumentStoreTest, UsageScoresShouldPersistOnOptimize) {
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> document_store,
+      DocumentStore::CreateResult create_result,
       DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
                             schema_store_.get()));
+  std::unique_ptr<DocumentStore> document_store =
+      std::move(create_result.document_store);
+
   ICING_ASSERT_OK_AND_ASSIGN(
       DocumentId document_id1,
       document_store->Put(DocumentProto(test_document1_)));
@@ -2720,9 +2903,10 @@
 
   // Get optimized document store
   ICING_ASSERT_OK_AND_ASSIGN(
-      std::unique_ptr<DocumentStore> optimized_document_store,
-      DocumentStore::Create(&filesystem_, optimized_dir, &fake_clock_,
-                            schema_store_.get()));
+      create_result, DocumentStore::Create(&filesystem_, optimized_dir,
+                                           &fake_clock_, schema_store_.get()));
+  std::unique_ptr<DocumentStore> optimized_document_store =
+      std::move(create_result.document_store);
 
   // Usage scores should be the same.
   // The original document_id2 should have become document_id2 - 1.
@@ -2730,6 +2914,88 @@
               IsOkAndHolds(expected_scores));
 }
 
+TEST_F(DocumentStoreTest, DetectPartialDataLoss) {
+  {
+    // Can put and delete fine.
+    ICING_ASSERT_OK_AND_ASSIGN(
+        DocumentStore::CreateResult create_result,
+        DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
+                              schema_store_.get()));
+    std::unique_ptr<DocumentStore> doc_store =
+        std::move(create_result.document_store);
+    EXPECT_THAT(create_result.data_loss, Eq(DataLoss::NONE));
+
+    ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
+                               doc_store->Put(DocumentProto(test_document1_)));
+    EXPECT_THAT(doc_store->Get(document_id),
+                IsOkAndHolds(EqualsProto(test_document1_)));
+  }
+
+  // "Corrupt" the content written in the log by adding non-checksummed data to
+  // it. This will mess up the checksum of the proto log, forcing it to rewind
+  // to the last saved point and triggering data loss.
+  DocumentProto document = DocumentBuilder().SetKey("namespace", "uri").Build();
+  const std::string serialized_document = document.SerializeAsString();
+
+  const std::string document_log_file =
+      absl_ports::StrCat(document_store_dir_, "/document_log");
+  int64_t file_size = filesystem_.GetFileSize(document_log_file.c_str());
+  filesystem_.PWrite(document_log_file.c_str(), file_size,
+                     serialized_document.data(), serialized_document.size());
+
+  // Successfully recover from a data loss issue.
+  ICING_ASSERT_OK_AND_ASSIGN(
+      DocumentStore::CreateResult create_result,
+      DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
+                            schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+  ASSERT_THAT(create_result.data_loss, Eq(DataLoss::PARTIAL));
+}
+
+TEST_F(DocumentStoreTest, DetectCompleteDataLoss) {
+  int64_t corruptible_offset;
+  const std::string document_log_file =
+      absl_ports::StrCat(document_store_dir_, "/document_log");
+  {
+    // Can put and delete fine.
+    ICING_ASSERT_OK_AND_ASSIGN(
+        DocumentStore::CreateResult create_result,
+        DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
+                              schema_store_.get()));
+    std::unique_ptr<DocumentStore> doc_store =
+        std::move(create_result.document_store);
+    EXPECT_THAT(create_result.data_loss, Eq(DataLoss::NONE));
+
+    // There's some space at the beginning of the file (e.g. header, kmagic,
+    // etc) that is necessary to initialize the FileBackedProtoLog. We can't
+    // corrupt that region, so we need to figure out the offset at which
+    // documents will be written to - which is the file size after
+    // initialization.
+    corruptible_offset = filesystem_.GetFileSize(document_log_file.c_str());
+
+    ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id,
+                               doc_store->Put(DocumentProto(test_document1_)));
+    EXPECT_THAT(doc_store->Get(document_id),
+                IsOkAndHolds(EqualsProto(test_document1_)));
+  }
+
+  // "Corrupt" the persisted content written in the log. We can't recover if
+  // the persisted data was corrupted.
+  std::string corruption = "abc";
+  filesystem_.PWrite(document_log_file.c_str(), /*offset=*/corruptible_offset,
+                     corruption.data(), corruption.size());
+
+  // Successfully recover from a data loss issue.
+  ICING_ASSERT_OK_AND_ASSIGN(
+      DocumentStore::CreateResult create_result,
+      DocumentStore::Create(&filesystem_, document_store_dir_, &fake_clock_,
+                            schema_store_.get()));
+  std::unique_ptr<DocumentStore> doc_store =
+      std::move(create_result.document_store);
+  ASSERT_THAT(create_result.data_loss, Eq(DataLoss::COMPLETE));
+}
+
 }  // namespace
 
 }  // namespace lib
diff --git a/icing/testing/fake-clock.h b/icing/testing/fake-clock.h
index 54b56c3..f9f3654 100644
--- a/icing/testing/fake-clock.h
+++ b/icing/testing/fake-clock.h
@@ -20,6 +20,22 @@
 namespace icing {
 namespace lib {
 
+// A fake timer class for tests. It makes sure that the elapsed time changes
+// every time it's requested.
+class FakeTimer : public Timer {
+ public:
+  int64_t GetElapsedMilliseconds() override {
+    return fake_elapsed_milliseconds_;
+  }
+
+  void SetElapsedMilliseconds(int64_t elapsed_milliseconds) {
+    fake_elapsed_milliseconds_ = elapsed_milliseconds;
+  }
+
+ private:
+  int64_t fake_elapsed_milliseconds_ = 0;
+};
+
 // Wrapper around real-time clock functions. This is separated primarily so
 // tests can override this clock and inject it into the class under test.
 class FakeClock : public Clock {
@@ -30,8 +46,17 @@
     milliseconds_ = milliseconds;
   }
 
+  std::unique_ptr<Timer> GetNewTimer() const override {
+    return std::make_unique<FakeTimer>(fake_timer_);
+  }
+
+  void SetTimerElapsedMilliseconds(int64_t timer_elapsed_milliseconds) {
+    fake_timer_.SetElapsedMilliseconds(timer_elapsed_milliseconds);
+  }
+
  private:
   int64_t milliseconds_ = 0;
+  FakeTimer fake_timer_;
 };
 
 }  // namespace lib
diff --git a/icing/testing/fake-clock_test.cc b/icing/testing/fake-clock_test.cc
index 3c75ae9..4b36727 100644
--- a/icing/testing/fake-clock_test.cc
+++ b/icing/testing/fake-clock_test.cc
@@ -24,7 +24,7 @@
 
 using ::testing::Eq;
 
-TEST(FakeClockTest, GetSetOk) {
+TEST(FakeClockTest, GetSetSystemTimeOk) {
   FakeClock fake_clock;
   EXPECT_THAT(fake_clock.GetSystemTimeMilliseconds(), Eq(0));
 
@@ -35,6 +35,17 @@
   EXPECT_THAT(fake_clock.GetSystemTimeMilliseconds(), Eq(-1));
 }
 
+TEST(FakeClockTest, GetSetTimerElapsedTimeOk) {
+  FakeClock fake_clock;
+  EXPECT_THAT(fake_clock.GetNewTimer()->GetElapsedMilliseconds(), Eq(0));
+
+  fake_clock.SetTimerElapsedMilliseconds(10);
+  EXPECT_THAT(fake_clock.GetNewTimer()->GetElapsedMilliseconds(), Eq(10));
+
+  fake_clock.SetTimerElapsedMilliseconds(-1);
+  EXPECT_THAT(fake_clock.GetNewTimer()->GetElapsedMilliseconds(), Eq(-1));
+}
+
 }  // namespace
 
 }  // namespace lib
diff --git a/icing/testing/platform.h b/icing/testing/platform.h
new file mode 100644
index 0000000..7b7f212
--- /dev/null
+++ b/icing/testing/platform.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ICING_TESTING_PLATFORM_H_
+#define ICING_TESTING_PLATFORM_H_
+
+// This file is meant to hold util functions for tests that help the test
+// determine which platform-specific configuration it may be running in.
+namespace icing {
+namespace lib {
+
+// Returns true if the test was built with the CFStringTokenizer as the
+// implementation of LanguageSegmenter.
+inline bool IsCfStringTokenization() {
+#if defined(__APPLE__) && !defined(ICING_IOS_ICU4C_SEGMENTATION)
+  return true;
+#endif  // defined(__APPLE__) && !defined(ICING_IOS_ICU4C_SEGMENTATION)
+  return false;
+}
+
+inline bool IsReverseJniTokenization() {
+#ifdef ICING_REVERSE_JNI_SEGMENTATION
+  return true;
+#endif  // ICING_REVERSE_JNI_SEGMENTATION
+  return false;
+}
+
+}  // namespace lib
+}  // namespace icing
+
+#endif  // ICING_TESTING_PLATFORM_H_
diff --git a/icing/tokenization/icu/icu-language-segmenter-factory.cc b/icing/tokenization/icu/icu-language-segmenter-factory.cc
index 9213fbe..363bc6d 100644
--- a/icing/tokenization/icu/icu-language-segmenter-factory.cc
+++ b/icing/tokenization/icu/icu-language-segmenter-factory.cc
@@ -32,7 +32,7 @@
 //   A LanguageSegmenter on success
 //   INVALID_ARGUMENT if locale string is invalid
 //
-// TODO(samzheng): Figure out if we want to verify locale strings and notify
+// TODO(b/156383798): Figure out if we want to verify locale strings and notify
 // users. Right now illegal locale strings will be ignored by ICU. ICU
 // components will be created with its default locale.
 libtextclassifier3::StatusOr<std::unique_ptr<LanguageSegmenter>> Create(
diff --git a/icing/tokenization/icu/icu-language-segmenter_test.cc b/icing/tokenization/icu/icu-language-segmenter_test.cc
index e60f6d5..c0d6d43 100644
--- a/icing/tokenization/icu/icu-language-segmenter_test.cc
+++ b/icing/tokenization/icu/icu-language-segmenter_test.cc
@@ -395,7 +395,6 @@
               IsOkAndHolds(ElementsAre("āăąḃḅḇčćç")));
 }
 
-// TODO(samzheng): test cases for more languages (e.g. top 20 in the world)
 TEST_P(IcuLanguageSegmenterAllLocalesTest, WhitespaceSplitLanguages) {
   ICING_ASSERT_OK_AND_ASSIGN(auto language_segmenter,
                              language_segmenter_factory::Create(GetOptions()));
@@ -408,7 +407,6 @@
       IsOkAndHolds(ElementsAre("나는", " ", "매일", " ", "출근합니다", ".")));
 }
 
-// TODO(samzheng): more mixed languages test cases
 TEST_P(IcuLanguageSegmenterAllLocalesTest, MixedLanguages) {
   ICING_ASSERT_OK_AND_ASSIGN(auto language_segmenter,
                              language_segmenter_factory::Create(GetOptions()));
diff --git a/icing/tokenization/language-segmenter-iterator_test.cc b/icing/tokenization/language-segmenter-iterator_test.cc
index a1b031a..2b1911e 100644
--- a/icing/tokenization/language-segmenter-iterator_test.cc
+++ b/icing/tokenization/language-segmenter-iterator_test.cc
@@ -17,6 +17,7 @@
 #include "icing/absl_ports/str_cat.h"
 #include "icing/helpers/icu/icu-data-file-helper.h"
 #include "icing/testing/common-matchers.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/test-data.h"
 #include "icing/tokenization/language-segmenter-factory.h"
 #include "icing/tokenization/language-segmenter.h"
@@ -35,10 +36,12 @@
 class LanguageSegmenterIteratorTest : public testing::Test {
  protected:
   void SetUp() override {
-    ICING_ASSERT_OK(
-        // File generated via icu_data_file rule in //icing/BUILD.
-        icu_data_file_helper::SetUpICUDataFile(
-            GetTestFilePath("icing/icu.dat")));
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      ICING_ASSERT_OK(
+          // File generated via icu_data_file rule in //icing/BUILD.
+          icu_data_file_helper::SetUpICUDataFile(
+              GetTestFilePath("icing/icu.dat")));
+    }
   }
 };
 
diff --git a/icing/tokenization/plain-tokenizer_test.cc b/icing/tokenization/plain-tokenizer_test.cc
index df0981b..f578567 100644
--- a/icing/tokenization/plain-tokenizer_test.cc
+++ b/icing/tokenization/plain-tokenizer_test.cc
@@ -21,6 +21,7 @@
 #include "icing/helpers/icu/icu-data-file-helper.h"
 #include "icing/testing/common-matchers.h"
 #include "icing/testing/icu-i18n-test-utils.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/test-data.h"
 #include "icing/tokenization/language-segmenter-factory.h"
 #include "icing/tokenization/tokenizer-factory.h"
@@ -35,10 +36,12 @@
 class PlainTokenizerTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    ICING_ASSERT_OK(
-        // File generated via icu_data_file rule in //icing/BUILD.
-        icu_data_file_helper::SetUpICUDataFile(
-            GetTestFilePath("icing/icu.dat")));
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      ICING_ASSERT_OK(
+          // File generated via icu_data_file rule in //icing/BUILD.
+          icu_data_file_helper::SetUpICUDataFile(
+              GetTestFilePath("icing/icu.dat")));
+    }
   }
 };
 
@@ -132,14 +135,29 @@
                                        EqualsToken(Token::REGULAR, "World"))));
 
   // Full-width punctuation marks are filtered out.
-  EXPECT_THAT(
-      plain_tokenizer->TokenizeAll("你好,世界!你好:世界。“你好”世界?"),
-      IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "你好"),
-                               EqualsToken(Token::REGULAR, "世界"),
-                               EqualsToken(Token::REGULAR, "你好"),
-                               EqualsToken(Token::REGULAR, "世界"),
-                               EqualsToken(Token::REGULAR, "你好"),
-                               EqualsToken(Token::REGULAR, "世界"))));
+  std::vector<std::string_view> exp_tokens;
+  if (IsCfStringTokenization()) {
+    EXPECT_THAT(
+        plain_tokenizer->TokenizeAll("你好,世界!你好:世界。“你好”世界?"),
+        IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "你"),
+                                 EqualsToken(Token::REGULAR, "好"),
+                                 EqualsToken(Token::REGULAR, "世界"),
+                                 EqualsToken(Token::REGULAR, "你"),
+                                 EqualsToken(Token::REGULAR, "好"),
+                                 EqualsToken(Token::REGULAR, "世界"),
+                                 EqualsToken(Token::REGULAR, "你"),
+                                 EqualsToken(Token::REGULAR, "好"),
+                                 EqualsToken(Token::REGULAR, "世界"))));
+  } else {
+    EXPECT_THAT(
+        plain_tokenizer->TokenizeAll("你好,世界!你好:世界。“你好”世界?"),
+        IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "你好"),
+                                 EqualsToken(Token::REGULAR, "世界"),
+                                 EqualsToken(Token::REGULAR, "你好"),
+                                 EqualsToken(Token::REGULAR, "世界"),
+                                 EqualsToken(Token::REGULAR, "你好"),
+                                 EqualsToken(Token::REGULAR, "世界"))));
+  }
 }
 
 TEST_F(PlainTokenizerTest, SpecialCharacters) {
@@ -166,7 +184,10 @@
 }
 
 TEST_F(PlainTokenizerTest, CJKT) {
-  language_segmenter_factory::SegmenterOptions options(ULOC_US);
+  // In plain tokenizer, CJKT characters are handled the same way as non-CJKT
+  // characters, just add these tests as sanity checks.
+  // Chinese
+  language_segmenter_factory::SegmenterOptions options(ULOC_SIMPLIFIED_CHINESE);
   ICING_ASSERT_OK_AND_ASSIGN(
       auto language_segmenter,
       language_segmenter_factory::Create(std::move(options)));
@@ -174,11 +195,6 @@
                              tokenizer_factory::CreateIndexingTokenizer(
                                  StringIndexingConfig::TokenizerType::PLAIN,
                                  language_segmenter.get()));
-
-  // In plain tokenizer, CJKT characters are handled the same way as non-CJKT
-  // characters, just add these tests as sanity checks.
-
-  // Chinese
   EXPECT_THAT(plain_tokenizer->TokenizeAll("我每天走路去上班。"),
               IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "我"),
                                        EqualsToken(Token::REGULAR, "每天"),
@@ -186,16 +202,38 @@
                                        EqualsToken(Token::REGULAR, "去"),
                                        EqualsToken(Token::REGULAR, "上班"))));
   // Japanese
-  EXPECT_THAT(
-      plain_tokenizer->TokenizeAll("私は毎日仕事に歩いています。"),
-      IsOkAndHolds(ElementsAre(
-          EqualsToken(Token::REGULAR, "私"), EqualsToken(Token::REGULAR, "は"),
-          EqualsToken(Token::REGULAR, "毎日"),
-          EqualsToken(Token::REGULAR, "仕事"),
-          EqualsToken(Token::REGULAR, "に"), EqualsToken(Token::REGULAR, "歩"),
-          EqualsToken(Token::REGULAR, "い"),
-          EqualsToken(Token::REGULAR, "てい"),
-          EqualsToken(Token::REGULAR, "ます"))));
+  options = language_segmenter_factory::SegmenterOptions(ULOC_JAPANESE);
+  ICING_ASSERT_OK_AND_ASSIGN(
+      language_segmenter,
+      language_segmenter_factory::Create(std::move(options)));
+  ICING_ASSERT_OK_AND_ASSIGN(plain_tokenizer,
+                             tokenizer_factory::CreateIndexingTokenizer(
+                                 StringIndexingConfig::TokenizerType::PLAIN,
+                                 language_segmenter.get()));
+  if (IsCfStringTokenization()) {
+    EXPECT_THAT(plain_tokenizer->TokenizeAll("私は毎日仕事に歩いています。"),
+                IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "私"),
+                                         EqualsToken(Token::REGULAR, "は"),
+                                         EqualsToken(Token::REGULAR, "毎日"),
+                                         EqualsToken(Token::REGULAR, "仕事"),
+                                         EqualsToken(Token::REGULAR, "に"),
+                                         EqualsToken(Token::REGULAR, "歩い"),
+                                         EqualsToken(Token::REGULAR, "て"),
+                                         EqualsToken(Token::REGULAR, "い"),
+                                         EqualsToken(Token::REGULAR, "ます"))));
+  } else {
+    EXPECT_THAT(plain_tokenizer->TokenizeAll("私は毎日仕事に歩いています。"),
+                IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "私"),
+                                         EqualsToken(Token::REGULAR, "は"),
+                                         EqualsToken(Token::REGULAR, "毎日"),
+                                         EqualsToken(Token::REGULAR, "仕事"),
+                                         EqualsToken(Token::REGULAR, "に"),
+                                         EqualsToken(Token::REGULAR, "歩"),
+                                         EqualsToken(Token::REGULAR, "い"),
+                                         EqualsToken(Token::REGULAR, "てい"),
+                                         EqualsToken(Token::REGULAR, "ます"))));
+  }
+
   // Khmer
   EXPECT_THAT(plain_tokenizer->TokenizeAll("ញុំដើរទៅធ្វើការរាល់ថ្ងៃ។"),
               IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "ញុំ"),
@@ -210,13 +248,27 @@
                                EqualsToken(Token::REGULAR, "출근합니다"))));
 
   // Thai
-  EXPECT_THAT(plain_tokenizer->TokenizeAll("ฉันเดินไปทำงานทุกวัน"),
-              IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "ฉัน"),
-                                       EqualsToken(Token::REGULAR, "เดิน"),
-                                       EqualsToken(Token::REGULAR, "ไป"),
-                                       EqualsToken(Token::REGULAR, "ทำงาน"),
-                                       EqualsToken(Token::REGULAR, "ทุก"),
-                                       EqualsToken(Token::REGULAR, "วัน"))));
+  // DIFFERENCE!! Disagreement over how to segment "ทุกวัน" (iOS groups).
+  // This difference persists even when locale is set to THAI
+  if (IsCfStringTokenization()) {
+    ICING_ASSERT_OK_AND_ASSIGN(
+        std::vector<Token> tokens,
+        plain_tokenizer->TokenizeAll("ฉันเดินไปทำงานทุกวัน"));
+
+    EXPECT_THAT(tokens, ElementsAre(EqualsToken(Token::REGULAR, "ฉัน"),
+                                    EqualsToken(Token::REGULAR, "เดิน"),
+                                    EqualsToken(Token::REGULAR, "ไป"),
+                                    EqualsToken(Token::REGULAR, "ทำงาน"),
+                                    EqualsToken(Token::REGULAR, "ทุกวัน")));
+  } else {
+    EXPECT_THAT(plain_tokenizer->TokenizeAll("ฉันเดินไปทำงานทุกวัน"),
+                IsOkAndHolds(ElementsAre(EqualsToken(Token::REGULAR, "ฉัน"),
+                                         EqualsToken(Token::REGULAR, "เดิน"),
+                                         EqualsToken(Token::REGULAR, "ไป"),
+                                         EqualsToken(Token::REGULAR, "ทำงาน"),
+                                         EqualsToken(Token::REGULAR, "ทุก"),
+                                         EqualsToken(Token::REGULAR, "วัน"))));
+  }
 }
 
 TEST_F(PlainTokenizerTest, ResetToTokenAfterSimple) {
diff --git a/icing/tokenization/raw-query-tokenizer.cc b/icing/tokenization/raw-query-tokenizer.cc
index 50b25c5..205d3a2 100644
--- a/icing/tokenization/raw-query-tokenizer.cc
+++ b/icing/tokenization/raw-query-tokenizer.cc
@@ -247,7 +247,7 @@
 //
 // NOTE: Please update the state transition table above if this is updated.
 //
-// TODO(samzheng): support syntax "-property1:term1", right now we don't allow
+// TODO(tjbarron): support syntax "-property1:term1", right now we don't allow
 // exclusion and property restriction applied on the same term.
 // TODO(b/141007791): figure out how we'd like to support special characters
 // like "+", "&", "@", "#" in indexing and query tokenizers.
diff --git a/icing/tokenization/raw-query-tokenizer_test.cc b/icing/tokenization/raw-query-tokenizer_test.cc
index d4af9ed..e1a666b 100644
--- a/icing/tokenization/raw-query-tokenizer_test.cc
+++ b/icing/tokenization/raw-query-tokenizer_test.cc
@@ -18,6 +18,7 @@
 #include "gtest/gtest.h"
 #include "icing/helpers/icu/icu-data-file-helper.h"
 #include "icing/testing/common-matchers.h"
+#include "icing/testing/platform.h"
 #include "icing/testing/test-data.h"
 #include "icing/tokenization/language-segmenter-factory.h"
 #include "icing/tokenization/tokenizer-factory.h"
@@ -33,10 +34,12 @@
 class RawQueryTokenizerTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    ICING_ASSERT_OK(
-        // File generated via icu_data_file rule in //icing/BUILD.
-        icu_data_file_helper::SetUpICUDataFile(
-            GetTestFilePath("icing/icu.dat")));
+    if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
+      ICING_ASSERT_OK(
+          // File generated via icu_data_file rule in //icing/BUILD.
+          icu_data_file_helper::SetUpICUDataFile(
+              GetTestFilePath("icing/icu.dat")));
+    }
   }
 };
 
@@ -466,16 +469,35 @@
                                               language_segmenter.get()));
 
   // Exclusion only applies to the term right after it.
-  EXPECT_THAT(raw_query_tokenizer->TokenizeAll("-今天天气很好"),
-              IsOkAndHolds(ElementsAre(EqualsToken(Token::QUERY_EXCLUSION, ""),
-                                       EqualsToken(Token::REGULAR, "今天"),
-                                       EqualsToken(Token::REGULAR, "天气"),
-                                       EqualsToken(Token::REGULAR, "很好"))));
+  if (IsCfStringTokenization()) {
+    EXPECT_THAT(
+        raw_query_tokenizer->TokenizeAll("-今天天气很好"),
+        IsOkAndHolds(ElementsAre(EqualsToken(Token::QUERY_EXCLUSION, ""),
+                                 EqualsToken(Token::REGULAR, "今天"),
+                                 EqualsToken(Token::REGULAR, "天气"),
+                                 EqualsToken(Token::REGULAR, "很"),
+                                 EqualsToken(Token::REGULAR, "好"))));
+  } else {
+    EXPECT_THAT(
+        raw_query_tokenizer->TokenizeAll("-今天天气很好"),
+        IsOkAndHolds(ElementsAre(EqualsToken(Token::QUERY_EXCLUSION, ""),
+                                 EqualsToken(Token::REGULAR, "今天"),
+                                 EqualsToken(Token::REGULAR, "天气"),
+                                 EqualsToken(Token::REGULAR, "很好"))));
+  }
 
-  EXPECT_THAT(
-      raw_query_tokenizer->TokenizeAll("property1:你好"),
-      IsOkAndHolds(ElementsAre(EqualsToken(Token::QUERY_PROPERTY, "property1"),
-                               EqualsToken(Token::REGULAR, "你好"))));
+  if (IsCfStringTokenization()) {
+    EXPECT_THAT(raw_query_tokenizer->TokenizeAll("property1:你好"),
+                IsOkAndHolds(
+                    ElementsAre(EqualsToken(Token::QUERY_PROPERTY, "property1"),
+                                EqualsToken(Token::REGULAR, "你"),
+                                EqualsToken(Token::REGULAR, "好"))));
+  } else {
+    EXPECT_THAT(raw_query_tokenizer->TokenizeAll("property1:你好"),
+                IsOkAndHolds(
+                    ElementsAre(EqualsToken(Token::QUERY_PROPERTY, "property1"),
+                                EqualsToken(Token::REGULAR, "你好"))));
+  }
 
   EXPECT_THAT(
       raw_query_tokenizer->TokenizeAll("标题:你好"),
@@ -567,21 +589,42 @@
       tokenizer_factory::CreateQueryTokenizer(tokenizer_factory::RAW_QUERY,
                                               language_segmenter.get()));
 
-  EXPECT_THAT(
-      raw_query_tokenizer->TokenizeAll(
-          "こんにちはgood afternoon, title:今天 OR (ในวันนี้ -B12)"),
-      IsOkAndHolds(ElementsAre(
-          EqualsToken(Token::REGULAR, "こんにちは"),
-          EqualsToken(Token::REGULAR, "good"),
-          EqualsToken(Token::REGULAR, "afternoon"),
-          EqualsToken(Token::QUERY_PROPERTY, "title"),
-          EqualsToken(Token::REGULAR, "今天"), EqualsToken(Token::QUERY_OR, ""),
-          EqualsToken(Token::QUERY_LEFT_PARENTHESES, ""),
-          EqualsToken(Token::REGULAR, "ใน"), EqualsToken(Token::REGULAR, "วัน"),
-          EqualsToken(Token::REGULAR, "นี้"),
-          EqualsToken(Token::QUERY_EXCLUSION, ""),
-          EqualsToken(Token::REGULAR, "B12"),
-          EqualsToken(Token::QUERY_RIGHT_PARENTHESES, ""))));
+  if (IsCfStringTokenization()) {
+    EXPECT_THAT(raw_query_tokenizer->TokenizeAll(
+                    "こんにちはgood afternoon, title:今天 OR (ในวันนี้ -B12)"),
+                IsOkAndHolds(ElementsAre(
+                    EqualsToken(Token::REGULAR, "こんにちは"),
+                    EqualsToken(Token::REGULAR, "good"),
+                    EqualsToken(Token::REGULAR, "afternoon"),
+                    EqualsToken(Token::QUERY_PROPERTY, "title"),
+                    EqualsToken(Token::REGULAR, "今天"),
+                    EqualsToken(Token::QUERY_OR, ""),
+                    EqualsToken(Token::QUERY_LEFT_PARENTHESES, ""),
+                    EqualsToken(Token::REGULAR, "ใน"),
+                    EqualsToken(Token::REGULAR, "วันนี้"),
+                    EqualsToken(Token::QUERY_EXCLUSION, ""),
+                    EqualsToken(Token::REGULAR, "B12"),
+                    EqualsToken(Token::QUERY_RIGHT_PARENTHESES, ""))));
+  } else {
+    ICING_ASSERT_OK_AND_ASSIGN(
+        std::vector<Token> tokens,
+        raw_query_tokenizer->TokenizeAll(
+            "こんにちはgood afternoon, title:今天 OR (ในวันนี้ -B12)"));
+    EXPECT_THAT(tokens,
+                ElementsAre(EqualsToken(Token::REGULAR, "こんにちは"),
+                            EqualsToken(Token::REGULAR, "good"),
+                            EqualsToken(Token::REGULAR, "afternoon"),
+                            EqualsToken(Token::QUERY_PROPERTY, "title"),
+                            EqualsToken(Token::REGULAR, "今天"),
+                            EqualsToken(Token::QUERY_OR, ""),
+                            EqualsToken(Token::QUERY_LEFT_PARENTHESES, ""),
+                            EqualsToken(Token::REGULAR, "ใน"),
+                            EqualsToken(Token::REGULAR, "วัน"),
+                            EqualsToken(Token::REGULAR, "นี้"),
+                            EqualsToken(Token::QUERY_EXCLUSION, ""),
+                            EqualsToken(Token::REGULAR, "B12"),
+                            EqualsToken(Token::QUERY_RIGHT_PARENTHESES, "")));
+  }
 }
 
 }  // namespace
diff --git a/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-factory.cc b/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-factory.cc
index db973f3..0da4c2d 100644
--- a/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-factory.cc
+++ b/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-factory.cc
@@ -34,7 +34,7 @@
 //   A LanguageSegmenter on success
 //   INVALID_ARGUMENT if locale string is invalid
 //
-// TODO(samzheng): Figure out if we want to verify locale strings and notify
+// TODO(b/156383798): Figure out if we want to verify locale strings and notify
 // users. Right now illegal locale strings will be ignored by ICU. ICU
 // components will be created with its default locale.
 libtextclassifier3::StatusOr<std::unique_ptr<LanguageSegmenter>> Create(
diff --git a/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-test.cc b/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-test.cc
index 1cd6fa3..2c268ff 100644
--- a/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-test.cc
+++ b/icing/tokenization/reverse_jni/reverse-jni-language-segmenter-test.cc
@@ -423,7 +423,6 @@
               IsOkAndHolds(ElementsAre("āăąḃḅḇčćç")));
 }
 
-// TODO(samzheng): test cases for more languages (e.g. top 20 in the world)
 TEST_P(ReverseJniLanguageSegmenterTest, WhitespaceSplitLanguages) {
   ICING_ASSERT_OK_AND_ASSIGN(
       auto language_segmenter,
@@ -438,7 +437,6 @@
       IsOkAndHolds(ElementsAre("나는", " ", "매일", " ", "출근합니다", ".")));
 }
 
-// TODO(samzheng): more mixed languages test cases
 TEST_P(ReverseJniLanguageSegmenterTest, MixedLanguages) {
   ICING_ASSERT_OK_AND_ASSIGN(
       auto language_segmenter,
diff --git a/icing/tokenization/simple/space-language-segmenter-factory.cc b/icing/tokenization/simple/space-language-segmenter-factory.cc
index 1cca603..856ba0a 100644
--- a/icing/tokenization/simple/space-language-segmenter-factory.cc
+++ b/icing/tokenization/simple/space-language-segmenter-factory.cc
@@ -27,7 +27,7 @@
 //   A LanguageSegmenter on success
 //   INVALID_ARGUMENT if locale string is invalid
 //
-// TODO(samzheng): Figure out if we want to verify locale strings and notify
+// TODO(b/156383798): Figure out if we want to verify locale strings and notify
 // users. Right now illegal locale strings will be ignored by ICU. ICU
 // components will be created with its default locale.
 libtextclassifier3::StatusOr<std::unique_ptr<LanguageSegmenter>> Create(
diff --git a/icing/tokenization/token.h b/icing/tokenization/token.h
index 0bb3aaf..dda9efc 100644
--- a/icing/tokenization/token.h
+++ b/icing/tokenization/token.h
@@ -20,10 +20,6 @@
 namespace icing {
 namespace lib {
 
-// TODO(samzheng) Add group id support if needed. Right now in raw query we
-// don't need group ids since all our query operators (OR, Exclusion, Property
-// Restriction) only apply to the token right after them (vs. applying to
-// multiple tokens after them). The "groups" of tokens can be easily recognized.
 struct Token {
   enum Type {
     // Common types
diff --git a/icing/transform/icu/icu-normalizer.cc b/icing/transform/icu/icu-normalizer.cc
index 0bb8326..eb0eead 100644
--- a/icing/transform/icu/icu-normalizer.cc
+++ b/icing/transform/icu/icu-normalizer.cc
@@ -41,7 +41,8 @@
 // form decomposition) and NFKC (compatible normalization form composition)
 // are applied as well as some other rules we need. More information at
 // http://www.unicode.org/reports/tr15/
-// TODO(samzheng) Figure out if we need to support small hiragana to katakana
+//
+// Please note that the following rules don't support small hiragana to katakana
 // transformation.
 constexpr UChar kTransformRulesUtf16[] =
     u"Lower; "                      // Lowercase
@@ -74,7 +75,7 @@
   }
 
   // Maximum number of pieces a Unicode character can be decomposed into.
-  // TODO(samzheng) figure out if this number is proper.
+  // TODO(tjbarron) figure out if this number is proper.
   constexpr int kDecompositionBufferCapacity = 5;
 
   // A buffer used to store Unicode decomposition mappings of only one
diff --git a/icing/transform/icu/icu-normalizer_test.cc b/icing/transform/icu/icu-normalizer_test.cc
index 83fa972..f5d20ff 100644
--- a/icing/transform/icu/icu-normalizer_test.cc
+++ b/icing/transform/icu/icu-normalizer_test.cc
@@ -125,7 +125,6 @@
   // Our current ICU rules can't handle Hebrew properly, e.g. the accents in
   // "אָלֶף־בֵּית עִבְרִי"
   // will be removed.
-  // TODO (samzheng): figure out how we should handle Hebrew.
 }
 
 TEST_F(IcuNormalizerTest, FullWidthCharsToASCII) {
diff --git a/icing/util/clock.cc b/icing/util/clock.cc
index 7843bc4..270b5f0 100644
--- a/icing/util/clock.cc
+++ b/icing/util/clock.cc
@@ -16,16 +16,11 @@
 
 #include <chrono>  // NOLINT. Abseil library is not available in AOSP so we have
                    // to use chrono to get current time in milliseconds.
+#include <memory>
 
 namespace icing {
 namespace lib {
 
-int64_t Clock::GetSystemTimeMilliseconds() const {
-  return std::chrono::duration_cast<std::chrono::milliseconds>(
-             std::chrono::system_clock::now().time_since_epoch())
-      .count();
-}
-
 int64_t GetSteadyTimeNanoseconds() {
   return std::chrono::duration_cast<std::chrono::nanoseconds>(
              std::chrono::steady_clock::now().time_since_epoch())
@@ -38,5 +33,15 @@
       .count();
 }
 
+int64_t Clock::GetSystemTimeMilliseconds() const {
+  return std::chrono::duration_cast<std::chrono::milliseconds>(
+             std::chrono::system_clock::now().time_since_epoch())
+      .count();
+}
+
+std::unique_ptr<Timer> Clock::GetNewTimer() const {
+  return std::make_unique<Timer>();
+}
+
 }  // namespace lib
 }  // namespace icing
diff --git a/icing/util/clock.h b/icing/util/clock.h
index a37fe58..06f1c9d 100644
--- a/icing/util/clock.h
+++ b/icing/util/clock.h
@@ -16,21 +16,11 @@
 #define ICING_UTIL_CLOCK_H_
 
 #include <cstdint>
+#include <memory>
 
 namespace icing {
 namespace lib {
 
-// Wrapper around real-time clock functions. This is separated primarily so
-// tests can override this clock and inject it into the class under test.
-class Clock {
- public:
-  virtual ~Clock() = default;
-
-  // Returns the current time in milliseconds, it's guaranteed that the return
-  // value is non-negative.
-  virtual int64_t GetSystemTimeMilliseconds() const;
-};
-
 // Returns the current steady time in nanoseconds. The steady clock is different
 // from the system clock. It's monotonic and never returns a lower value than a
 // previous call, while a system clock can be occasionally adjusted.
@@ -42,6 +32,38 @@
 // adjusted.
 int64_t GetSteadyTimeMilliseconds();
 
+// Used to calculate the elapsed time.
+class Timer {
+ public:
+  // Creates and starts the timer.
+  Timer() : start_timestamp_milliseconds_(GetSteadyTimeMilliseconds()) {}
+
+  virtual ~Timer() = default;
+
+  // Returns the elapsed time from when timer started.
+  virtual int64_t GetElapsedMilliseconds() {
+    return GetSteadyTimeMilliseconds() - start_timestamp_milliseconds_;
+  }
+
+ private:
+  int64_t start_timestamp_milliseconds_;
+};
+
+// Wrapper around real-time clock functions. This is separated primarily so
+// tests can override this clock and inject it into the class under test.
+class Clock {
+ public:
+  virtual ~Clock() = default;
+
+  // Returns the current time in milliseconds, it's guaranteed that the return
+  // value is non-negative.
+  virtual int64_t GetSystemTimeMilliseconds() const;
+
+  // Returns a timer used to calculate the elapsed time. The timer starts when
+  // the method returns.
+  virtual std::unique_ptr<Timer> GetNewTimer() const;
+};
+
 }  // namespace lib
 }  // namespace icing
 
diff --git a/icing/util/crc32.h b/icing/util/crc32.h
index e8c7c8f..5befe44 100644
--- a/icing/util/crc32.h
+++ b/icing/util/crc32.h
@@ -28,10 +28,6 @@
 // implementation.
 //
 // See https://www.zlib.net/manual.html#Checksum for more details.
-//
-// TODO (samzheng): investigate/benchmark swapping zlib crc32 with
-// util/hash/crc32c.h. Regarding util/hash/crc32c.h, CRC32C::Extend crashes as
-// described in b/145837799.
 class Crc32 {
  public:
   // Default to the checksum of an empty string, that is "0".
diff --git a/icing/util/data-loss.h b/icing/util/data-loss.h
new file mode 100644
index 0000000..cb19ce2
--- /dev/null
+++ b/icing/util/data-loss.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ICING_UTIL_DATA_LOSS_H_
+#define ICING_UTIL_DATA_LOSS_H_
+
+namespace icing {
+namespace lib {
+
+enum DataLoss {
+  // No data loss happened. Everything initialized correctly.
+  NONE,
+
+  // Anything changes made after a persist to disk call were lost. This includes
+  // adding new data, removing old data, and modifying existing data.
+  PARTIAL,
+
+  // All data is lost. IcingSearchEngine has completely reset.
+  COMPLETE
+};
+
+}
+}  // namespace icing
+
+#endif  // ICING_UTIL_DATA_LOSS_H_
diff --git a/icing/util/document-validator.cc b/icing/util/document-validator.cc
index 36b84f8..4d86913 100644
--- a/icing/util/document-validator.cc
+++ b/icing/util/document-validator.cc
@@ -101,7 +101,6 @@
     const PropertyConfigProto& property_config = *property_iter->second;
 
     // Get the property value size according to data type.
-    // TODO (samzheng): make sure values of other data types are empty.
     int value_size = 0;
     if (property_config.data_type() == PropertyConfigProto::DataType::STRING) {
       value_size = property.string_values_size();
diff --git a/icing/util/document-validator_test.cc b/icing/util/document-validator_test.cc
index 16bdf78..6067162 100644
--- a/icing/util/document-validator_test.cc
+++ b/icing/util/document-validator_test.cc
@@ -23,6 +23,7 @@
 #include "icing/proto/schema.pb.h"
 #include "icing/schema/schema-store.h"
 #include "icing/testing/common-matchers.h"
+#include "icing/testing/fake-clock.h"
 #include "icing/testing/tmp-directory.h"
 
 namespace icing {
@@ -57,7 +58,8 @@
     CreateConversationTypeConfig(type_config);
 
     ICING_ASSERT_OK_AND_ASSIGN(
-        schema_store_, SchemaStore::Create(&filesystem_, GetTestTempDir()));
+        schema_store_,
+        SchemaStore::Create(&filesystem_, GetTestTempDir(), &fake_clock_));
     ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
 
     document_validator_ =
@@ -121,6 +123,7 @@
   std::unique_ptr<DocumentValidator> document_validator_;
   std::unique_ptr<SchemaStore> schema_store_;
   Filesystem filesystem_;
+  FakeClock fake_clock_;
 };
 
 TEST_F(DocumentValidatorTest, ValidateSimpleSchemasOk) {
@@ -334,7 +337,7 @@
   // Set a schema with only the 'Email' type
   ICING_ASSERT_OK_AND_ASSIGN(
       std::unique_ptr<SchemaStore> schema_store,
-      SchemaStore::Create(&filesystem_, custom_schema_dir));
+      SchemaStore::Create(&filesystem_, custom_schema_dir, &fake_clock_));
   ASSERT_THAT(schema_store->SetSchema(email_schema), IsOk());
 
   DocumentValidator document_validator(schema_store.get());
diff --git a/icing/util/timer.h b/icing/util/timer.h
deleted file mode 100644
index da872fe..0000000
--- a/icing/util/timer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ICING_UTIL_TIMER_H_
-#define ICING_UTIL_TIMER_H_
-
-#include <cstdint>
-
-#include "icing/util/clock.h"
-
-namespace icing {
-namespace lib {
-
-// A util class to calculate the elapsed time.
-class Timer {
- public:
-  // Timer starts.
-  Timer() : start_timestamp_milliseconds_(GetSteadyTimeMilliseconds()) {}
-
-  // Returns the elapsed time from when timer started.
-  int64_t GetElapsedMilliseconds() {
-    return GetSteadyTimeMilliseconds() - start_timestamp_milliseconds_;
-  }
-
- private:
-  int64_t start_timestamp_milliseconds_;
-};
-
-}  // namespace lib
-}  // namespace icing
-
-#endif  // ICING_UTIL_TIMER_H_
diff --git a/java/src/com/google/android/icing/IcingSearchEngine.java b/java/src/com/google/android/icing/IcingSearchEngine.java
index 76fa33d..cac9fcd 100644
--- a/java/src/com/google/android/icing/IcingSearchEngine.java
+++ b/java/src/com/google/android/icing/IcingSearchEngine.java
@@ -30,6 +30,7 @@
 import com.google.android.icing.proto.OptimizeResultProto;
 import com.google.android.icing.proto.PersistToDiskResultProto;
 import com.google.android.icing.proto.PutResultProto;
+import com.google.android.icing.proto.ReportUsageResultProto;
 import com.google.android.icing.proto.ResetResultProto;
 import com.google.android.icing.proto.ResultSpecProto;
 import com.google.android.icing.proto.SchemaProto;
@@ -38,6 +39,7 @@
 import com.google.android.icing.proto.SearchSpecProto;
 import com.google.android.icing.proto.SetSchemaResultProto;
 import com.google.android.icing.proto.StatusProto;
+import com.google.android.icing.proto.UsageReport;
 import com.google.protobuf.ExtensionRegistryLite;
 import com.google.protobuf.InvalidProtocolBufferException;
 
@@ -48,7 +50,7 @@
   private static final ExtensionRegistryLite EXTENSION_REGISTRY_LITE =
       ExtensionRegistryLite.getEmptyRegistry();
 
-  private final long nativePointer;
+  private long nativePointer;
 
   static {
     // NOTE: This can fail with an UnsatisfiedLinkError
@@ -192,6 +194,26 @@
   }
 
   @NonNull
+  public ReportUsageResultProto reportUsage(@NonNull UsageReport usageReport) {
+    byte[] reportUsageResultBytes = nativeReportUsage(nativePointer, usageReport.toByteArray());
+    if (reportUsageResultBytes == null) {
+      Log.e(TAG, "Received null ReportUsageResultProto from native.");
+      return ReportUsageResultProto.newBuilder()
+          .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL))
+          .build();
+    }
+
+    try {
+      return ReportUsageResultProto.parseFrom(reportUsageResultBytes, EXTENSION_REGISTRY_LITE);
+    } catch (InvalidProtocolBufferException e) {
+      Log.e(TAG, "Error parsing ReportUsageResultProto.", e);
+      return ReportUsageResultProto.newBuilder()
+          .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL))
+          .build();
+    }
+  }
+
+  @NonNull
   public GetAllNamespacesResultProto getAllNamespaces() {
     byte[] getAllNamespacesResultBytes = nativeGetAllNamespaces(nativePointer);
     if (getAllNamespacesResultBytes == null) {
@@ -338,8 +360,7 @@
     }
 
     try {
-      return DeleteResultProto.parseFrom(
-          deleteResultBytes, EXTENSION_REGISTRY_LITE);
+      return DeleteResultProto.parseFrom(deleteResultBytes, EXTENSION_REGISTRY_LITE);
     } catch (InvalidProtocolBufferException e) {
       Log.e(TAG, "Error parsing DeleteResultProto.", e);
       return DeleteResultProto.newBuilder()
@@ -429,8 +450,17 @@
     }
   }
 
+  public void destroy() {
+    if (nativePointer != 0) {
+      nativeDestroy(nativePointer);
+    }
+    nativePointer = 0;
+  }
+
   private static native long nativeCreate(byte[] icingSearchEngineOptionsBytes);
 
+  private static native void nativeDestroy(long nativePointer);
+
   private static native byte[] nativeInitialize(long nativePointer);
 
   private static native byte[] nativeSetSchema(
@@ -444,6 +474,8 @@
 
   private static native byte[] nativeGet(long nativePointer, String namespace, String uri);
 
+  private static native byte[] nativeReportUsage(long nativePointer, byte[] usageReportBytes);
+
   private static native byte[] nativeGetAllNamespaces(long nativePointer);
 
   private static native byte[] nativeSearch(
diff --git a/java/tests/instrumentation/src/com/google/android/icing/IcingSearchEngineTest.java b/java/tests/instrumentation/src/com/google/android/icing/IcingSearchEngineTest.java
index 4c05a7a..ee8ff37 100644
--- a/java/tests/instrumentation/src/com/google/android/icing/IcingSearchEngineTest.java
+++ b/java/tests/instrumentation/src/com/google/android/icing/IcingSearchEngineTest.java
@@ -33,6 +33,7 @@
 import com.google.android.icing.proto.PropertyConfigProto;
 import com.google.android.icing.proto.PropertyProto;
 import com.google.android.icing.proto.PutResultProto;
+import com.google.android.icing.proto.ReportUsageResultProto;
 import com.google.android.icing.proto.ResetResultProto;
 import com.google.android.icing.proto.ResultSpecProto;
 import com.google.android.icing.proto.SchemaProto;
@@ -45,10 +46,12 @@
 import com.google.android.icing.proto.StringIndexingConfig;
 import com.google.android.icing.proto.StringIndexingConfig.TokenizerType;
 import com.google.android.icing.proto.TermMatchType;
+import com.google.android.icing.proto.UsageReport;
 import com.google.android.icing.IcingSearchEngine;
 import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -68,6 +71,8 @@
 
   private File tempDir;
 
+  private IcingSearchEngine icingSearchEngine;
+
   private static SchemaTypeConfigProto createEmailTypeConfig() {
     return SchemaTypeConfigProto.newBuilder()
         .setSchemaType(EMAIL_TYPE)
@@ -104,77 +109,72 @@
   @Before
   public void setUp() throws Exception {
     tempDir = temporaryFolder.newFolder();
+    IcingSearchEngineOptions options =
+        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
+    icingSearchEngine = new IcingSearchEngine(options);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    icingSearchEngine.destroy();
   }
 
   @Test
   public void testInitialize() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-
-    InitializeResultProto initializeResultProto = icing.initialize();
+    InitializeResultProto initializeResultProto = icingSearchEngine.initialize();
     assertStatusOk(initializeResultProto.getStatus());
   }
 
   @Test
   public void testSetAndGetSchema() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     SetSchemaResultProto setSchemaResultProto =
-        icing.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false);
+        icingSearchEngine.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false);
     assertStatusOk(setSchemaResultProto.getStatus());
 
-    GetSchemaResultProto getSchemaResultProto = icing.getSchema();
+    GetSchemaResultProto getSchemaResultProto = icingSearchEngine.getSchema();
     assertStatusOk(getSchemaResultProto.getStatus());
     assertThat(getSchemaResultProto.getSchema()).isEqualTo(schema);
 
     GetSchemaTypeResultProto getSchemaTypeResultProto =
-        icing.getSchemaType(emailTypeConfig.getSchemaType());
+        icingSearchEngine.getSchemaType(emailTypeConfig.getSchemaType());
     assertStatusOk(getSchemaTypeResultProto.getStatus());
     assertThat(getSchemaTypeResultProto.getSchemaTypeConfig()).isEqualTo(emailTypeConfig);
   }
 
   @Test
   public void testPutAndGetDocuments() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-            icing
+            icingSearchEngine
                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
                 .getStatus()
                 .getCode())
         .isEqualTo(StatusProto.Code.OK);
 
     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
-    PutResultProto putResultProto = icing.put(emailDocument);
+    PutResultProto putResultProto = icingSearchEngine.put(emailDocument);
     assertStatusOk(putResultProto.getStatus());
 
-    GetResultProto getResultProto = icing.get("namespace", "uri");
+    GetResultProto getResultProto = icingSearchEngine.get("namespace", "uri");
     assertStatusOk(getResultProto.getStatus());
     assertThat(getResultProto.getDocument()).isEqualTo(emailDocument);
   }
 
   @Test
   public void testSearch() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-            icing
+            icingSearchEngine
                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
                 .getStatus()
                 .getCode())
@@ -184,7 +184,7 @@
         createEmailDocument("namespace", "uri").toBuilder()
             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
             .build();
-    assertStatusOk(icing.put(emailDocument).getStatus());
+    assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
 
     SearchSpecProto searchSpec =
         SearchSpecProto.newBuilder()
@@ -193,7 +193,7 @@
             .build();
 
     SearchResultProto searchResultProto =
-        icing.search(
+        icingSearchEngine.search(
             searchSpec,
             ScoringSpecProto.getDefaultInstance(),
             ResultSpecProto.getDefaultInstance());
@@ -204,15 +204,12 @@
 
   @Test
   public void testGetNextPage() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-            icing
+            icingSearchEngine
                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
                 .getStatus()
                 .getCode())
@@ -225,8 +222,8 @@
               .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
               .build();
       documents.put("uri:" + i, emailDocument);
-      assertWithMessage(icing.put(emailDocument).getStatus().getMessage())
-          .that(icing.put(emailDocument).getStatus().getCode())
+      assertWithMessage(icingSearchEngine.put(emailDocument).getStatus().getMessage())
+          .that(icingSearchEngine.put(emailDocument).getStatus().getCode())
           .isEqualTo(StatusProto.Code.OK);
     }
 
@@ -238,7 +235,8 @@
     ResultSpecProto resultSpecProto = ResultSpecProto.newBuilder().setNumPerPage(1).build();
 
     SearchResultProto searchResultProto =
-        icing.search(searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto);
+        icingSearchEngine.search(
+            searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto);
     assertStatusOk(searchResultProto.getStatus());
     assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
     DocumentProto resultDocument = searchResultProto.getResults(0).getDocument();
@@ -246,7 +244,7 @@
 
     // fetch rest pages
     for (int i = 1; i < 5; i++) {
-      searchResultProto = icing.getNextPage(searchResultProto.getNextPageToken());
+      searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken());
       assertWithMessage(searchResultProto.getStatus().getMessage())
           .that(searchResultProto.getStatus().getCode())
           .isEqualTo(StatusProto.Code.OK);
@@ -256,120 +254,109 @@
     }
 
     // invalidate rest result
-    icing.invalidateNextPageToken(searchResultProto.getNextPageToken());
+    icingSearchEngine.invalidateNextPageToken(searchResultProto.getNextPageToken());
 
-    searchResultProto = icing.getNextPage(searchResultProto.getNextPageToken());
+    searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken());
     assertStatusOk(searchResultProto.getStatus());
     assertThat(searchResultProto.getResultsCount()).isEqualTo(0);
   }
 
   @Test
   public void testDelete() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-            icing
+            icingSearchEngine
                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
                 .getStatus()
                 .getCode())
         .isEqualTo(StatusProto.Code.OK);
 
     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
-    assertStatusOk(icing.put(emailDocument).getStatus());
+    assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
 
-    DeleteResultProto deleteResultProto = icing.delete("namespace", "uri");
+    DeleteResultProto deleteResultProto = icingSearchEngine.delete("namespace", "uri");
     assertStatusOk(deleteResultProto.getStatus());
 
-    GetResultProto getResultProto = icing.get("namespace", "uri");
+    GetResultProto getResultProto = icingSearchEngine.get("namespace", "uri");
     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
   }
 
   @Test
   public void testDeleteByNamespace() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-            icing
+            icingSearchEngine
                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
                 .getStatus()
                 .getCode())
         .isEqualTo(StatusProto.Code.OK);
 
     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
-    assertStatusOk(icing.put(emailDocument).getStatus());
+    assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
 
     DeleteByNamespaceResultProto deleteByNamespaceResultProto =
-        icing.deleteByNamespace("namespace");
+        icingSearchEngine.deleteByNamespace("namespace");
     assertStatusOk(deleteByNamespaceResultProto.getStatus());
 
-    GetResultProto getResultProto = icing.get("namespace", "uri");
+    GetResultProto getResultProto = icingSearchEngine.get("namespace", "uri");
     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
   }
 
   @Test
   public void testDeleteBySchemaType() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-            icing
+            icingSearchEngine
                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
                 .getStatus()
                 .getCode())
         .isEqualTo(StatusProto.Code.OK);
 
     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
-    assertStatusOk(icing.put(emailDocument).getStatus());
+    assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
 
     DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto =
-        icing.deleteBySchemaType(EMAIL_TYPE);
+        icingSearchEngine.deleteBySchemaType(EMAIL_TYPE);
     assertStatusOk(deleteBySchemaTypeResultProto.getStatus());
 
-    GetResultProto getResultProto = icing.get("namespace", "uri");
+    GetResultProto getResultProto = icingSearchEngine.get("namespace", "uri");
     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
   }
 
-
   @Test
   public void testDeleteByQuery() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-        icing
-            .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
-            .getStatus()
-            .getCode())
+            icingSearchEngine
+                .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
+                .getStatus()
+                .getCode())
         .isEqualTo(StatusProto.Code.OK);
 
     DocumentProto emailDocument1 =
         createEmailDocument("namespace", "uri1").toBuilder()
-        .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
-        .build();;
-    assertStatusOk(icing.put(emailDocument1).getStatus());
+            .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
+            .build();
+
+    assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus());
     DocumentProto emailDocument2 =
         createEmailDocument("namespace", "uri2").toBuilder()
-        .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar"))
-        .build();;
-    assertStatusOk(icing.put(emailDocument2).getStatus());
+            .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar"))
+            .build();
+
+    assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus());
 
     SearchSpecProto searchSpec =
         SearchSpecProto.newBuilder()
@@ -378,7 +365,7 @@
             .build();
 
     SearchResultProto searchResultProto =
-        icing.search(
+        icingSearchEngine.search(
             searchSpec,
             ScoringSpecProto.getDefaultInstance(),
             ResultSpecProto.getDefaultInstance());
@@ -386,45 +373,36 @@
     assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
     assertThat(searchResultProto.getResults(0).getDocument()).isEqualTo(emailDocument1);
 
-    DeleteResultProto deleteResultProto = icing.deleteByQuery(searchSpec);
+    DeleteResultProto deleteResultProto = icingSearchEngine.deleteByQuery(searchSpec);
     assertStatusOk(deleteResultProto.getStatus());
 
-    GetResultProto getResultProto = icing.get("namespace", "uri1");
+    GetResultProto getResultProto = icingSearchEngine.get("namespace", "uri1");
     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
-    getResultProto = icing.get("namespace", "uri2");
+    getResultProto = icingSearchEngine.get("namespace", "uri2");
     assertStatusOk(getResultProto.getStatus());
   }
 
   @Test
   public void testPersistToDisk() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
-    PersistToDiskResultProto persistToDiskResultProto = icing.persistToDisk();
+    PersistToDiskResultProto persistToDiskResultProto = icingSearchEngine.persistToDisk();
     assertStatusOk(persistToDiskResultProto.getStatus());
   }
 
   @Test
   public void testOptimize() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
-    OptimizeResultProto optimizeResultProto = icing.optimize();
+    OptimizeResultProto optimizeResultProto = icingSearchEngine.optimize();
     assertStatusOk(optimizeResultProto.getStatus());
   }
 
   @Test
   public void testGetOptimizeInfo() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
-    GetOptimizeInfoResultProto getOptimizeInfoResultProto = icing.getOptimizeInfo();
+    GetOptimizeInfoResultProto getOptimizeInfoResultProto = icingSearchEngine.getOptimizeInfo();
     assertStatusOk(getOptimizeInfoResultProto.getStatus());
     assertThat(getOptimizeInfoResultProto.getOptimizableDocs()).isEqualTo(0);
     assertThat(getOptimizeInfoResultProto.getEstimatedOptimizableBytes()).isEqualTo(0);
@@ -432,39 +410,63 @@
 
   @Test
   public void testGetAllNamespaces() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
     assertThat(
-        icing
-            .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
-            .getStatus()
-            .getCode())
+            icingSearchEngine
+                .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
+                .getStatus()
+                .getCode())
         .isEqualTo(StatusProto.Code.OK);
 
     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
-    assertStatusOk(icing.put(emailDocument).getStatus());
+    assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
 
-    GetAllNamespacesResultProto getAllNamespacesResultProto = icing.getAllNamespaces();
+    GetAllNamespacesResultProto getAllNamespacesResultProto = icingSearchEngine.getAllNamespaces();
     assertStatusOk(getAllNamespacesResultProto.getStatus());
     assertThat(getAllNamespacesResultProto.getNamespacesList()).containsExactly("namespace");
   }
 
   @Test
   public void testReset() throws Exception {
-    IcingSearchEngineOptions options =
-        IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
-    IcingSearchEngine icing = new IcingSearchEngine(options);
-    assertStatusOk(icing.initialize().getStatus());
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
 
-    ResetResultProto resetResultProto = icing.reset();
+    ResetResultProto resetResultProto = icingSearchEngine.reset();
     assertStatusOk(resetResultProto.getStatus());
   }
 
+  @Test
+  public void testReportUsage() throws Exception {
+    assertStatusOk(icingSearchEngine.initialize().getStatus());
+
+    // Set schema and put a document.
+    SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
+    SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
+    assertThat(
+            icingSearchEngine
+                .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
+                .getStatus()
+                .getCode())
+        .isEqualTo(StatusProto.Code.OK);
+
+    DocumentProto emailDocument = createEmailDocument("namespace", "uri");
+    PutResultProto putResultProto = icingSearchEngine.put(emailDocument);
+    assertStatusOk(putResultProto.getStatus());
+
+    // Report usage
+    UsageReport usageReport =
+        UsageReport.newBuilder()
+            .setDocumentNamespace("namespace")
+            .setDocumentUri("uri")
+            .setUsageTimestampMs(1)
+            .setUsageType(UsageReport.UsageType.USAGE_TYPE1)
+            .build();
+    ReportUsageResultProto reportUsageResultProto = icingSearchEngine.reportUsage(usageReport);
+    assertStatusOk(reportUsageResultProto.getStatus());
+  }
+
   private static void assertStatusOk(StatusProto status) {
     assertWithMessage(status.getMessage()).that(status.getCode()).isEqualTo(StatusProto.Code.OK);
   }