diff --git a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java index f3d73902414c7..8d2d04471f8e1 100644 --- a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java +++ b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java @@ -37,15 +37,15 @@ public class LongArrayMultiStateCounterPerfTest { /** * A complete line-for-line reimplementation of - * {@link }com.android.internal.os.CpuTimeInFreqMultiStateCounter}, only in Java instead of + * {@link com.android.internal.os.LongArrayMultiStateCounter}, only in Java instead of * native. */ private static class TestLongArrayMultiStateCounter { private final int mStateCount; private final int mArrayLength; private int mCurrentState; - private long mLastStateChangeTimestampMs; - private long mLastUpdateTimestampMs; + private long mLastStateChangeTimestampMs = -1; + private long mLastUpdateTimestampMs = -1; private static class State { private long mTimeInStateSinceUpdate; @@ -56,13 +56,9 @@ public class LongArrayMultiStateCounterPerfTest { private final long[] mLastTimeInFreq; private final long[] mDelta; - TestLongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, - long timestampMs) { + TestLongArrayMultiStateCounter(int stateCount, int arrayLength) { mStateCount = stateCount; mArrayLength = arrayLength; - mCurrentState = initialState; - mLastStateChangeTimestampMs = timestampMs; - mLastUpdateTimestampMs = timestampMs; mStates = new State[stateCount]; for (int i = 0; i < mStateCount; i++) { mStates[i] = new State(); @@ -73,12 +69,14 @@ public class LongArrayMultiStateCounterPerfTest { } public void setState(int state, long timestampMs) { - if (timestampMs >= mLastStateChangeTimestampMs) { - mStates[mCurrentState].mTimeInStateSinceUpdate += - timestampMs - mLastStateChangeTimestampMs; - } else { - for (int i = 0; i < mStateCount; i++) { - mStates[i].mTimeInStateSinceUpdate = 0; + if (mLastStateChangeTimestampMs > 0) { + if (timestampMs >= mLastStateChangeTimestampMs) { + mStates[mCurrentState].mTimeInStateSinceUpdate += + timestampMs - mLastStateChangeTimestampMs; + } else { + for (int i = 0; i < mStateCount; i++) { + mStates[i].mTimeInStateSinceUpdate = 0; + } } } mCurrentState = state; @@ -88,21 +86,23 @@ public class LongArrayMultiStateCounterPerfTest { public void updateValue(long[] timeInFreq, long timestampMs) { setState(mCurrentState, timestampMs); - if (timestampMs > mLastUpdateTimestampMs) { - if (delta(mLastTimeInFreq, timeInFreq, mDelta)) { - long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs; - for (int i = 0; i < mStateCount; i++) { - long timeInState = mStates[i].mTimeInStateSinceUpdate; - if (timeInState > 0) { - add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate); - mStates[i].mTimeInStateSinceUpdate = 0; + if (mLastUpdateTimestampMs >= 0) { + if (timestampMs > mLastUpdateTimestampMs) { + if (delta(mLastTimeInFreq, timeInFreq, mDelta)) { + long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs; + for (int i = 0; i < mStateCount; i++) { + long timeInState = mStates[i].mTimeInStateSinceUpdate; + if (timeInState > 0) { + add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate); + mStates[i].mTimeInStateSinceUpdate = 0; + } } + } else { + throw new RuntimeException(); } - } else { + } else if (timestampMs < mLastUpdateTimestampMs) { throw new RuntimeException(); } - } else if (timestampMs < mLastUpdateTimestampMs) { - throw new RuntimeException(); } System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength); mLastUpdateTimestampMs = timestampMs; @@ -142,7 +142,7 @@ public class LongArrayMultiStateCounterPerfTest { @Test public void javaImplementation() { TestLongArrayMultiStateCounter counter = - new TestLongArrayMultiStateCounter(2, 4, 0, 1000); + new TestLongArrayMultiStateCounter(2, 4); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); long time = 1000; long[] timeInFreq = {100, 200, 300, 400}; @@ -156,7 +156,7 @@ public class LongArrayMultiStateCounterPerfTest { @Test public void nativeImplementation() { - LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000); + LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); long time = 1000; LongArrayMultiStateCounter.LongArrayContainer timeInFreq = diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java index 46e6ba09a2471..47a95c0ae198d 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -16,6 +16,11 @@ 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; @@ -47,7 +52,7 @@ import libcore.util.NativeAllocationRegistry; * * @hide */ -public class LongArrayMultiStateCounter { +public final class LongArrayMultiStateCounter implements Parcelable { /** * Container for a native equivalent of a long[]. @@ -112,14 +117,22 @@ public class LongArrayMultiStateCounter { // methods. final long mNativeObject; - public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState, - long timestampMs) { + public LongArrayMultiStateCounter(int stateCount, int arrayLength) { + Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0"); mStateCount = stateCount; mLength = arrayLength; - mNativeObject = native_init(stateCount, arrayLength, initialState, timestampMs); + mNativeObject = native_init(stateCount, arrayLength); sRegistry.registerNativeAllocation(this, mNativeObject); } + private LongArrayMultiStateCounter(Parcel in) { + mNativeObject = native_initFromParcel(in); + sRegistry.registerNativeAllocation(this, mNativeObject); + + mStateCount = native_getStateCount(mNativeObject); + mLength = native_getArrayLength(mNativeObject); + } + /** * Sets the current state to the supplied value. */ @@ -161,9 +174,32 @@ public class LongArrayMultiStateCounter { 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 CREATOR = + new Creator() { + @Override + public LongArrayMultiStateCounter createFromParcel(Parcel in) { + return new LongArrayMultiStateCounter(in); + } + + @Override + public LongArrayMultiStateCounter[] newArray(int size) { + return new LongArrayMultiStateCounter[size]; + } + }; + + @CriticalNative - private static native long native_init(int stateCount, int arrayLength, int initialState, - long timestampMs); + private static native long native_init(int stateCount, int arrayLength); @CriticalNative private static native long native_getReleaseFunc(); @@ -181,4 +217,16 @@ public class LongArrayMultiStateCounter { @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); + + @CriticalNative + private static native int native_getArrayLength(long nativeObject); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 21a327c4959a0..945adb7cd3ddb 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -263,6 +263,7 @@ cc_library_shared { "libdebuggerd_client", "libutils", "libbinder", + "libbinder_ndk", "libui", "libgraphicsenv", "libgui", diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp index 32c57d1177b41..716c85c2b0fea 100644 --- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp +++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp @@ -14,17 +14,22 @@ * limitations under the License. */ +#include +#include +#include +#include #include + #include + #include "LongArrayMultiStateCounter.h" #include "core_jni_helpers.h" namespace android { -static jlong native_init(jint stateCount, jint arrayLength, jint initialState, jlong timestamp) { +static jlong native_init(jint stateCount, jint arrayLength) { battery::LongArrayMultiStateCounter *counter = - new battery::LongArrayMultiStateCounter(stateCount, initialState, - std::vector(arrayLength), timestamp); + new battery::LongArrayMultiStateCounter(stateCount, std::vector(arrayLength)); return reinterpret_cast(counter); } @@ -69,13 +74,95 @@ static jobject native_toString(JNIEnv *env, jobject self, jlong nativePtr) { return env->NewStringUTF(counter->toString().c_str()); } +static void throwWriteRE(JNIEnv *env, binder_status_t status) { + ALOGE("Could not write LongArrayMultiStateCounter to Parcel, status = %d", status); + jniThrowRuntimeException(env, "Could not write LongArrayMultiStateCounter 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::LongArrayMultiStateCounter *counter = + reinterpret_cast(nativePtr); + AParcel *parcel = AParcel_fromJavaParcel(env, jParcel); + + uint16_t stateCount = counter->getStateCount(); + THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel, stateCount)); + + // LongArrayMultiStateCounter has at least state 0 + const std::vector &anyState = counter->getCount(0); + THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel, anyState.size())); + + for (battery::state_t state = 0; state < stateCount; state++) { + THROW_ON_WRITE_ERROR(ndk::AParcel_writeVector(parcel, counter->getCount(state))); + } +} + +static void throwReadRE(JNIEnv *env, binder_status_t status) { + ALOGE("Could not read LongArrayMultiStateCounter from Parcel, status = %d", status); + jniThrowRuntimeException(env, "Could not read LongArrayMultiStateCounter 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)); + + int32_t arrayLength; + THROW_ON_READ_ERROR(AParcel_readInt32(parcel, &arrayLength)); + + battery::LongArrayMultiStateCounter *counter = + new battery::LongArrayMultiStateCounter(stateCount, std::vector(arrayLength)); + + std::vector value; + value.reserve(arrayLength); + + for (battery::state_t state = 0; state < stateCount; state++) { + THROW_ON_READ_ERROR(ndk::AParcel_readVector(parcel, &value)); + counter->setValue(state, value); + } + + return reinterpret_cast(counter); +} + +static jint native_getStateCount(jlong nativePtr) { + battery::LongArrayMultiStateCounter *counter = + reinterpret_cast(nativePtr); + return counter->getStateCount(); +} + +static jint native_getArrayLength(jlong nativePtr) { + battery::LongArrayMultiStateCounter *counter = + reinterpret_cast(nativePtr); + + // LongArrayMultiStateCounter has at least state 0 + const std::vector &anyState = counter->getCount(0); + return anyState.size(); +} + static jlong native_init_LongArrayContainer(jint length) { return reinterpret_cast(new std::vector(length)); } static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = { // @CriticalNative - {"native_init", "(IIIJ)J", (void *)native_init}, + {"native_init", "(II)J", (void *)native_init}, // @CriticalNative {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc}, // @CriticalNative @@ -86,6 +173,14 @@ static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = { {"native_getCounts", "(JJI)V", (void *)native_getCounts}, // @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}, + // @CriticalNative + {"native_getArrayLength", "(J)I", (void *)native_getArrayLength}, }; /////////////////////// LongArrayMultiStateCounter.LongArrayContainer //////////////////////// diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java index eb3bae042fff5..418257470be93 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java @@ -282,7 +282,11 @@ public class KernelSingleUidTimeReaderTest { @Test public void testAddDeltaFromBpf() { - LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5, 0, 0); + LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5); + counter.setState(0, 0); + mInjector.setCpuTimeInStatePerClusterNs(new long[][]{{0, 0, 0}, {0, 0}}); + boolean success = mInjector.addDelta(TEST_UID, counter, 0); + assertThat(success).isTrue(); // Nanoseconds mInjector.setCpuTimeInStatePerClusterNs( @@ -290,7 +294,7 @@ public class KernelSingleUidTimeReaderTest { {1_000_000, 2_000_000, 3_000_000}, {4_000_000, 5_000_000}}); - boolean success = mInjector.addDelta(TEST_UID, counter, 2000); + success = mInjector.addDelta(TEST_UID, counter, 2000); assertThat(success).isTrue(); LongArrayMultiStateCounter.LongArrayContainer array = diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java index 2da9f5761294d..da158e645de6c 100644 --- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java @@ -18,6 +18,8 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; +import android.os.Parcel; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -30,26 +32,74 @@ public class LongArrayMultiStateCounterTest { @Test public void setStateAndUpdateValue() { - LongArrayMultiStateCounter.LongArrayContainer longArrayContainer = - new LongArrayMultiStateCounter.LongArrayContainer(4); - LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000); + LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4); + + updateValue(counter, new long[]{0, 0, 0, 0}, 1000); + counter.setState(0, 1000); counter.setState(1, 2000); counter.setState(0, 4000); - longArrayContainer.setValues(new long[]{100, 200, 300, 400}); - counter.updateValues(longArrayContainer, 9000); - counter.getCounts(longArrayContainer, 0); + updateValue(counter, new long[]{100, 200, 300, 400}, 9000); - long[] result = new long[4]; - longArrayContainer.getValues(result); - assertThat(result).isEqualTo(new long[]{75, 150, 225, 300}); - - counter.getCounts(longArrayContainer, 1); - longArrayContainer.getValues(result); - assertThat(result).isEqualTo(new long[]{25, 50, 75, 100}); + assertCounts(counter, 0, new long[]{75, 150, 225, 300}); + assertCounts(counter, 1, new long[]{25, 50, 75, 100}); assertThat(counter.toString()).isEqualTo( - "currentState: 0 lastStateChangeTimestamp: 9000 lastUpdateTimestamp: 9000 states:" - + " [0: time: 0 counter: { 75, 150, 225, 300}" - + ", 1: time: 0 counter: { 25, 50, 75, 100}]"); + "[0: {75, 150, 225, 300}, 1: {25, 50, 75, 100}] updated: 9000 currentState: 0"); + } + + @Test + public void parceling() { + LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4); + updateValue(counter, new long[]{0, 0, 0, 0}, 1000); + counter.setState(0, 1000); + updateValue(counter, new long[]{100, 200, 300, 400}, 2000); + counter.setState(1, 2000); + updateValue(counter, new long[]{101, 202, 304, 408}, 3000); + + assertCounts(counter, 0, new long[]{100, 200, 300, 400}); + assertCounts(counter, 1, new long[]{1, 2, 4, 8}); + + Parcel parcel = Parcel.obtain(); + counter.writeToParcel(parcel, 0); + byte[] bytes = parcel.marshall(); + parcel.recycle(); + + parcel = Parcel.obtain(); + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + + LongArrayMultiStateCounter newCounter = + LongArrayMultiStateCounter.CREATOR.createFromParcel(parcel); + parcel.recycle(); + + assertCounts(newCounter, 0, new long[]{100, 200, 300, 400}); + assertCounts(newCounter, 1, new long[]{1, 2, 4, 8}); + + // ==== Verify that the counter keeps accumulating after unparceling. + + // State, last update timestamp and current counts are undefined at this point. + newCounter.setState(0, 100); + updateValue(newCounter, new long[]{300, 400, 500, 600}, 100); + + // A new base state and counters are established; we can continue accumulating deltas + updateValue(newCounter, new long[]{316, 432, 564, 728}, 200); + + assertCounts(newCounter, 0, new long[]{116, 232, 364, 528}); + } + + private void updateValue(LongArrayMultiStateCounter counter, long[] values, int timestamp) { + LongArrayMultiStateCounter.LongArrayContainer container = + new LongArrayMultiStateCounter.LongArrayContainer(values.length); + container.setValues(values); + counter.updateValues(container, timestamp); + } + + private void assertCounts(LongArrayMultiStateCounter counter, int state, long[] expected) { + LongArrayMultiStateCounter.LongArrayContainer container = + new LongArrayMultiStateCounter.LongArrayContainer(expected.length); + long[] counts = new long[expected.length]; + counter.getCounts(container, state); + container.getValues(counts); + assertThat(counts).isEqualTo(expected); } }