Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cff33a77 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "MediaMetrics: Add logSessionId handling" into sc-dev

parents 456787fa cbcfaa20
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ static constexpr const char * const AudioRecordDeviceUsageFields[] = {
    "selected_device_id",
    "caller",
    "source",
    "log_session_id",
};

static constexpr const char * const AudioThreadDeviceUsageFields[] = {
@@ -124,6 +125,7 @@ static constexpr const char * const AudioTrackDeviceUsageFields[] = {
    "content_type",
    "caller",
    "traits",
    "log_session_id",
};

static constexpr const char * const AudioDeviceConnectionFields[] = {
@@ -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
@@ -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,
@@ -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());
@@ -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);
@@ -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
@@ -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) {
@@ -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());
+78 −0
Original line number Diff line number Diff line
@@ -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
+10 −2
Original line number Diff line number Diff line
@@ -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"

@@ -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()));
+10 −1
Original line number Diff line number Diff line
@@ -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"

@@ -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()));
+39 −0
Original line number Diff line number Diff line
@@ -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));
}