MediaMetrics: Garbage collection fixes

1) Correct TransactionLog low water mark
2) Limit number of items historically stored in any TimeMachine property.

Test: Special instrumented atest
Bug: 150547711
Bug: 150598686
Change-Id: I67b25c84b1fb62ce83caa5ec48b800cab85467c9
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 811e135..d27fa5e 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -113,6 +113,7 @@
     case AID_MEDIA_CODEC:
     case AID_MEDIA_EX:
     case AID_MEDIA_DRM:
+    // case AID_SHELL: // DEBUG ONLY - used for mediametrics_tests to add new keys
     case AID_SYSTEM:
         // trusted source, only override default values
         isTrusted = true;
@@ -145,9 +146,10 @@
         }
     }
 
-    ALOGV("%s: given uid %d; sanitized uid: %d sanitized pkg: %s "
+    ALOGV("%s: isTrusted:%d given uid %d; sanitized uid: %d sanitized pkg: %s "
           "sanitized pkg version: %lld",
           __func__,
+          (int)isTrusted,
           uid_given, item->getUid(),
           item->getPkgName().c_str(),
           (long long)item->getPkgVersionCode());
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
index e048d0e..f0bb2fb 100644
--- a/services/mediametrics/TimeMachine.h
+++ b/services/mediametrics/TimeMachine.h
@@ -124,12 +124,23 @@
                 T&& e, int64_t time = 0) {
             if (time == 0) time = systemTime(SYSTEM_TIME_BOOTTIME);
             mLastModificationTime = time;
+            if (mPropertyMap.size() >= kKeyMaxProperties &&
+                    !mPropertyMap.count(property)) {
+                ALOGV("%s: too many properties, rejecting %s", __func__, property.c_str());
+                return;
+            }
             auto& timeSequence = mPropertyMap[property];
             Elem el{std::forward<T>(e)};
             if (timeSequence.empty()           // no elements
                     || property.back() == AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED
                     || timeSequence.rbegin()->second != el) { // value changed
                 timeSequence.emplace(time, std::move(el));
+
+                if (timeSequence.size() > kTimeSequenceMaxElements) {
+                    ALOGV("%s: restricting maximum elements (discarding oldest) for %s",
+                            __func__, property.c_str());
+                    timeSequence.erase(timeSequence.begin());
+                }
             }
         }
 
@@ -188,6 +199,8 @@
 
     using History = std::map<std::string /* key */, std::shared_ptr<KeyHistory>>;
 
+    static inline constexpr size_t kTimeSequenceMaxElements = 100;
+    static inline constexpr size_t kKeyMaxProperties = 100;
     static inline constexpr size_t kKeyLowWaterMark = 500;
     static inline constexpr size_t kKeyHighWaterMark = 1000;
 
@@ -239,6 +252,9 @@
         const int64_t time = item->getTimestamp();
         const std::string &key = item->getKey();
 
+        ALOGV("%s(%zu, %zu): key: %s  isTrusted:%d  size:%zu",
+                __func__, mKeyLowWaterMark, mKeyHighWaterMark,
+                key.c_str(), (int)isTrusted, item->count());
         std::shared_ptr<KeyHistory> keyHistory;
         {
             std::vector<std::any> garbage;
@@ -446,6 +462,9 @@
     /**
      * Garbage collects if the TimeMachine size exceeds the high water mark.
      *
+     * This GC operation limits the number of keys stored (not the size of properties
+     * stored in each key).
+     *
      * \param garbage a type-erased vector of elements to be destroyed
      *        outside of lock.  Move large items to be destroyed here.
      *
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
index 27e2c14..190a99e 100644
--- a/services/mediametrics/TransactionLog.h
+++ b/services/mediametrics/TransactionLog.h
@@ -264,7 +264,7 @@
         return ret;
     }
 
-    const size_t mLowWaterMark = kLogItemsHighWater;
+    const size_t mLowWaterMark = kLogItemsLowWater;
     const size_t mHighWaterMark = kLogItemsHighWater;
 
     mutable std::mutex mLock;
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index 27b72eb..b8e566e 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -846,3 +846,21 @@
       ASSERT_EQ(ll, (int32_t) countNewlines(s.c_str()));
   }
 }
+
+#if 0
+// Stress test code for garbage collection, you need to enable AID_SHELL as trusted to run
+// in MediaMetricsService.cpp.
+//
+// TODO: Make a dedicated stress test.
+//
+TEST(mediametrics_tests, gc_same_key) {
+  // random keys ignored when empty
+  for (int i = 0; i < 10000000; ++i) {
+      std::unique_ptr<mediametrics::Item> test_key(mediametrics::Item::create("audio.zzz.123"));
+      test_key->set("event#", "hello");
+      test_key->set("value",  (int)10);
+      test_key->selfrecord();
+  }
+  //mediaMetrics->dump(fileno(stdout), {} /* args */);
+}
+#endif