MediaMetrics: Add unit tests

Document some interface differences.

Test: mediametrics_tests
Change-Id: Idf19827e79136dcca6522e6df53e28e3ea4a2231
diff --git a/media/libmediametrics/include/IMediaAnalyticsService.h b/media/libmediametrics/include/IMediaAnalyticsService.h
index f635e94..f2e7710 100644
--- a/media/libmediametrics/include/IMediaAnalyticsService.h
+++ b/media/libmediametrics/include/IMediaAnalyticsService.h
@@ -39,19 +39,25 @@
 public:
     DECLARE_META_INTERFACE(MediaAnalyticsService);
 
-    // generate a unique sessionID to use across multiple requests
-    // 'unique' is within this device, since last reboot
+    /**
+     * Returns a unique sessionID to use across multiple requests;
+     * 'unique' is within this device, since last reboot.
+     */
     virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() = 0;
 
-    // submit the indicated record to the mediaanalytics service, where
-    // it will be merged (if appropriate) with incomplete records that
-    // share the same key and sessionid.
-    // 'forcenew' marks any matching incomplete record as complete before
-    // inserting this new record.
-    // returns the sessionID associated with that item.
-    // caller continues to own the passed item
+    /**
+     * Submits the indicated record to the mediaanalytics service, where
+     * it will be merged (if appropriate) with incomplete records that
+     * share the same key and sessionID.
+     *
+     * \param item the item to submit.
+     * \param forcenew marks any matching incomplete record as complete before
+     *                 inserting this new record.
+     *
+     * \return the sessionID associated with that item or
+     *         MediaAnalyticsItem::SessionIDInvalid on failure.
+     */
     virtual MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew) = 0;
-
 };
 
 // ----------------------------------------------------------------------------
@@ -59,10 +65,10 @@
 class BnMediaAnalyticsService: public BnInterface<IMediaAnalyticsService>
 {
 public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
+    status_t onTransact(uint32_t code,
+                        const Parcel& data,
+                        Parcel* reply,
+                        uint32_t flags = 0) override;
 };
 
 }; // namespace android
diff --git a/services/mediaanalytics/Android.bp b/services/mediaanalytics/Android.bp
index 80816d7..dc72064 100644
--- a/services/mediaanalytics/Android.bp
+++ b/services/mediaanalytics/Android.bp
@@ -5,8 +5,32 @@
     name: "mediametrics",
 
     srcs: [
-        "iface_statsd.cpp",
         "main_mediametrics.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libmediaanalyticsservice",
+        "libutils",
+    ],
+
+    init_rc: [
+        "mediametrics.rc",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
+
+cc_library_shared {
+    name: "libmediaanalyticsservice",
+
+    srcs: [
+        "iface_statsd.cpp",
         "MediaAnalyticsService.cpp",
         "statsd_audiopolicy.cpp",
         "statsd_audiorecord.cpp",
@@ -37,16 +61,12 @@
     ],
 
     include_dirs: [
-        "system/media/audio_utils/include"
+        "system/media/audio_utils/include",
     ],
 
-    init_rc: ["mediametrics.rc"],
-
     cflags: [
         "-Wall",
         "-Werror",
         "-Wextra",
     ],
-    clang: true,
-
 }
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 1101a8c..ed7b7b1 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -34,8 +34,24 @@
     MediaAnalyticsService();
     ~MediaAnalyticsService() override;
 
-    // caller surrenders ownership of item, MediaAnalyticsService will delete.
-    int64_t submit(MediaAnalyticsItem *item, bool forcenew) override;
+    /**
+     * Submits the indicated record to the mediaanalytics service, where
+     * it will be merged (if appropriate) with incomplete records that
+     * share the same key and sessionid.
+     *
+     * \param item the item to submit.
+     * \param forcenew marks any matching incomplete record as complete before
+     *                 inserting this new record.
+     *
+     * \return the sessionID associated with that item or
+     *         MediaAnalyticsItem::SessionIDInvalid on failure.
+     *
+     * BEWARE: When called directly on the service (not from the binder interface),
+     * the caller surrenders ownership of item, MediaAnalyticsService will delete
+     * even on error.  The binder interface does not take ownership.
+     * TODO: fix this inconsistency with the binder RPC interface.
+     */
+    MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew) override;
 
     status_t dump(int fd, const Vector<String16>& args) override;
 
diff --git a/services/mediaanalytics/tests/Android.bp b/services/mediaanalytics/tests/Android.bp
new file mode 100644
index 0000000..7bca1c4
--- /dev/null
+++ b/services/mediaanalytics/tests/Android.bp
@@ -0,0 +1,25 @@
+cc_test {
+    name: "mediametrics_tests",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+
+    include_dirs: [
+        "frameworks/av/services/mediaanalytics",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libmediaanalyticsservice",
+        "libmediametrics",
+        "libutils",
+    ],
+
+    srcs: [
+        "mediametrics_tests.cpp",
+    ],
+}
diff --git a/services/mediaanalytics/tests/build_and_run_all_unit_tests.sh b/services/mediaanalytics/tests/build_and_run_all_unit_tests.sh
new file mode 100755
index 0000000..2511c30
--- /dev/null
+++ b/services/mediaanalytics/tests/build_and_run_all_unit_tests.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Run tests in this directory.
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+    echo "Android build environment not set"
+    exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount
+
+echo "========================================"
+
+echo "testing mediametrics"
+adb push $OUT/data/nativetest/mediametrics_tests/mediametrics_tests /system/bin
+adb shell /system/bin/mediametrics_tests
diff --git a/services/mediaanalytics/tests/mediametrics_tests.cpp b/services/mediaanalytics/tests/mediametrics_tests.cpp
new file mode 100644
index 0000000..794b2f0
--- /dev/null
+++ b/services/mediaanalytics/tests/mediametrics_tests.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "mediametrics_tests"
+#include <utils/Log.h>
+
+#include "MediaAnalyticsService.h"
+
+#include <stdio.h>
+
+#include <gtest/gtest.h>
+#include <media/MediaAnalyticsItem.h>
+
+using namespace android;
+
+TEST(mediametrics_tests, instantiate) {
+  sp mediaMetrics = new MediaAnalyticsService();
+  status_t status;
+
+  // NOTE: submission of items to MediaMetrics releases ownership, even on error.
+
+  // random keys ignored when empty
+  status = mediaMetrics->submit(MediaAnalyticsItem::create("random_key"), false);
+  ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
+
+  // random keys ignored with data
+  auto random_key = MediaAnalyticsItem::create("random_key");
+  random_key->setInt32("foo", 10);
+  status = mediaMetrics->submit(random_key, false);
+  ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
+
+  // known keys ignored if empty
+  status = mediaMetrics->submit(MediaAnalyticsItem::create("audiotrack"), false);
+  ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
+
+  auto audiotrack = MediaAnalyticsItem::create("audiotrack");
+  audiotrack->addInt32("foo", 10);
+  status = mediaMetrics->submit(audiotrack, false);
+  ASSERT_GT(status, MediaAnalyticsItem::SessionIDNone);
+
+  mediaMetrics->dump(fileno(stdout), {} /* args */);
+}