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

Commit 4c21a3b7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Increase size limit for pulled StatsEvent." into rvc-dev

parents e30c1fe9 351e40fb
Loading
Loading
Loading
Loading
+42 −8
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;

/**
 * StatsEvent builds and stores the buffer sent over the statsd socket.
 * This class defines and encapsulates the socket protocol.
@@ -224,7 +226,9 @@ public final class StatsEvent {

    // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
    // See android_util_StatsLog.cpp.
    private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
    private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;

    private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB

    private final int mAtomId;
    private final byte[] mPayload;
@@ -619,6 +623,7 @@ public final class StatsEvent {
        @NonNull
        public Builder usePooledBuffer() {
            mUsePooledBuffer = true;
            mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos);
            return this;
        }

@@ -694,8 +699,9 @@ public final class StatsEvent {
        @GuardedBy("sLock")
        private static Buffer sPool;

        private final byte[] mBytes = new byte[MAX_PAYLOAD_SIZE];
        private byte[] mBytes = new byte[MAX_PUSH_PAYLOAD_SIZE];
        private boolean mOverflow = false;
        private int mMaxSize = MAX_PULL_PAYLOAD_SIZE;

        @NonNull
        private static Buffer obtain() {
@@ -717,15 +723,26 @@ public final class StatsEvent {
        }

        private void release() {
            // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under.
            if (mBytes.length <= MAX_PUSH_PAYLOAD_SIZE) {
                synchronized (sLock) {
                    if (null == sPool) {
                        sPool = this;
                    }
                }
            }
        }

        private void reset() {
            mOverflow = false;
            mMaxSize = MAX_PULL_PAYLOAD_SIZE;
        }

        private void setMaxSize(final int maxSize, final int numBytesWritten) {
            mMaxSize = maxSize;
            if (numBytesWritten > maxSize) {
                mOverflow = true;
            }
        }

        private boolean hasOverflowed() {
@@ -740,11 +757,28 @@ public final class StatsEvent {
         * @return true if space is available, false otherwise.
         **/
        private boolean hasEnoughSpace(final int index, final int numBytes) {
            final boolean result = index + numBytes < MAX_PAYLOAD_SIZE;
            if (!result) {
            final int totalBytesNeeded = index + numBytes;

            if (totalBytesNeeded > mMaxSize) {
                mOverflow = true;
                return false;
            }

            // Expand buffer if needed.
            if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) {
                int newSize = mBytes.length;
                do {
                    newSize *= 2;
                } while (newSize <= totalBytesNeeded);

                if (newSize > mMaxSize) {
                    newSize = mMaxSize;
                }
            return result;

                mBytes = Arrays.copyOf(mBytes, newSize);
            }

            return true;
        }

        /**
+160 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import org.junit.runner.RunWith;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;

/**
 * Internal tests for {@link StatsEvent}.
@@ -644,6 +645,165 @@ public class StatsEventTest {
        statsEvent.release();
    }

    @Test
    public void testLargePulledEvent() {
        final int expectedAtomId = 10_020;
        byte[] field1 = new byte[10 * 1024];
        new Random().nextBytes(field1);

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent =
                StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get())
                .isEqualTo(3);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong())
                .isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);

        assertWithMessage("Third element is not byte array")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_BYTE_ARRAY);

        final byte[] field1Actual = getByteArrayFromByteBuffer(buffer);
        assertWithMessage("Incorrect field 1").that(field1Actual).isEqualTo(field1);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    @Test
    public void testPulledEventOverflow() {
        final int expectedAtomId = 10_020;
        byte[] field1 = new byte[50 * 1024];
        new Random().nextBytes(field1);

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent =
                StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get())
                .isEqualTo(3);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong())
                .isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);

        assertWithMessage("Third element is not errors type")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_ERRORS);

        final int errorMask = buffer.getInt();

        assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
                .that(errorMask)
                .isEqualTo(StatsEvent.ERROR_OVERFLOW);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    @Test
    public void testPushedEventOverflow() {
        final int expectedAtomId = 10_020;
        byte[] field1 = new byte[10 * 1024];
        new Random().nextBytes(field1);

        final long minTimestamp = SystemClock.elapsedRealtimeNanos();
        final StatsEvent statsEvent = StatsEvent.newBuilder()
                                              .setAtomId(expectedAtomId)
                                              .writeByteArray(field1)
                                              .usePooledBuffer()
                                              .build();
        final long maxTimestamp = SystemClock.elapsedRealtimeNanos();

        assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);

        final ByteBuffer buffer =
                ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);

        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_OBJECT);

        assertWithMessage("Incorrect number of elements in root object")
                .that(buffer.get())
                .isEqualTo(3);

        assertWithMessage("First element is not timestamp")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_LONG);

        assertWithMessage("Incorrect timestamp")
                .that(buffer.getLong())
                .isIn(Range.closed(minTimestamp, maxTimestamp));

        assertWithMessage("Second element is not atom id")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_INT);

        assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);

        assertWithMessage("Third element is not errors type")
                .that(buffer.get())
                .isEqualTo(StatsEvent.TYPE_ERRORS);

        final int errorMask = buffer.getInt();

        assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
                .that(errorMask)
                .isEqualTo(StatsEvent.ERROR_OVERFLOW);

        assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());

        statsEvent.release();
    }

    private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
        final int numBytes = buffer.getInt();
        byte[] bytes = new byte[numBytes];