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

Commit fef64cca authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Add LongMultiStateCounter

Bug: 197162116
Test: atest FrameworksCoreTests:LongMultiStateCounterTest
Change-Id: If5af2a7c8d9f56ef5e751aeac4082ac346b276ad
parent 6551462d
Loading
Loading
Loading
Loading
+210 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

package com.android.internal.os;

import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.util.Preconditions;

import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;

import libcore.util.NativeAllocationRegistry;

/**
 * Performs per-state counting of long integers over time.  The tracked "value" is expected
 * to increase monotonously. The counter keeps track of the current state.  When the
 * updateValue method is called, the delta from the previous invocation of this method
 * and the new value is added to the counter corresponding to the current state.  If the
 * state changed in the interim, the delta is distributed proptionally.
 *
 * The class's behavior is illustrated by this example:
 * <pre>
 *   // At 0 ms, the state of the tracked object is 0 and the initial tracked value is 100
 *   counter.setState(0, 0);
 *   counter.updateValue(100, 0);
 *
 *   // At 1000 ms, the state changes to 1
 *   counter.setState(1, 1000);
 *
 *   // At 3000 ms, the tracked value is updated to 130
 *   counter.updateValue(130, 3000);
 *
 *   // The delta (130 - 100 = 30) is distributed between states 0 and 1 according to the time
 *   // spent in those respective states; in this specific case, 1000 and 2000 ms.
 *   long countForState0 == counter.getCount(0);  // 10
 *   long countForState1 == counter.getCount(1);  // 20
 * </pre>
 *
 * The tracked values are expected to increase monotonically.
 *
 * @hide
 */
public final class LongMultiStateCounter implements Parcelable {

    private static final NativeAllocationRegistry sRegistry =
            NativeAllocationRegistry.createMalloced(
                    LongMultiStateCounter.class.getClassLoader(), native_getReleaseFunc());

    private final int mStateCount;

    // Visible to other objects in this package so that it can be passed to @CriticalNative
    // methods.
    final long mNativeObject;

    public LongMultiStateCounter(int stateCount) {
        Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0");
        mStateCount = stateCount;
        mNativeObject = native_init(stateCount);
        sRegistry.registerNativeAllocation(this, mNativeObject);
    }

    private LongMultiStateCounter(Parcel in) {
        mNativeObject = native_initFromParcel(in);
        sRegistry.registerNativeAllocation(this, mNativeObject);

        mStateCount = native_getStateCount(mNativeObject);
    }

    public int getStateCount() {
        return mStateCount;
    }

    /**
     * Enables or disables the counter.  When the counter is disabled, it does not
     * accumulate counts supplied by the {@link #updateValue} method.
     */
    public void setEnabled(boolean enabled, long timestampMs) {
        native_setEnabled(mNativeObject, enabled, timestampMs);
    }

    /**
     * Sets the current state to the supplied value.
     *
     * @param state The new state
     * @param timestampMs The time when the state change occurred, e.g.
     *                    SystemClock.elapsedRealtime()
     */
    public void setState(int state, long timestampMs) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
        }
        native_setState(mNativeObject, state, timestampMs);
    }

    /**
     * Sets the new values.  The delta between the previously set values and these values
     * is distributed among the state according to the time the object spent in those states
     * since the previous call to updateValues.
     */
    public void updateValue(long value, long timestampMs) {
        native_updateValue(mNativeObject, value, timestampMs);
    }

    /**
     * Adds the supplied values to the current accumulated values in the counter.
     */
    public void addCount(long count) {
        native_addCount(mNativeObject, count);
    }

    /**
     * Resets the accumulated counts to 0.
     */
    public void reset() {
        native_reset(mNativeObject);
    }

    /**
     * Returns the accumulated count for the specified state.
     */
    public long getCount(int state) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + mStateCount + "]");
        }
        return native_getCount(mNativeObject, state);
    }

    @Override
    public String toString() {
        return native_toString(mNativeObject);
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        native_writeToParcel(mNativeObject, dest, flags);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<LongMultiStateCounter> CREATOR =
            new Creator<LongMultiStateCounter>() {
                @Override
                public LongMultiStateCounter createFromParcel(Parcel in) {
                    return new LongMultiStateCounter(in);
                }

                @Override
                public LongMultiStateCounter[] newArray(int size) {
                    return new LongMultiStateCounter[size];
                }
            };


    @CriticalNative
    private static native long native_init(int stateCount);

    @CriticalNative
    private static native long native_getReleaseFunc();

    @CriticalNative
    private static native void native_setEnabled(long nativeObject, boolean enabled,
            long timestampMs);

    @CriticalNative
    private static native void native_setState(long nativeObject, int state, long timestampMs);

    @CriticalNative
    private static native void native_updateValue(long nativeObject, long value, long timestampMs);

    @CriticalNative
    private static native void native_addCount(long nativeObject, long count);

    @CriticalNative
    private static native void native_reset(long nativeObject);

    @CriticalNative
    private static native long native_getCount(long nativeObject, int state);

    @FastNative
    private native String native_toString(long nativeObject);

    @FastNative
    private native void native_writeToParcel(long nativeObject, Parcel dest, int flags);

    @FastNative
    private static native long native_initFromParcel(Parcel parcel);

    @CriticalNative
    private static native int native_getStateCount(long nativeObject);
}
+1 −0
Original line number Diff line number Diff line
@@ -218,6 +218,7 @@ cc_library_shared {
                "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
                "com_android_internal_os_KernelSingleUidTimeReader.cpp",
                "com_android_internal_os_LongArrayMultiStateCounter.cpp",
                "com_android_internal_os_LongMultiStateCounter.cpp",
                "com_android_internal_os_Zygote.cpp",
                "com_android_internal_os_ZygoteCommandBuffer.cpp",
                "com_android_internal_os_ZygoteInit.cpp",
+2 −0
Original line number Diff line number Diff line
@@ -205,6 +205,7 @@ extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env
extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
extern int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv* env);
extern int register_com_android_internal_os_LongMultiStateCounter(JNIEnv* env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
@@ -1588,6 +1589,7 @@ static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
        REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
        REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
        REG_JNI(register_com_android_internal_os_LongMultiStateCounter),
        REG_JNI(register_com_android_internal_os_Zygote),
        REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
        REG_JNI(register_com_android_internal_os_ZygoteInit),
+195 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
#include <android/binder_parcel_utils.h>
#include <android_runtime/Log.h>

#include <cstring>

#include "MultiStateCounter.h"
#include "core_jni_helpers.h"

namespace android {

namespace battery {

typedef battery::MultiStateCounter<int64_t> LongMultiStateCounter;

template <>
bool LongMultiStateCounter::delta(const int64_t &previousValue, const int64_t &newValue,
                                  int64_t *outValue) const {
    *outValue = newValue - previousValue;
    return *outValue >= 0;
}

template <>
void LongMultiStateCounter::add(int64_t *value1, const int64_t &value2, const uint64_t numerator,
                                const uint64_t denominator) const {
    if (numerator != denominator) {
        // The caller ensures that denominator != 0
        *value1 += value2 * numerator / denominator;
    } else {
        *value1 += value2;
    }
}

template <>
std::string LongMultiStateCounter::valueToString(const int64_t &v) const {
    return std::to_string(v);
}

} // namespace battery

static inline battery::LongMultiStateCounter *asLongMultiStateCounter(const jlong nativePtr) {
    return reinterpret_cast<battery::LongMultiStateCounter *>(nativePtr);
}

static jlong native_init(jint stateCount) {
    battery::LongMultiStateCounter *counter = new battery::LongMultiStateCounter(stateCount, 0);
    return reinterpret_cast<jlong>(counter);
}

static void native_dispose(void *nativePtr) {
    delete reinterpret_cast<battery::LongMultiStateCounter *>(nativePtr);
}

static jlong native_getReleaseFunc() {
    return reinterpret_cast<jlong>(native_dispose);
}

static void native_setEnabled(jlong nativePtr, jboolean enabled, jlong timestamp) {
    asLongMultiStateCounter(nativePtr)->setEnabled(enabled, timestamp);
}

static void native_setState(jlong nativePtr, jint state, jlong timestamp) {
    asLongMultiStateCounter(nativePtr)->setState(state, timestamp);
}

static void native_updateValue(jlong nativePtr, jlong value, jlong timestamp) {
    asLongMultiStateCounter(nativePtr)->updateValue((int64_t)value, timestamp);
}

static void native_addCount(jlong nativePtr, jlong count) {
    asLongMultiStateCounter(nativePtr)->addValue(count);
}

static void native_reset(jlong nativePtr) {
    asLongMultiStateCounter(nativePtr)->reset();
}

static jlong native_getCount(jlong nativePtr, jint state) {
    return asLongMultiStateCounter(nativePtr)->getCount(state);
}

static jobject native_toString(JNIEnv *env, jobject self, jlong nativePtr) {
    return env->NewStringUTF(asLongMultiStateCounter(nativePtr)->toString().c_str());
}

static void throwWriteRE(JNIEnv *env, binder_status_t status) {
    ALOGE("Could not write LongMultiStateCounter to Parcel, status = %d", status);
    jniThrowRuntimeException(env, "Could not write LongMultiStateCounter to Parcel");
}

#define THROW_ON_WRITE_ERROR(expr)     \
    {                                  \
        binder_status_t status = expr; \
        if (status != STATUS_OK) {     \
            throwWriteRE(env, status); \
        }                              \
    }

static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, jobject jParcel,
                                 jint flags) {
    battery::LongMultiStateCounter *counter = asLongMultiStateCounter(nativePtr);
    AParcel *parcel = AParcel_fromJavaParcel(env, jParcel);

    uint16_t stateCount = counter->getStateCount();
    THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel, stateCount));

    for (battery::state_t state = 0; state < stateCount; state++) {
        THROW_ON_WRITE_ERROR(AParcel_writeInt64(parcel, counter->getCount(state)));
    }
}

static void throwReadRE(JNIEnv *env, binder_status_t status) {
    ALOGE("Could not read LongMultiStateCounter from Parcel, status = %d", status);
    jniThrowRuntimeException(env, "Could not read LongMultiStateCounter from Parcel");
}

#define THROW_ON_READ_ERROR(expr)      \
    {                                  \
        binder_status_t status = expr; \
        if (status != STATUS_OK) {     \
            throwReadRE(env, status);  \
        }                              \
    }

static jlong native_initFromParcel(JNIEnv *env, jclass theClass, jobject jParcel) {
    AParcel *parcel = AParcel_fromJavaParcel(env, jParcel);

    int32_t stateCount;
    THROW_ON_READ_ERROR(AParcel_readInt32(parcel, &stateCount));

    battery::LongMultiStateCounter *counter = new battery::LongMultiStateCounter(stateCount, 0);

    for (battery::state_t state = 0; state < stateCount; state++) {
        int64_t value;
        THROW_ON_READ_ERROR(AParcel_readInt64(parcel, &value));
        counter->setValue(state, value);
    }

    return reinterpret_cast<jlong>(counter);
}

static jint native_getStateCount(jlong nativePtr) {
    return asLongMultiStateCounter(nativePtr)->getStateCount();
}

static const JNINativeMethod g_methods[] = {
        // @CriticalNative
        {"native_init", "(I)J", (void *)native_init},
        // @CriticalNative
        {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc},
        // @CriticalNative
        {"native_setEnabled", "(JZJ)V", (void *)native_setEnabled},
        // @CriticalNative
        {"native_setState", "(JIJ)V", (void *)native_setState},
        // @CriticalNative
        {"native_updateValue", "(JJJ)V", (void *)native_updateValue},
        // @CriticalNative
        {"native_addCount", "(JJ)V", (void *)native_addCount},
        // @CriticalNative
        {"native_reset", "(J)V", (void *)native_reset},
        // @CriticalNative
        {"native_getCount", "(JI)J", (void *)native_getCount},
        // @FastNative
        {"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
        // @FastNative
        {"native_writeToParcel", "(JLandroid/os/Parcel;I)V", (void *)native_writeToParcel},
        // @FastNative
        {"native_initFromParcel", "(Landroid/os/Parcel;)J", (void *)native_initFromParcel},
        // @CriticalNative
        {"native_getStateCount", "(J)I", (void *)native_getStateCount},
};

int register_com_android_internal_os_LongMultiStateCounter(JNIEnv *env) {
    return RegisterMethodsOrDie(env, "com/android/internal/os/LongMultiStateCounter", g_methods,
                                NELEM(g_methods));
}

} // namespace android
+2 −0
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ import org.junit.runners.Suite;
        KernelSingleProcessCpuThreadReaderTest.class,
        KernelSingleUidTimeReaderTest.class,
        KernelWakelockReaderTest.class,
        LongArrayMultiStateCounterTest.class,
        LongMultiStateCounterTest.class,
        LongSamplingCounterTest.class,
        LongSamplingCounterArrayTest.class,
        MobileRadioPowerCalculatorTest.class,
Loading