Loading services/mediametrics/AudioAnalytics.cpp +20 −1 Original line number Diff line number Diff line Loading @@ -87,6 +87,7 @@ static constexpr const char * const AudioRecordDeviceUsageFields[] = { "selected_device_id", "caller", "source", "log_session_id", }; static constexpr const char * const AudioThreadDeviceUsageFields[] = { Loading Loading @@ -124,6 +125,7 @@ static constexpr const char * const AudioTrackDeviceUsageFields[] = { "content_type", "caller", "traits", "log_session_id", }; static constexpr const char * const AudioDeviceConnectionFields[] = { Loading Loading @@ -521,12 +523,18 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( std::string source; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_SOURCE, &source); // Android S std::string logSessionId; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId); const auto callerNameForStats = types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName); const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding); const auto flagsForStats = types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags); const auto sourceForStats = types::lookup<types::SOURCE_TYPE, short_enum_type_t>(source); // Android S const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId); LOG(LOG_LEVEL) << "key:" << key << " id:" << id Loading @@ -541,7 +549,9 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( << ") packageName:" << packageName << " selectedDeviceId:" << selectedDeviceId << " callerName:" << callerName << "(" << callerNameForStats << ") source:" << source << "(" << sourceForStats << ")"; << ") source:" << source << "(" << sourceForStats << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats << ")"; if (clientCalled // only log if client app called AudioRecord. && mAudioAnalytics.mDeliverStatistics) { const auto [ result, str ] = sendToStatsd(AudioRecordDeviceUsageFields, Loading @@ -559,6 +569,7 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( , selectedDeviceId , ENUM_EXTRACT(callerNameForStats) , ENUM_EXTRACT(sourceForStats) , logSessionIdForStats.c_str() ); ALOGV("%s: statsd %s", __func__, str.c_str()); mAudioAnalytics.mStatsdLog.log("%s", str.c_str()); Loading Loading @@ -659,6 +670,10 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( std::string usage; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_USAGE, &usage); // Android S std::string logSessionId; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId); const auto callerNameForStats = types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName); Loading @@ -671,6 +686,8 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( const auto traitsForStats = types::lookup<types::TRACK_TRAITS, short_enum_type_t>(traits); const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage); // Android S const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId); LOG(LOG_LEVEL) << "key:" << key << " id:" << id Loading @@ -695,6 +712,7 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( << " streamType:" << streamType << "(" << streamTypeForStats << ") traits:" << traits << "(" << traitsForStats << ") usage:" << usage << "(" << usageForStats << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats << ")"; if (clientCalled // only log if client app called AudioTracks && mAudioAnalytics.mDeliverStatistics) { Loading @@ -719,6 +737,7 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( , ENUM_EXTRACT(contentTypeForStats) , ENUM_EXTRACT(callerNameForStats) , ENUM_EXTRACT(traitsForStats) , logSessionIdForStats.c_str() ); ALOGV("%s: statsd %s", __func__, str.c_str()); mAudioAnalytics.mStatsdLog.log("%s", str.c_str()); Loading services/mediametrics/StringUtils.h +78 −0 Original line number Diff line number Diff line Loading @@ -68,4 +68,82 @@ std::vector<std::pair<std::string, std::string>> getDeviceAddressPairs(const std */ size_t replace(std::string &str, const char *targetChars, const char replaceChar); // RFC 1421, 2045, 2152, 4648(4), 4880 inline constexpr char Base64Table[] = // 0000000000111111111122222222223333333333444444444455555555556666 // 0123456789012345678901234567890123456789012345678901234567890123 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // RFC 4648(5) URL-safe Base64 encoding inline constexpr char Base64UrlTable[] = // 0000000000111111111122222222223333333333444444444455555555556666 // 0123456789012345678901234567890123456789012345678901234567890123 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; // An constexpr struct that transposes/inverts a string conversion table. struct Transpose { // constexpr bug, returning char still means -1 == 0xff, so we use unsigned char. using base_char_t = unsigned char; static inline constexpr base_char_t INVALID_CHAR = 0xff; template <size_t N> explicit constexpr Transpose(const char(&string)[N]) { for (auto& e : mMap) { e = INVALID_CHAR; } for (size_t i = 0; string[i] != 0; ++i) { mMap[static_cast<size_t>(string[i]) & 0xff] = i; } } constexpr base_char_t operator[] (size_t n) const { return n < sizeof(mMap) ? mMap[n] : INVALID_CHAR; } constexpr const auto& get() const { return mMap; } private: base_char_t mMap[256]; // construct an inverse character mapping. }; // This table is used to convert an input char to a 6 bit (0 - 63) value. // If the input char is not in the Base64Url charset, Transpose::INVALID_CHAR is returned. inline constexpr Transpose InverseBase64UrlTable(Base64UrlTable); // Returns true if s consists of only valid Base64Url characters (no padding chars allowed). inline constexpr bool isBase64Url(const char *s) { for (; *s != 0; ++s) { if (InverseBase64UrlTable[(unsigned char)*s] == Transpose::INVALID_CHAR) return false; } return true; } // Returns true if s is a valid log session id: exactly 16 Base64Url characters. // // logSessionIds are a web-safe Base64Url RFC 4648(5) encoded string of 16 characters // (representing 96 unique bits 16 * 6). // // The string version is considered the reference representation. However, for ease of // manipulation and comparison, it may be converted to an int128. // // For int128 conversion, some common interpretations exist - for example // (1) the 16 Base64 chars can be converted 6 bits per char to a 96 bit value // (with the most significant 32 bits as zero) as there are only 12 unique bytes worth of data // or (2) the 16 Base64 chars can be used to directly fill the 128 bits of int128 assuming // the 16 chars are 16 bytes, filling the layout of the int128 variable. // Endianness of the data may follow whatever is convenient in the interpretation as long // as it is applied to each such conversion of string to int128 identically. // inline constexpr bool isLogSessionId(const char *s) { return std::char_traits<std::decay_t<decltype(*s)>>::length(s) == 16 && isBase64Url(s); } // Returns either the original string or an empty string if isLogSessionId check fails. inline std::string sanitizeLogSessionId(const std::string& string) { if (isLogSessionId(string.c_str())) return string; return {}; // if not a logSessionId, return an empty string. } } // namespace android::mediametrics::stringutils services/mediametrics/statsd_audiorecord.cpp +10 −2 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <statslog.h> #include "MediaMetricsService.h" #include "StringUtils.h" #include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h" #include "iface_statsd.h" Loading Loading @@ -134,19 +135,26 @@ bool statsd_audiorecord(const mediametrics::Item *item) metrics_proto.set_start_count(startcount); } std::string serialized; if (!metrics_proto.SerializeToString(&serialized)) { ALOGE("Failed to serialize audiorecord metrics"); return false; } // Android S // log_session_id (string) std::string logSessionId; (void)item->getString("android.media.audiorecord.logSessionId", &logSessionId); const auto logSessionIdForStats = mediametrics::stringutils::sanitizeLogSessionId(logSessionId); if (enabled_statsd) { android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED, timestamp, pkgName.c_str(), pkgVersionCode, mediaApexVersion, bf_serialized); bf_serialized, logSessionIdForStats.c_str()); } else { ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); Loading services/mediametrics/statsd_audiotrack.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <statslog.h> #include "MediaMetricsService.h" #include "StringUtils.h" #include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h" #include "iface_statsd.h" Loading Loading @@ -132,12 +133,20 @@ bool statsd_audiotrack(const mediametrics::Item *item) return false; } // Android S // log_session_id (string) std::string logSessionId; (void)item->getString("android.media.audiotrack.logSessionId", &logSessionId); const auto logSessionIdForStats = mediametrics::stringutils::sanitizeLogSessionId(logSessionId); if (enabled_statsd) { android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED, timestamp, pkgName.c_str(), pkgVersionCode, mediaApexVersion, bf_serialized); bf_serialized, logSessionIdForStats.c_str()); } else { ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); Loading services/mediametrics/tests/mediametrics_tests.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -1082,3 +1082,42 @@ TEST(mediametrics_tests, gc_same_key) { //mediaMetrics->dump(fileno(stdout), {} /* args */); } #endif // Base64Url and isLogSessionId string utilities can be tested by static asserts. static_assert(mediametrics::stringutils::isBase64Url("abc")); static_assert(mediametrics::stringutils::InverseBase64UrlTable['A'] == 0); static_assert(mediametrics::stringutils::InverseBase64UrlTable['a'] == 26); static_assert(mediametrics::stringutils::InverseBase64UrlTable['!'] == mediametrics::stringutils::Transpose::INVALID_CHAR); static_assert(mediametrics::stringutils::InverseBase64UrlTable['@'] == mediametrics::stringutils::Transpose::INVALID_CHAR); static_assert(mediametrics::stringutils::InverseBase64UrlTable['#'] == mediametrics::stringutils::Transpose::INVALID_CHAR); static_assert(!mediametrics::stringutils::isBase64Url("!@#")); static_assert(mediametrics::stringutils::isLogSessionId("0123456789abcdef")); static_assert(!mediametrics::stringutils::isLogSessionId("abc")); static_assert(!mediametrics::stringutils::isLogSessionId("!@#")); static_assert(!mediametrics::stringutils::isLogSessionId("0123456789abcde!")); TEST(mediametrics_tests, sanitizeLogSessionId) { // invalid id returns empty string. ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId("abc")); // valid id passes through. std::string validId = "fedcba9876543210"; ASSERT_EQ(validId, mediametrics::stringutils::sanitizeLogSessionId(validId)); // one more char makes the id invalid ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId + "A")); std::string validId2 = "ZYXWVUT123456789"; ASSERT_EQ(validId2, mediametrics::stringutils::sanitizeLogSessionId(validId2)); // one fewer char makes the id invalid ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId.c_str() + 1)); // replacing one character with an invalid character makes an invalid id. validId2[3] = '!'; ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId2)); } Loading
services/mediametrics/AudioAnalytics.cpp +20 −1 Original line number Diff line number Diff line Loading @@ -87,6 +87,7 @@ static constexpr const char * const AudioRecordDeviceUsageFields[] = { "selected_device_id", "caller", "source", "log_session_id", }; static constexpr const char * const AudioThreadDeviceUsageFields[] = { Loading Loading @@ -124,6 +125,7 @@ static constexpr const char * const AudioTrackDeviceUsageFields[] = { "content_type", "caller", "traits", "log_session_id", }; static constexpr const char * const AudioDeviceConnectionFields[] = { Loading Loading @@ -521,12 +523,18 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( std::string source; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_SOURCE, &source); // Android S std::string logSessionId; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId); const auto callerNameForStats = types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName); const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding); const auto flagsForStats = types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags); const auto sourceForStats = types::lookup<types::SOURCE_TYPE, short_enum_type_t>(source); // Android S const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId); LOG(LOG_LEVEL) << "key:" << key << " id:" << id Loading @@ -541,7 +549,9 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( << ") packageName:" << packageName << " selectedDeviceId:" << selectedDeviceId << " callerName:" << callerName << "(" << callerNameForStats << ") source:" << source << "(" << sourceForStats << ")"; << ") source:" << source << "(" << sourceForStats << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats << ")"; if (clientCalled // only log if client app called AudioRecord. && mAudioAnalytics.mDeliverStatistics) { const auto [ result, str ] = sendToStatsd(AudioRecordDeviceUsageFields, Loading @@ -559,6 +569,7 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( , selectedDeviceId , ENUM_EXTRACT(callerNameForStats) , ENUM_EXTRACT(sourceForStats) , logSessionIdForStats.c_str() ); ALOGV("%s: statsd %s", __func__, str.c_str()); mAudioAnalytics.mStatsdLog.log("%s", str.c_str()); Loading Loading @@ -659,6 +670,10 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( std::string usage; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_USAGE, &usage); // Android S std::string logSessionId; mAudioAnalytics.mAnalyticsState->timeMachine().get( key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId); const auto callerNameForStats = types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName); Loading @@ -671,6 +686,8 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( const auto traitsForStats = types::lookup<types::TRACK_TRAITS, short_enum_type_t>(traits); const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage); // Android S const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId); LOG(LOG_LEVEL) << "key:" << key << " id:" << id Loading @@ -695,6 +712,7 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( << " streamType:" << streamType << "(" << streamTypeForStats << ") traits:" << traits << "(" << traitsForStats << ") usage:" << usage << "(" << usageForStats << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats << ")"; if (clientCalled // only log if client app called AudioTracks && mAudioAnalytics.mDeliverStatistics) { Loading @@ -719,6 +737,7 @@ void AudioAnalytics::DeviceUse::endAudioIntervalGroup( , ENUM_EXTRACT(contentTypeForStats) , ENUM_EXTRACT(callerNameForStats) , ENUM_EXTRACT(traitsForStats) , logSessionIdForStats.c_str() ); ALOGV("%s: statsd %s", __func__, str.c_str()); mAudioAnalytics.mStatsdLog.log("%s", str.c_str()); Loading
services/mediametrics/StringUtils.h +78 −0 Original line number Diff line number Diff line Loading @@ -68,4 +68,82 @@ std::vector<std::pair<std::string, std::string>> getDeviceAddressPairs(const std */ size_t replace(std::string &str, const char *targetChars, const char replaceChar); // RFC 1421, 2045, 2152, 4648(4), 4880 inline constexpr char Base64Table[] = // 0000000000111111111122222222223333333333444444444455555555556666 // 0123456789012345678901234567890123456789012345678901234567890123 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // RFC 4648(5) URL-safe Base64 encoding inline constexpr char Base64UrlTable[] = // 0000000000111111111122222222223333333333444444444455555555556666 // 0123456789012345678901234567890123456789012345678901234567890123 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; // An constexpr struct that transposes/inverts a string conversion table. struct Transpose { // constexpr bug, returning char still means -1 == 0xff, so we use unsigned char. using base_char_t = unsigned char; static inline constexpr base_char_t INVALID_CHAR = 0xff; template <size_t N> explicit constexpr Transpose(const char(&string)[N]) { for (auto& e : mMap) { e = INVALID_CHAR; } for (size_t i = 0; string[i] != 0; ++i) { mMap[static_cast<size_t>(string[i]) & 0xff] = i; } } constexpr base_char_t operator[] (size_t n) const { return n < sizeof(mMap) ? mMap[n] : INVALID_CHAR; } constexpr const auto& get() const { return mMap; } private: base_char_t mMap[256]; // construct an inverse character mapping. }; // This table is used to convert an input char to a 6 bit (0 - 63) value. // If the input char is not in the Base64Url charset, Transpose::INVALID_CHAR is returned. inline constexpr Transpose InverseBase64UrlTable(Base64UrlTable); // Returns true if s consists of only valid Base64Url characters (no padding chars allowed). inline constexpr bool isBase64Url(const char *s) { for (; *s != 0; ++s) { if (InverseBase64UrlTable[(unsigned char)*s] == Transpose::INVALID_CHAR) return false; } return true; } // Returns true if s is a valid log session id: exactly 16 Base64Url characters. // // logSessionIds are a web-safe Base64Url RFC 4648(5) encoded string of 16 characters // (representing 96 unique bits 16 * 6). // // The string version is considered the reference representation. However, for ease of // manipulation and comparison, it may be converted to an int128. // // For int128 conversion, some common interpretations exist - for example // (1) the 16 Base64 chars can be converted 6 bits per char to a 96 bit value // (with the most significant 32 bits as zero) as there are only 12 unique bytes worth of data // or (2) the 16 Base64 chars can be used to directly fill the 128 bits of int128 assuming // the 16 chars are 16 bytes, filling the layout of the int128 variable. // Endianness of the data may follow whatever is convenient in the interpretation as long // as it is applied to each such conversion of string to int128 identically. // inline constexpr bool isLogSessionId(const char *s) { return std::char_traits<std::decay_t<decltype(*s)>>::length(s) == 16 && isBase64Url(s); } // Returns either the original string or an empty string if isLogSessionId check fails. inline std::string sanitizeLogSessionId(const std::string& string) { if (isLogSessionId(string.c_str())) return string; return {}; // if not a logSessionId, return an empty string. } } // namespace android::mediametrics::stringutils
services/mediametrics/statsd_audiorecord.cpp +10 −2 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <statslog.h> #include "MediaMetricsService.h" #include "StringUtils.h" #include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h" #include "iface_statsd.h" Loading Loading @@ -134,19 +135,26 @@ bool statsd_audiorecord(const mediametrics::Item *item) metrics_proto.set_start_count(startcount); } std::string serialized; if (!metrics_proto.SerializeToString(&serialized)) { ALOGE("Failed to serialize audiorecord metrics"); return false; } // Android S // log_session_id (string) std::string logSessionId; (void)item->getString("android.media.audiorecord.logSessionId", &logSessionId); const auto logSessionIdForStats = mediametrics::stringutils::sanitizeLogSessionId(logSessionId); if (enabled_statsd) { android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED, timestamp, pkgName.c_str(), pkgVersionCode, mediaApexVersion, bf_serialized); bf_serialized, logSessionIdForStats.c_str()); } else { ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); Loading
services/mediametrics/statsd_audiotrack.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <statslog.h> #include "MediaMetricsService.h" #include "StringUtils.h" #include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h" #include "iface_statsd.h" Loading Loading @@ -132,12 +133,20 @@ bool statsd_audiotrack(const mediametrics::Item *item) return false; } // Android S // log_session_id (string) std::string logSessionId; (void)item->getString("android.media.audiotrack.logSessionId", &logSessionId); const auto logSessionIdForStats = mediametrics::stringutils::sanitizeLogSessionId(logSessionId); if (enabled_statsd) { android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED, timestamp, pkgName.c_str(), pkgVersionCode, mediaApexVersion, bf_serialized); bf_serialized, logSessionIdForStats.c_str()); } else { ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); Loading
services/mediametrics/tests/mediametrics_tests.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -1082,3 +1082,42 @@ TEST(mediametrics_tests, gc_same_key) { //mediaMetrics->dump(fileno(stdout), {} /* args */); } #endif // Base64Url and isLogSessionId string utilities can be tested by static asserts. static_assert(mediametrics::stringutils::isBase64Url("abc")); static_assert(mediametrics::stringutils::InverseBase64UrlTable['A'] == 0); static_assert(mediametrics::stringutils::InverseBase64UrlTable['a'] == 26); static_assert(mediametrics::stringutils::InverseBase64UrlTable['!'] == mediametrics::stringutils::Transpose::INVALID_CHAR); static_assert(mediametrics::stringutils::InverseBase64UrlTable['@'] == mediametrics::stringutils::Transpose::INVALID_CHAR); static_assert(mediametrics::stringutils::InverseBase64UrlTable['#'] == mediametrics::stringutils::Transpose::INVALID_CHAR); static_assert(!mediametrics::stringutils::isBase64Url("!@#")); static_assert(mediametrics::stringutils::isLogSessionId("0123456789abcdef")); static_assert(!mediametrics::stringutils::isLogSessionId("abc")); static_assert(!mediametrics::stringutils::isLogSessionId("!@#")); static_assert(!mediametrics::stringutils::isLogSessionId("0123456789abcde!")); TEST(mediametrics_tests, sanitizeLogSessionId) { // invalid id returns empty string. ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId("abc")); // valid id passes through. std::string validId = "fedcba9876543210"; ASSERT_EQ(validId, mediametrics::stringutils::sanitizeLogSessionId(validId)); // one more char makes the id invalid ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId + "A")); std::string validId2 = "ZYXWVUT123456789"; ASSERT_EQ(validId2, mediametrics::stringutils::sanitizeLogSessionId(validId2)); // one fewer char makes the id invalid ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId.c_str() + 1)); // replacing one character with an invalid character makes an invalid id. validId2[3] = '!'; ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId2)); }