Reducing amount of files created in dropbox for keystore

This change will reduce the number of files written to dropbox for the
purposes of keystore logging. Previously, if a ton of operations were
being done, a file would be created for each operation which led to
spamming of the dropbox directory.

Now, all equivalent operations are counted and only one copy is sent
along with the corresponding count over the time period of an hour. This
limits the number of files keystore can write to dropbox to 24 a day,
and will reduce the possible size of files since redundant operations
aren't being written.

Bug: 117823210
Bug: 110988360
Test: atest cts/tests/tests/keystore/src/android/keystore/cts
Change-Id: I79367aa7a8eb3679aace2058e128d06e513e25ea
diff --git a/keystore/operation.cpp b/keystore/operation.cpp
index 07f9d6c..71ab340 100644
--- a/keystore/operation.cpp
+++ b/keystore/operation.cpp
@@ -63,7 +63,7 @@
     if (entry == mMap.end()) return {};
 
     auto op = entry->second;
-    uploadOpAsProto(*op, wasSuccessful);
+    operationUploader.uploadOpAsProto(*op, wasSuccessful);
     mMap.erase(entry);
 
     auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
diff --git a/keystore/operation.h b/keystore/operation.h
index efe791f..e0865a4 100644
--- a/keystore/operation.h
+++ b/keystore/operation.h
@@ -70,6 +70,7 @@
     std::list<sp<IBinder>> mLru;
     std::map<sp<IBinder>, std::vector<sp<IBinder>>> mAppTokenMap;
     IBinder::DeathRecipient* mDeathRecipient;
+    OperationProtoHandler operationUploader;
 };
 
 }  // namespace keystore
diff --git a/keystore/operation_config.proto b/keystore/operation_config.proto
index 37b4cbb..efbb4fe 100644
--- a/keystore/operation_config.proto
+++ b/keystore/operation_config.proto
@@ -20,6 +20,7 @@
 
 option optimize_for = LITE_RUNTIME;
 
+// A single operation config
 message OperationConfig {
   // What type of encryption algorithm is the key being used in the op for.
   optional string algorithm = 1;
@@ -61,3 +62,16 @@
   // Standalone or is a file system required
   optional string key_blob_usage_reqs = 12;
 }
+
+message OperationConfigEvent {
+  optional OperationConfig op_config = 1;
+
+  // counts corresponds to the number of times each op_config in the above array
+  // was recorded during the collection period.
+  optional uint32 count = 2;
+}
+
+message OperationConfigEvents {
+    repeated OperationConfigEvent op_config_events = 1;
+}
+
diff --git a/keystore/operation_proto_handler.cpp b/keystore/operation_proto_handler.cpp
index 992232d..1833acb 100644
--- a/keystore/operation_proto_handler.cpp
+++ b/keystore/operation_proto_handler.cpp
@@ -25,10 +25,12 @@
 #include <utils/String16.h>
 #include <utils/StrongPointer.h>
 
-#include "operation_config.pb.h"
+using namespace std::chrono;
 
 namespace keystore {
 
+constexpr auto kCollectionTime = 1h;
+
 void determinePurpose(KeyPurpose purpose, OperationConfig* operationConfig) {
     switch (purpose) {
     case KeyPurpose::VERIFY:
@@ -103,19 +105,42 @@
     }
 }
 
-void uploadOpAsProto(Operation& op, bool wasOpSuccessful) {
+void OperationProtoHandler::uploadOpAsProto(Operation& op, bool wasOpSuccessful) {
     OperationConfig operationConfig;
     determinePurpose(op.purpose, &operationConfig);
     checkKeyCharacteristics(op.characteristics.softwareEnforced, &operationConfig);
     checkKeyCharacteristics(op.characteristics.hardwareEnforced, &operationConfig);
     checkOpCharacteristics(op.params, &operationConfig);
-    android::sp<android::os::DropBoxManager> dropbox(new android::os::DropBoxManager);
     operationConfig.set_was_op_successful(wasOpSuccessful);
+    // Only bother with counting an hour out when an operation entry is actually
+    // added
+    if (protoMap.empty()) {
+        start_time = std::chrono::steady_clock::now();
+    }
+    auto cur_time = std::chrono::steady_clock::now();
 
-    size_t size = operationConfig.ByteSize();
-    auto data = std::make_unique<uint8_t[]>(size);
-    operationConfig.SerializeWithCachedSizesToArray(data.get());
-    dropbox->addData(android::String16("keymaster"), data.get(), size, 0);
+    // Add operations to a map within the time duration of an hour. Deduplicate
+    // repeated ops by incrementing the counter of the original one stored and
+    // discarding the new one.
+    protoMap[operationConfig.SerializeAsString()]++;
+
+    if (cur_time - start_time >= kCollectionTime) {
+        // Iterate through the unordered map and dump all the operation protos
+        // accumulated over the hour into the holding list proto after setting
+        // their counts.
+        OperationConfigEvents opConfigEvents;
+        for (auto elem : protoMap) {
+            OperationConfigEvent* event = opConfigEvents.add_op_config_events();
+            event->mutable_op_config()->ParseFromString(elem.first);
+            event->set_count(elem.second);
+        }
+        android::sp<android::os::DropBoxManager> dropbox(new android::os::DropBoxManager);
+        size_t size = opConfigEvents.ByteSize();
+        auto data = std::make_unique<uint8_t[]>(size);
+        opConfigEvents.SerializeWithCachedSizesToArray(data.get());
+        dropbox->addData(android::String16("keymaster"), data.get(), size, 0);
+        protoMap.clear();
+    }
 }
 
 }  // namespace keystore
diff --git a/keystore/operation_proto_handler.h b/keystore/operation_proto_handler.h
index bf461b4..838f3ec 100644
--- a/keystore/operation_proto_handler.h
+++ b/keystore/operation_proto_handler.h
@@ -17,14 +17,25 @@
 #ifndef KEYSTORE_OPERATION_PROTO_HANDLER_H_
 #define KEYSTORE_OPERATION_PROTO_HANDLER_H_
 
+#include "operation_config.pb.h"
 #include "operation_struct.h"
+#include <chrono>
+#include <unordered_map>
+#include <vector>
 
 namespace keystore {
 
 using ::android::IBinder;
 using keymaster::support::Keymaster;
 
-void uploadOpAsProto(Operation& op, bool wasOpSuccessful);
+class OperationProtoHandler {
+  public:
+    void uploadOpAsProto(Operation& op, bool wasOpSuccessful);
+
+  private:
+    std::unordered_map<std::string, int> protoMap;
+    std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now();
+};
 
 }  // namespace keystore