[Sync] Implement garbage collection by age for directory.

For age out, we only do it once a day, because garbage collection will
scan all the directory entries for that datatype, and for heavy user,
this will be expensive.

BUG=347253, 702020

Review-Url: https://codereview.chromium.org/2966623002
Cr-Commit-Position: refs/heads/master@{#484370}
diff --git a/components/sync/engine_impl/process_updates_util.cc b/components/sync/engine_impl/process_updates_util.cc
index 0592b52..fbbdd22 100644
--- a/components/sync/engine_impl/process_updates_util.cc
+++ b/components/sync/engine_impl/process_updates_util.cc
@@ -331,4 +331,30 @@
   }
 }
 
+void ExpireEntriesByAge(syncable::Directory* dir,
+                        syncable::ModelNeutralWriteTransaction* trans,
+                        ModelType type,
+                        int32_t age_watermark_in_days) {
+  syncable::Directory::Metahandles handles;
+  base::Time to_be_expired =
+      base::Time::Now() - base::TimeDelta::FromDays(age_watermark_in_days);
+  dir->GetMetaHandlesOfType(trans, type, &handles);
+  for (size_t i = 0; i < handles.size(); ++i) {
+    syncable::ModelNeutralMutableEntry entry(trans, syncable::GET_BY_HANDLE,
+                                             handles[i]);
+    if (!entry.good() || !entry.GetId().ServerKnows() ||
+        entry.GetUniqueServerTag() == ModelTypeToRootTag(type) ||
+        entry.GetIsUnappliedUpdate() || entry.GetIsUnsynced() ||
+        entry.GetIsDel() || entry.GetServerIsDel() ||
+        entry.GetMtime() > to_be_expired) {
+      continue;
+    }
+
+    // Mark entry as unapplied update first to ensure journaling the deletion.
+    entry.PutIsUnappliedUpdate(true);
+    // Mark entry as deleted by server.
+    entry.PutServerIsDel(true);
+  }
+}
+
 }  // namespace syncer