MediaMetricsService: Extract common string utilities to audio_utils

Flag: EXEMPT refactor
Test: atest audio_stringutils_tests
Test: atest mediametrics_tests
Bug: 376579631
Change-Id: Ic28c5f2b6c6cc55579b15300fc5ad80ff40cf673
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index c90488f..d29aa80 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -159,7 +159,7 @@
     },
 
     shared_libs: [
-        "mediametricsservice-aidl-cpp",
+        "libaudioutils",
         "libbase", // android logging
         "libbinder",
         "libcutils",
@@ -174,6 +174,7 @@
         "libstatspull",
         "libstatssocket",
         "libutils",
+        "mediametricsservice-aidl-cpp",
         "packagemanager_aidl-cpp",
     ],
 
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index 201d740..095832c 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -25,6 +25,7 @@
 #include <sstream>
 #include <string>
 #include <audio_utils/clock.h>
+#include <audio_utils/StringUtils.h>
 #include <cutils/properties.h>
 #include <stats_media_metrics.h>
 #include <sys/timerfd.h>
@@ -131,7 +132,7 @@
 
 int32_t AudioPowerUsage::deviceFromStringPairs(const std::string& device_strings) {
     int32_t deviceMask = 0;
-    const auto devaddrvec = stringutils::getDeviceAddressPairs(device_strings);
+    const auto devaddrvec = audio_utils::stringutils::getDeviceAddressPairs(device_strings);
     for (const auto &[device, addr] : devaddrvec) {
         int32_t combo_device = 0;
         deviceFromString(device, combo_device);
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
index 353ae12..0eeff2b 100644
--- a/services/mediametrics/AudioTypes.cpp
+++ b/services/mediametrics/AudioTypes.cpp
@@ -17,6 +17,7 @@
 #include "AudioTypes.h"
 #include "MediaMetricsConstants.h"
 #include "StringUtils.h"
+#include <audio_utils/StringUtils.h>
 #include <media/TypeConverter.h> // requires libmedia_helper to get the Audio code.
 #include <stats_media_metrics.h>            // statsd
 
@@ -349,7 +350,7 @@
 template <typename Traits>
 int32_t int32FromFlags(const std::string &flags)
 {
-    const auto result = stringutils::split(flags, "|");
+    const auto result = audio_utils::stringutils::split(flags, "|");
     int32_t intFlags = 0;
     for (const auto& flag : result) {
         typename Traits::Type value;
@@ -364,7 +365,7 @@
 template <typename Traits>
 std::string stringFromFlags(const std::string &flags, size_t len)
 {
-    const auto result = stringutils::split(flags, "|");
+    const auto result = audio_utils::stringutils::split(flags, "|");
     std::string sFlags;
     for (const auto& flag : result) {
         typename Traits::Type value;
@@ -383,7 +384,7 @@
 {
     if (str.empty()) return {};
 
-    const auto result = stringutils::split(str, "|");
+    const auto result = audio_utils::stringutils::split(str, "|");
     std::stringstream ss;
     for (const auto &s : result) {
         if (map.count(s) > 0) {
@@ -399,7 +400,7 @@
 {
     if (str.empty()) return {};
 
-    const auto result = stringutils::split(str, "|");
+    const auto result = audio_utils::stringutils::split(str, "|");
     typename M::mapped_type value{};
     for (const auto &s : result) {
         auto it = map.find(s);
@@ -416,7 +417,7 @@
 
     if (str.empty()) return v;
 
-    const auto result = stringutils::split(str, "|");
+    const auto result = audio_utils::stringutils::split(str, "|");
     for (const auto &s : result) {
         auto it = map.find(s);
         if (it == map.end()) continue;
@@ -429,7 +430,7 @@
 {
     std::vector<int64_t> v;
 
-    const auto result = stringutils::split(s, "|");
+    const auto result = audio_utils::stringutils::split(s, "|");
     for (const auto &mask : result) {
         // 0 if undetected or if actually 0.
         int64_t int64Mask = strtoll(mask.c_str(), nullptr, 0);
diff --git a/services/mediametrics/StringUtils.cpp b/services/mediametrics/StringUtils.cpp
index 3b2db85..c4111ae 100644
--- a/services/mediametrics/StringUtils.cpp
+++ b/services/mediametrics/StringUtils.cpp
@@ -19,105 +19,12 @@
 #include <utils/Log.h>
 
 #include "StringUtils.h"
-
+#include "AudioTypes.h"
+#include <audio_utils/StringUtils.h>
 #include <charconv>
 
-#include "AudioTypes.h"
-
 namespace android::mediametrics::stringutils {
 
-std::string tokenizer(std::string::const_iterator& it,
-        const std::string::const_iterator& end, const char *reserved)
-{
-    // consume leading white space
-    for (; it != end && std::isspace(*it); ++it);
-    if (it == end) return {};
-
-    auto start = it;
-    // parse until we hit a reserved keyword or space
-    if (strchr(reserved, *it)) return {start, ++it};
-    for (;;) {
-        ++it;
-        if (it == end || std::isspace(*it) || strchr(reserved, *it)) return {start, it};
-    }
-}
-
-std::vector<std::string> split(const std::string& flags, const char *delim)
-{
-    std::vector<std::string> result;
-    for (auto it = flags.begin(); ; ) {
-        auto flag = tokenizer(it, flags.end(), delim);
-        if (flag.empty() || !std::isalnum(flag[0])) return result;
-        result.emplace_back(std::move(flag));
-
-        // look for the delimeter and discard
-        auto token = tokenizer(it, flags.end(), delim);
-        if (token.size() != 1 || strchr(delim, token[0]) == nullptr) return result;
-    }
-}
-
-bool parseVector(const std::string &str, std::vector<int32_t> *vector) {
-    std::vector<int32_t> values;
-    const char *p = str.c_str();
-    const char *last = p + str.size();
-    while (p != last) {
-        if (*p == ',' || *p == '{' || *p == '}') {
-            p++;
-        }
-        int32_t value = -1;
-        auto [ptr, error] = std::from_chars(p, last, value);
-        if (error == std::errc::invalid_argument || error == std::errc::result_out_of_range) {
-            return false;
-        }
-        p = ptr;
-        values.push_back(value);
-    }
-    *vector = std::move(values);
-    return true;
-}
-
-std::vector<std::pair<std::string, std::string>> getDeviceAddressPairs(const std::string& devices)
-{
-    std::vector<std::pair<std::string, std::string>> result;
-
-    // Currently, the device format is
-    //
-    // devices = device_addr OR device_addr|devices
-    // device_addr = device OR (device, addr)
-    //
-    // EXAMPLE:
-    // device1|(device2, addr2)|...
-
-    static constexpr char delim[] = "()|,";
-    for (auto it = devices.begin(); ; ) {
-        std::string address;
-        std::string device = tokenizer(it, devices.end(), delim);
-        if (device.empty()) return result;
-        if (device == "(") {  // it is a pair otherwise we consider it a device
-            device = tokenizer(it, devices.end(), delim); // get actual device
-            auto token = tokenizer(it, devices.end(), delim);
-            if (token != ",") return result;  // malformed, must have a comma
-
-            // special handling here for empty addresses
-            address = tokenizer(it, devices.end(), delim);
-            if (address.empty()) return result;
-            if (address == ")") {  // no address, just the ")"
-                address.clear();
-            } else {
-                token = tokenizer(it, devices.end(), delim);
-                if (token != ")") return result;
-            }
-        }
-        // misaligned token, device must start alphanumeric.
-        if (!std::isalnum(device[0])) return result;
-
-        result.emplace_back(std::move(device), std::move(address));
-
-        auto token = tokenizer(it, devices.end(), delim);
-        if (token != "|") return result;  // this includes end of string detection
-    }
-}
-
 size_t replace(std::string &str, const char *targetChars, const char replaceChar)
 {
     size_t replaced = 0;
@@ -134,7 +41,7 @@
 std::pair<std::string /* external statsd */, std::string /* internal */>
 parseDevicePairs(const std::string& devicePairs) {
     std::pair<std::string, std::string> result{};
-    const auto devaddrvec = stringutils::getDeviceAddressPairs(devicePairs);
+    const auto devaddrvec = audio_utils::stringutils::getDeviceAddressPairs(devicePairs);
     for (const auto& [device, addr] : devaddrvec) { // addr ignored for now.
         if (!result.second.empty()) {
             result.second.append("|"); // delimit devices with '|'.
diff --git a/services/mediametrics/fuzzer/Android.bp b/services/mediametrics/fuzzer/Android.bp
index 99703e3..efea252 100644
--- a/services/mediametrics/fuzzer/Android.bp
+++ b/services/mediametrics/fuzzer/Android.bp
@@ -36,6 +36,7 @@
     ],
 
     shared_libs: [
+        "libaudioutils",
         "libbase",
         "libbinder",
         "libcutils",
diff --git a/services/mediametrics/include/mediametricsservice/StringUtils.h b/services/mediametrics/include/mediametricsservice/StringUtils.h
index ed2cf2e..3e1cafc 100644
--- a/services/mediametrics/include/mediametricsservice/StringUtils.h
+++ b/services/mediametrics/include/mediametricsservice/StringUtils.h
@@ -61,30 +61,6 @@
 }
 
 /**
- * Return string tokens from iterator, separated by spaces and reserved chars.
- */
-std::string tokenizer(std::string::const_iterator& it,
-        const std::string::const_iterator& end, const char *reserved);
-
-/**
- * Splits flags string based on delimeters (or, whitespace which is removed).
- */
-std::vector<std::string> split(const std::string& flags, const char *delim);
-
-/**
- * Parses a vector of integers using ',' '{' and '}' as delimeters. Leaves
- * vector unmodified if the parsing fails.
- */
-bool parseVector(const std::string &str, std::vector<int32_t> *vector);
-
-/**
- * Parse the devices string and return a vector of device address pairs.
- *
- * A failure to parse returns early with the contents that were able to be parsed.
- */
-std::vector<std::pair<std::string, std::string>> getDeviceAddressPairs(const std::string &devices);
-
-/**
  * Replaces targetChars with replaceChar in string, returns number of chars replaced.
  */
 size_t replace(std::string &str, const char *targetChars, const char replaceChar);
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index 844f204..2f7c4f9 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -33,6 +33,7 @@
 #include <stats_media_metrics.h>
 #include <stats_event.h>
 
+#include <audio_utils/StringUtils.h>
 #include <frameworks/proto_logging/stats/message/mediametrics_message.pb.h>
 #include <mediametricsservice/cleaner.h>
 #include <mediametricsservice/iface_statsd.h>
@@ -171,7 +172,7 @@
 }
 
 static void parseVector(const std::string &str, std::vector<int32_t> *vector) {
-    if (!mediametrics::stringutils::parseVector(str, vector)) {
+    if (!audio_utils::stringutils::parseVector(str, vector)) {
         ALOGE("failed to parse integer vector from '%s'", str.c_str());
     }
 }
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index f3933a7..383ed6a 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -22,6 +22,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include <audio_utils/StringUtils.h>
 #include <gtest/gtest.h>
 #include <media/MediaMetricsItem.h>
 #include <mediametricsservice/AudioTypes.h>
@@ -31,7 +32,7 @@
 #include <system/audio.h>
 
 using namespace android;
-using android::mediametrics::stringutils::parseVector;
+using android::audio_utils::stringutils::parseVector;
 
 static size_t countNewlines(const char *s) {
     size_t count = 0;
@@ -59,35 +60,6 @@
   ASSERT_EQ(false, android::mediametrics::startsWith(s, std::string("est")));
 }
 
-TEST(mediametrics_tests, parseVector) {
-    {
-        std::vector<int32_t> values;
-        EXPECT_EQ(true, parseVector("0{4,300,0,-112343,350}9", &values));
-        EXPECT_EQ(values, std::vector<int32_t>({0, 4, 300, 0, -112343, 350, 9}));
-    }
-    {
-        std::vector<int32_t> values;
-        EXPECT_EQ(true, parseVector("53", &values));
-        EXPECT_EQ(values, std::vector<int32_t>({53}));
-    }
-    {
-        std::vector<int32_t> values;
-        EXPECT_EQ(false, parseVector("5{3,6*3}3", &values));
-        EXPECT_EQ(values, std::vector<int32_t>({}));
-    }
-    {
-        std::vector<int32_t> values = {1}; // should still be this when parsing fails
-        std::vector<int32_t> expected = {1};
-        EXPECT_EQ(false, parseVector("51342abcd,1232", &values));
-        EXPECT_EQ(values, std::vector<int32_t>({1}));
-    }
-    {
-        std::vector<int32_t> values = {2}; // should still be this when parsing fails
-        EXPECT_EQ(false, parseVector("12345678901234,12345678901234", &values));
-        EXPECT_EQ(values, std::vector<int32_t>({2}));
-    }
-}
-
 TEST(mediametrics_tests, defer) {
   bool check = false;
   {
@@ -934,62 +906,6 @@
   }
 }
 
-TEST(mediametrics_tests, device_parsing) {
-    auto devaddr = android::mediametrics::stringutils::getDeviceAddressPairs("(DEVICE, )");
-    ASSERT_EQ((size_t)1, devaddr.size());
-    ASSERT_EQ("DEVICE", devaddr[0].first);
-    ASSERT_EQ("", devaddr[0].second);
-
-    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
-            "(DEVICE1, A)|(D, ADDRB)");
-    ASSERT_EQ((size_t)2, devaddr.size());
-    ASSERT_EQ("DEVICE1", devaddr[0].first);
-    ASSERT_EQ("A", devaddr[0].second);
-    ASSERT_EQ("D", devaddr[1].first);
-    ASSERT_EQ("ADDRB", devaddr[1].second);
-
-    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
-            "(A,B)|(C,D)");
-    ASSERT_EQ((size_t)2, devaddr.size());
-    ASSERT_EQ("A", devaddr[0].first);
-    ASSERT_EQ("B", devaddr[0].second);
-    ASSERT_EQ("C", devaddr[1].first);
-    ASSERT_EQ("D", devaddr[1].second);
-
-    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
-            "  ( A1 , B )  | ( C , D2 )  ");
-    ASSERT_EQ((size_t)2, devaddr.size());
-    ASSERT_EQ("A1", devaddr[0].first);
-    ASSERT_EQ("B", devaddr[0].second);
-    ASSERT_EQ("C", devaddr[1].first);
-    ASSERT_EQ("D2", devaddr[1].second);
-
-    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
-            " Z  ");
-    ASSERT_EQ((size_t)1, devaddr.size());
-    ASSERT_EQ("Z", devaddr[0].first);
-
-    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
-            "  A | B|C  ");
-    ASSERT_EQ((size_t)3, devaddr.size());
-    ASSERT_EQ("A", devaddr[0].first);
-    ASSERT_EQ("", devaddr[0].second);
-    ASSERT_EQ("B", devaddr[1].first);
-    ASSERT_EQ("", devaddr[1].second);
-    ASSERT_EQ("C", devaddr[2].first);
-    ASSERT_EQ("", devaddr[2].second);
-
-    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
-            "  A | (B1, 10) |C  ");
-    ASSERT_EQ((size_t)3, devaddr.size());
-    ASSERT_EQ("A", devaddr[0].first);
-    ASSERT_EQ("", devaddr[0].second);
-    ASSERT_EQ("B1", devaddr[1].first);
-    ASSERT_EQ("10", devaddr[1].second);
-    ASSERT_EQ("C", devaddr[2].first);
-    ASSERT_EQ("", devaddr[2].second);
-}
-
 TEST(mediametrics_tests, timed_action) {
     android::mediametrics::TimedAction timedAction;
     std::atomic_int value1 = 0;