MediaMetrics: Add AnalyticsActions and AnalyticsState
Factor out common analytics code from audio specifics.
Allow for saving and clearing analytics state if audioserver crashes.
Test: atest mediametrics_tests
Test: instrumented check on audioserver restart
Bug: 138583596
Change-Id: I1073f3ef95f44a383a7f14b0c2ea6f978a84ee24
diff --git a/services/mediametrics/AnalyticsState.h b/services/mediametrics/AnalyticsState.h
new file mode 100644
index 0000000..4711bb6
--- /dev/null
+++ b/services/mediametrics/AnalyticsState.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "TimeMachine.h"
+#include "TransactionLog.h"
+
+namespace android::mediametrics {
+
+/**
+ * AnalyticsState consists of a TimeMachine and TransactionLog for a set
+ * of MediaMetrics Items.
+ *
+ * One can add new Items with the submit() method.
+ *
+ * The AnalyticsState may be cleared or duplicated to preserve state after crashes
+ * in services are detected.
+ *
+ * As its members may not be moveable due to mutexes, we use this encapsulation
+ * with a shared pointer in order to save it or duplicate it.
+ */
+class AnalyticsState {
+public:
+ /**
+ * Returns success if AnalyticsState accepts the item.
+ *
+ * A trusted source can create a new key, an untrusted source
+ * can only modify the key if the uid will match that authorized
+ * on the existing key.
+ *
+ * \param item the item to be submitted.
+ * \param isTrusted whether the transaction comes from a trusted source.
+ * In this case, a trusted source is verified by binder
+ * UID to be a system service by MediaMetrics service.
+ * Do not use true if you haven't really checked!
+ *
+ * \return NO_ERROR on success or
+ * PERMISSION_DENIED if the item cannot be put into the AnalyticsState.
+ */
+ status_t submit(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted) {
+ return mTimeMachine.put(item, isTrusted) ?: mTransactionLog.put(item);
+ }
+
+ /**
+ * Returns a pair consisting of the dump string, and the number of lines in the string.
+ *
+ * The number of lines in the returned pair is used as an optimization
+ * for subsequent line limiting.
+ *
+ * The TimeMachine and the TransactionLog are dumped separately under
+ * different locks, so may not be 100% consistent with the last data
+ * delivered.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ */
+ std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const {
+ std::stringstream ss;
+ int32_t ll = lines;
+
+ if (ll > 0) {
+ ss << "TransactionLog:\n";
+ --ll;
+ }
+ if (ll > 0) {
+ auto [s, l] = mTransactionLog.dump(ll);
+ ss << s;
+ ll -= l;
+ }
+ if (ll > 0) {
+ ss << "TimeMachine:\n";
+ --ll;
+ }
+ if (ll > 0) {
+ auto [s, l] = mTimeMachine.dump(ll);
+ ss << s;
+ ll -= l;
+ }
+ return { ss.str(), lines - ll };
+ }
+
+ /**
+ * Clears the AnalyticsState.
+ */
+ void clear() {
+ mTimeMachine.clear();
+ mTransactionLog.clear();
+ }
+
+private:
+ // Note: TimeMachine and TransactionLog are individually locked.
+ // Access to these objects under multiple threads will be weakly synchronized,
+ // which is acceptable as modifications only increase the history (or with GC,
+ // eliminates very old history).
+
+ TimeMachine mTimeMachine;
+ TransactionLog mTransactionLog;
+};
+
+} // namespace android::mediametrics