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

Commit 0a316738 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes from topic "StatsEvent JNI"

* changes:
  Improve stats_event memory usage
  StatsEventCompat
  Remove libstats/Android.bp
  Make stats_event write function parameters const
  Create stats_buffer_writer
parents 27fa358d 79dd3eaa
Loading
Loading
Loading
Loading

libstats/Android.bp

deleted100644 → 0
+0 −4
Original line number Diff line number Diff line
subdirs = [
    "socket",
    "socket_q",
]
+54 −0
Original line number Diff line number Diff line
//
// 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.
//

// =========================================================================
// Native library that toggles between the old and new statsd socket
// protocols. This library should only be used by DNS resolver or other
// native modules on Q that log pushed atoms to statsd.
// =========================================================================
cc_defaults {
    name: "libstatspush_compat_defaults",
    srcs: ["StatsEventCompat.cpp"],
    cflags: [
        "-Wall",
        "-Werror",
    ],
    header_libs: ["libstatssocket_headers"],
    static_libs: [
        "libbase",
        "liblog",
        "libstatssocket_q",
        "libutils"
    ],
}

cc_library {
    name: "libstatspush_compat",
    defaults: ["libstatspush_compat_defaults"],
    export_include_dirs: ["include"],
    static_libs: ["libgtest_prod"],
}

cc_test {
    name: "libstatspush_compat_test",
    defaults: ["libstatspush_compat_defaults"],
    test_suites: ["device_tests"],
    srcs: [
        "tests/StatsEventCompat_test.cpp",
    ],
    static_libs: ["libgmock"],
}
+221 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include "include/StatsEventCompat.h"
#include <android-base/properties.h>
#include <android/api-level.h>
#include <android/log.h>
#include <dlfcn.h>
#include <utils/SystemClock.h>

using android::base::GetProperty;

const static int kStatsEventTag = 1937006964;

/* Checking ro.build.version.release is fragile, as the release field is
 * an opaque string without structural guarantees. However, testing confirms
 * that on Q devices, the property is "10," and on R, it is "R." Until
 * android_get_device_api_level() is updated, this is the only solution.
 *
 * TODO(b/146019024): migrate to android_get_device_api_level()
 */
const bool StatsEventCompat::mPlatformAtLeastR =
        GetProperty("ro.build.version.codename", "") == "R" ||
        android_get_device_api_level() > __ANDROID_API_Q__;

// definitions of static class variables
bool StatsEventCompat::mAttemptedLoad = false;
struct stats_event_api_table* StatsEventCompat::mStatsEventApi = nullptr;
std::mutex StatsEventCompat::mLoadLock;

StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
    // guard loading because StatsEventCompat might be called from multithreaded
    // environment
    {
        std::lock_guard<std::mutex> lg(mLoadLock);
        if (!mAttemptedLoad) {
            void* handle = dlopen("libstatssocket.so", RTLD_NOW);
            if (handle) {
                mStatsEventApi = (struct stats_event_api_table*)dlsym(handle, "table");
            } else {
                ALOGE("dlopen failed: %s\n", dlerror());
            }
        }
        mAttemptedLoad = true;
    }

    if (mStatsEventApi) {
        mEventR = mStatsEventApi->obtain();
    } else if (!mPlatformAtLeastR) {
        mEventQ << android::elapsedRealtimeNano();
    }
}

StatsEventCompat::~StatsEventCompat() {
    if (mStatsEventApi) mStatsEventApi->release(mEventR);
}

void StatsEventCompat::setAtomId(int32_t atomId) {
    if (mStatsEventApi) {
        mStatsEventApi->set_atom_id(mEventR, (uint32_t)atomId);
    } else if (!mPlatformAtLeastR) {
        mEventQ << atomId;
    }
}

void StatsEventCompat::writeInt32(int32_t value) {
    if (mStatsEventApi) {
        mStatsEventApi->write_int32(mEventR, value);
    } else if (!mPlatformAtLeastR) {
        mEventQ << value;
    }
}

void StatsEventCompat::writeInt64(int64_t value) {
    if (mStatsEventApi) {
        mStatsEventApi->write_int64(mEventR, value);
    } else if (!mPlatformAtLeastR) {
        mEventQ << value;
    }
}

void StatsEventCompat::writeFloat(float value) {
    if (mStatsEventApi) {
        mStatsEventApi->write_float(mEventR, value);
    } else if (!mPlatformAtLeastR) {
        mEventQ << value;
    }
}

void StatsEventCompat::writeBool(bool value) {
    if (mStatsEventApi) {
        mStatsEventApi->write_bool(mEventR, value);
    } else if (!mPlatformAtLeastR) {
        mEventQ << value;
    }
}

void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
    if (mStatsEventApi) {
        mStatsEventApi->write_byte_array(mEventR, (const uint8_t*)buffer, length);
    } else if (!mPlatformAtLeastR) {
        mEventQ.AppendCharArray(buffer, length);
    }
}

void StatsEventCompat::writeString(const char* value) {
    if (value == nullptr) value = "";

    if (mStatsEventApi) {
        mStatsEventApi->write_string8(mEventR, value);
    } else if (!mPlatformAtLeastR) {
        mEventQ << value;
    }
}

void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
                                             const vector<const char*>& tags) {
    if (mStatsEventApi) {
        mStatsEventApi->write_attribution_chain(mEventR, (const uint32_t*)uids, tags.data(),
                                                (uint8_t)numUids);
    } else if (!mPlatformAtLeastR) {
        mEventQ.begin();
        for (size_t i = 0; i < numUids; i++) {
            mEventQ.begin();
            mEventQ << uids[i];
            const char* tag = tags[i] ? tags[i] : "";
            mEventQ << tag;
            mEventQ.end();
        }
        mEventQ.end();
    }
}

void StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map,
                                          const map<int, int64_t>& int64Map,
                                          const map<int, const char*>& stringMap,
                                          const map<int, float>& floatMap) {
    if (mStatsEventApi) {
        vector<struct key_value_pair> pairs;

        for (const auto& it : int32Map) {
            pairs.push_back({.key = it.first, .valueType = INT32_TYPE, .int32Value = it.second});
        }
        for (const auto& it : int64Map) {
            pairs.push_back({.key = it.first, .valueType = INT64_TYPE, .int64Value = it.second});
        }
        for (const auto& it : stringMap) {
            pairs.push_back({.key = it.first, .valueType = STRING_TYPE, .stringValue = it.second});
        }
        for (const auto& it : floatMap) {
            pairs.push_back({.key = it.first, .valueType = FLOAT_TYPE, .floatValue = it.second});
        }

        mStatsEventApi->write_key_value_pairs(mEventR, pairs.data(), (uint8_t)pairs.size());
    }

    else if (!mPlatformAtLeastR) {
        mEventQ.begin();
        writeKeyValuePairMap(int32Map);
        writeKeyValuePairMap(int64Map);
        writeKeyValuePairMap(stringMap);
        writeKeyValuePairMap(floatMap);
        mEventQ.end();
    }
}

template <class T>
void StatsEventCompat::writeKeyValuePairMap(const map<int, T>& keyValuePairMap) {
    for (const auto& it : keyValuePairMap) {
        mEventQ.begin();
        mEventQ << it.first;
        mEventQ << it.second;
        mEventQ.end();
    }
}

// explicitly specify which types we're going to use
template void StatsEventCompat::writeKeyValuePairMap<int32_t>(const map<int, int32_t>&);
template void StatsEventCompat::writeKeyValuePairMap<int64_t>(const map<int, int64_t>&);
template void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float>&);
template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);

void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
    if (mStatsEventApi) mStatsEventApi->add_bool_annotation(mEventR, annotationId, value);
    // Don't do anything if on Q.
}

void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
    if (mStatsEventApi) mStatsEventApi->add_int32_annotation(mEventR, annotationId, value);
    // Don't do anything if on Q.
}

int StatsEventCompat::writeToSocket() {
    if (mStatsEventApi) {
        mStatsEventApi->build(mEventR);
        return mStatsEventApi->write(mEventR);
    }

    if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS);

    // We reach here only if we're on R, but libstatspush_compat was unable to
    // be loaded using dlopen.
    return -ENOLINK;
}

bool StatsEventCompat::usesNewSchema() {
    return mStatsEventApi != nullptr;
}
+71 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#pragma once

#include <gtest/gtest_prod.h>
#include <map>
#include <mutex>
#include <vector>
#include "stats_event.h"
#include "stats_event_list.h"

using std::map;
using std::vector;

class StatsEventCompat {
  public:
    StatsEventCompat();
    ~StatsEventCompat();

    void setAtomId(int32_t atomId);
    void writeInt32(int32_t value);
    void writeInt64(int64_t value);
    void writeFloat(float value);
    void writeBool(bool value);
    void writeByteArray(const char* buffer, size_t length);
    void writeString(const char* value);

    // Pre-condition: numUids == tags.size()
    void writeAttributionChain(const int32_t* uids, size_t numUids,
                               const vector<const char*>& tags);

    void writeKeyValuePairs(const map<int, int32_t>& int32Map, const map<int, int64_t>& int64Map,
                            const map<int, const char*>& stringMap,
                            const map<int, float>& floatMap);

    void addBoolAnnotation(uint8_t annotationId, bool value);
    void addInt32Annotation(uint8_t annotationId, int32_t value);

    int writeToSocket();

  private:
    // static member variables
    const static bool mPlatformAtLeastR;
    static bool mAttemptedLoad;
    static std::mutex mLoadLock;
    static struct stats_event_api_table* mStatsEventApi;

    // non-static member variables
    struct stats_event* mEventR = nullptr;
    stats_event_list mEventQ;

    template <class T>
    void writeKeyValuePairMap(const map<int, T>& keyValuePairMap);

    bool usesNewSchema();
    FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading);
};
+38 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include "include/StatsEventCompat.h"
#include <android-base/properties.h>
#include <android/api-level.h>
#include <gtest/gtest.h>

using android::base::GetProperty;

/* Checking ro.build.version.release is fragile, as the release field is
 * an opaque string without structural guarantees. However, testing confirms
 * that on Q devices, the property is "10," and on R, it is "R." Until
 * android_get_device_api_level() is updated, this is the only solution.
 *
 *
 * TODO(b/146019024): migrate to android_get_device_api_level()
 */
const static bool mPlatformAtLeastR = GetProperty("ro.build.version.release", "") == "R" ||
                                      android_get_device_api_level() > __ANDROID_API_Q__;

TEST(StatsEventCompatTest, TestDynamicLoading) {
    StatsEventCompat event;
    EXPECT_EQ(mPlatformAtLeastR, event.usesNewSchema());
}
Loading