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

Commit 2ae454dd authored by Ruchir Rastogi's avatar Ruchir Rastogi
Browse files

Wrap StatsDimensionsValue over structured parcel

Reduces code complexity by leveraging the readFromParcel and
createFromParcel functions created in the autogenerated
StatsDimensionsValueParcel class.

Wrote a unit test that checks the conversion from a
StatsDimensionsValueParcel to a StatsDimensionsValue.

Test: atest FrameworkStatsdTests
Test: atest GtsStatsdHostTestCases
Bug: 149103391
Change-Id: I64850c6b962a68799d51a26138e95cb1e0c99aee
parent 91482ccc
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -4,12 +4,12 @@ package android.os;
 * @hide
 */
parcelable StatsDimensionsValueParcel {
    /**
     * Field equals:
     *      - atomTag for top level StatsDimensionsValueParcel
     *      - position in dimension for all other levels
     */
    // Field equals atomTag for top level StatsDimensionsValueParcels or
    // positions in depth (1-indexed) for lower level parcels.
    int field;

    // Indicator for which type of value is stored. Should be set to one
    // of the constants in StatsDimensionsValue.java.
    int valueType;

    String stringValue;
+83 −161
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ public final class StatsDimensionsValue implements Parcelable {
    private static final String TAG = "StatsDimensionsValue";

    // Values of the value type correspond to stats_log.proto's DimensionValue fields.
    // Keep constants in sync with services/include/android/os/StatsDimensionsValue.h.
    // Keep constants in sync with frameworks/base/cmds/statsd/src/HashableDimensionKey.cpp.
    /** Indicates that this holds a String. */
    public static final int STRING_VALUE_TYPE = 2;
    /** Indicates that this holds an int. */
@@ -72,17 +72,7 @@ public final class StatsDimensionsValue implements Parcelable {
    /** Indicates that this holds a List of StatsDimensionsValues. */
    public static final int TUPLE_VALUE_TYPE = 7;

    /** Value of a stats_log.proto DimensionsValue.field. */
    private final int mField;

    /** Type of stats_log.proto DimensionsValue.value, according to the VALUE_TYPEs above. */
    private final int mValueType;

    /**
     * Value of a stats_log.proto DimensionsValue.value.
     * String, Integer, Long, Boolean, Float, or StatsDimensionsValue[].
     */
    private final Object mValue; // immutable or array of immutables
    private final StatsDimensionsValueParcel mInner;

    /**
     * Creates a {@code StatsDimensionValue} from a parcel.
@@ -90,51 +80,17 @@ public final class StatsDimensionsValue implements Parcelable {
     * @hide
     */
    public StatsDimensionsValue(Parcel in) {
        mField = in.readInt();
        mValueType = in.readInt();
        mValue = readValueFromParcel(mValueType, in);
        mInner = StatsDimensionsValueParcel.CREATOR.createFromParcel(in);
    }

    /**
     * Creates a {@code StatsDimensionsValue} from a StatsDimensionsValueParcel
     * TODO(b/149103391): Make StatsDimensionsValue a wrapper on top of
     * StatsDimensionsValueParcel.
     *
     * @hide
     */
    public StatsDimensionsValue(StatsDimensionsValueParcel parcel) {
        mField = parcel.field;
        mValueType = parcel.valueType;
        switch (mValueType) {
            case STRING_VALUE_TYPE:
                mValue = parcel.stringValue;
                break;
            case INT_VALUE_TYPE:
                mValue = parcel.intValue;
                break;
            case LONG_VALUE_TYPE:
                mValue = parcel.longValue;
                break;
            case BOOLEAN_VALUE_TYPE:
                mValue = parcel.boolValue;
                break;
            case FLOAT_VALUE_TYPE:
                mValue = parcel.floatValue;
                break;
            case TUPLE_VALUE_TYPE:
                StatsDimensionsValue[] values = new StatsDimensionsValue[parcel.tupleValue.length];
                for (int i = 0; i < parcel.tupleValue.length; i++) {
                    values[i] = new StatsDimensionsValue(parcel.tupleValue[i]);
                }
                mValue = values;
                break;
            default:
                Log.w(TAG, "StatsDimensionsValueParcel contains bad valueType: " + mValueType);
                mValue = null;
                break;
        mInner = parcel;
    }
    }


    /**
     * Return the field, i.e. the tag of a statsd atom.
@@ -142,7 +98,7 @@ public final class StatsDimensionsValue implements Parcelable {
     * @return the field
     */
    public int getField() {
        return mField;
        return mInner.field;
    }

    /**
@@ -152,13 +108,13 @@ public final class StatsDimensionsValue implements Parcelable {
     *         null otherwise
     */
    public String getStringValue() {
        try {
            if (mValueType == STRING_VALUE_TYPE) return (String) mValue;
        } catch (ClassCastException e) {
            Log.w(TAG, "Failed to successfully get value", e);
        }
        if (mInner.valueType == STRING_VALUE_TYPE) {
            return mInner.stringValue;
        } else {
            Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not string.");
            return null;
        }
    }

    /**
     * Retrieve the int held, if any.
@@ -166,13 +122,13 @@ public final class StatsDimensionsValue implements Parcelable {
     * @return the int held if {@link #getValueType()} == {@link #INT_VALUE_TYPE}, 0 otherwise
     */
    public int getIntValue() {
        try {
            if (mValueType == INT_VALUE_TYPE) return (Integer) mValue;
        } catch (ClassCastException e) {
            Log.w(TAG, "Failed to successfully get value", e);
        }
        if (mInner.valueType == INT_VALUE_TYPE) {
            return mInner.intValue;
        } else {
            Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not int.");
            return 0;
        }
    }

    /**
     * Retrieve the long held, if any.
@@ -180,13 +136,13 @@ public final class StatsDimensionsValue implements Parcelable {
     * @return the long held if {@link #getValueType()} == {@link #LONG_VALUE_TYPE}, 0 otherwise
     */
    public long getLongValue() {
        try {
            if (mValueType == LONG_VALUE_TYPE) return (Long) mValue;
        } catch (ClassCastException e) {
            Log.w(TAG, "Failed to successfully get value", e);
        }
        if (mInner.valueType == LONG_VALUE_TYPE) {
            return mInner.longValue;
        } else {
            Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not long.");
            return 0;
        }
    }

    /**
     * Retrieve the boolean held, if any.
@@ -195,13 +151,13 @@ public final class StatsDimensionsValue implements Parcelable {
     *         false otherwise
     */
    public boolean getBooleanValue() {
        try {
            if (mValueType == BOOLEAN_VALUE_TYPE) return (Boolean) mValue;
        } catch (ClassCastException e) {
            Log.w(TAG, "Failed to successfully get value", e);
        }
        if (mInner.valueType == BOOLEAN_VALUE_TYPE) {
            return mInner.boolValue;
        } else {
            Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not boolean.");
            return false;
        }
    }

    /**
     * Retrieve the float held, if any.
@@ -209,13 +165,13 @@ public final class StatsDimensionsValue implements Parcelable {
     * @return the float held if {@link #getValueType()} == {@link #FLOAT_VALUE_TYPE}, 0 otherwise
     */
    public float getFloatValue() {
        try {
            if (mValueType == FLOAT_VALUE_TYPE) return (Float) mValue;
        } catch (ClassCastException e) {
            Log.w(TAG, "Failed to successfully get value", e);
        }
        if (mInner.valueType == FLOAT_VALUE_TYPE) {
            return mInner.floatValue;
        } else {
            Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not float.");
            return 0;
        }
    }

    /**
     * Retrieve the tuple, in the form of a {@link List} of {@link StatsDimensionsValue}, held,
@@ -226,19 +182,15 @@ public final class StatsDimensionsValue implements Parcelable {
     *         null otherwise
     */
    public List<StatsDimensionsValue> getTupleValueList() {
        if (mValueType != TUPLE_VALUE_TYPE) {
            return null;
        }
        try {
            StatsDimensionsValue[] orig = (StatsDimensionsValue[]) mValue;
            List<StatsDimensionsValue> copy = new ArrayList<>(orig.length);
            // Shallow copy since StatsDimensionsValue is immutable anyway
            for (int i = 0; i < orig.length; i++) {
                copy.add(orig[i]);
        if (mInner.valueType == TUPLE_VALUE_TYPE) {
            int length = (mInner.tupleValue == null) ? 0 : mInner.tupleValue.length;
            List<StatsDimensionsValue> tupleValues = new ArrayList<>(length);
            for (int i = 0; i < length; i++) {
                tupleValues.add(new StatsDimensionsValue(mInner.tupleValue[i]));
            }
            return copy;
        } catch (ClassCastException e) {
            Log.w(TAG, "Failed to successfully get value", e);
            return tupleValues;
        } else {
            Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not tuple.");
            return null;
        }
    }
@@ -257,7 +209,7 @@ public final class StatsDimensionsValue implements Parcelable {
     * @return the constant representing the type of value stored
     */
    public int getValueType() {
        return mValueType;
        return mInner.valueType;
    }

    /**
@@ -267,7 +219,7 @@ public final class StatsDimensionsValue implements Parcelable {
     * @return true if {@link #getValueType()} is equal to {@code valueType}.
     */
    public boolean isValueType(int valueType) {
        return mValueType == valueType;
        return mInner.valueType == valueType;
    }

    /**
@@ -280,26 +232,40 @@ public final class StatsDimensionsValue implements Parcelable {
     */
    // Follows the format of statsd's dimension.h toString.
    public String toString() {
        try {
        StringBuilder sb = new StringBuilder();
            sb.append(mField);
        sb.append(mInner.field);
        sb.append(":");
            if (mValueType == TUPLE_VALUE_TYPE) {
        switch (mInner.valueType) {
            case STRING_VALUE_TYPE:
                sb.append(mInner.stringValue);
                break;
            case INT_VALUE_TYPE:
                sb.append(String.valueOf(mInner.intValue));
                break;
            case LONG_VALUE_TYPE:
                sb.append(String.valueOf(mInner.longValue));
                break;
            case BOOLEAN_VALUE_TYPE:
                sb.append(String.valueOf(mInner.boolValue));
                break;
            case FLOAT_VALUE_TYPE:
                sb.append(String.valueOf(mInner.floatValue));
                break;
            case TUPLE_VALUE_TYPE:
                sb.append("{");
                StatsDimensionsValue[] sbvs = (StatsDimensionsValue[]) mValue;
                for (int i = 0; i < sbvs.length; i++) {
                    sb.append(sbvs[i].toString());
                int length = (mInner.tupleValue == null) ? 0 : mInner.tupleValue.length;
                for (int i = 0; i < length; i++) {
                    StatsDimensionsValue child = new StatsDimensionsValue(mInner.tupleValue[i]);
                    sb.append(child.toString());
                    sb.append("|");
                }
                sb.append("}");
            } else {
                sb.append(mValue.toString());
                break;
            default:
                Log.w(TAG, "Incorrect value type");
                break;
        }
        return sb.toString();
        } catch (ClassCastException e) {
            Log.w(TAG, "Failed to successfully get value", e);
        }
        return "";
    }

    /**
@@ -324,72 +290,28 @@ public final class StatsDimensionsValue implements Parcelable {

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(mField);
        out.writeInt(mValueType);
        writeValueToParcel(mValueType, mValue, out, flags);
        mInner.writeToParcel(out, flags);
    }

    /** Writes mValue to a parcel. Returns true if succeeds. */
    private static boolean writeValueToParcel(int valueType, Object value, Parcel out, int flags) {
        try {
            switch (valueType) {
                case STRING_VALUE_TYPE:
                    out.writeString((String) value);
                    return true;
                case INT_VALUE_TYPE:
                    out.writeInt((Integer) value);
                    return true;
                case LONG_VALUE_TYPE:
                    out.writeLong((Long) value);
                    return true;
                case BOOLEAN_VALUE_TYPE:
                    out.writeBoolean((Boolean) value);
                    return true;
                case FLOAT_VALUE_TYPE:
                    out.writeFloat((Float) value);
                    return true;
                case TUPLE_VALUE_TYPE: {
                    StatsDimensionsValue[] values = (StatsDimensionsValue[]) value;
                    out.writeInt(values.length);
                    for (int i = 0; i < values.length; i++) {
                        values[i].writeToParcel(out, flags);
                    }
                    return true;
                }
                default:
                    Log.w(TAG, "readValue of an impossible type " + valueType);
                    return false;
            }
        } catch (ClassCastException e) {
            Log.w(TAG, "writeValue cast failed", e);
            return false;
        }
    }

    /** Reads mValue from a parcel. */
    private static Object readValueFromParcel(int valueType, Parcel parcel) {
        switch (valueType) {
    /**
     * Returns a string representation of the type of value stored.
     */
    private String getValueTypeAsString() {
        switch (mInner.valueType) {
            case STRING_VALUE_TYPE:
                return parcel.readString();
                return "string";
            case INT_VALUE_TYPE:
                return parcel.readInt();
                return "int";
            case LONG_VALUE_TYPE:
                return parcel.readLong();
                return "long";
            case BOOLEAN_VALUE_TYPE:
                return parcel.readBoolean();
                return "boolean";
            case FLOAT_VALUE_TYPE:
                return parcel.readFloat();
            case TUPLE_VALUE_TYPE: {
                final int sz = parcel.readInt();
                StatsDimensionsValue[] values = new StatsDimensionsValue[sz];
                for (int i = 0; i < sz; i++) {
                    values[i] = new StatsDimensionsValue(parcel);
                }
                return values;
            }
                return "float";
            case TUPLE_VALUE_TYPE:
                return "tuple";
            default:
                Log.w(TAG, "readValue of an impossible type " + valueType);
                return null;
                return "unknown";
        }
    }
}
+115 −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.
 */

package android.os;

import static com.google.common.truth.Truth.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.List;

@RunWith(JUnit4.class)
public final class StatsDimensionsValueTest {

    @Test
    public void testConversionFromStructuredParcel() {
        int tupleField = 100; // atom id
        String stringValue = "Hello";
        int intValue = 123;
        long longValue = 123456789L;
        float floatValue = 1.1f;
        boolean boolValue = true;

        // Construct structured parcel
        StatsDimensionsValueParcel sdvp = new StatsDimensionsValueParcel();
        sdvp.field = tupleField;
        sdvp.valueType = StatsDimensionsValue.TUPLE_VALUE_TYPE;
        sdvp.tupleValue = new StatsDimensionsValueParcel[5];

        for (int i = 0; i < 5; i++) {
            sdvp.tupleValue[i] = new StatsDimensionsValueParcel();
            sdvp.tupleValue[i].field = i + 1;
        }

        sdvp.tupleValue[0].valueType = StatsDimensionsValue.STRING_VALUE_TYPE;
        sdvp.tupleValue[1].valueType = StatsDimensionsValue.INT_VALUE_TYPE;
        sdvp.tupleValue[2].valueType = StatsDimensionsValue.LONG_VALUE_TYPE;
        sdvp.tupleValue[3].valueType = StatsDimensionsValue.FLOAT_VALUE_TYPE;
        sdvp.tupleValue[4].valueType = StatsDimensionsValue.BOOLEAN_VALUE_TYPE;

        sdvp.tupleValue[0].stringValue = stringValue;
        sdvp.tupleValue[1].intValue = intValue;
        sdvp.tupleValue[2].longValue = longValue;
        sdvp.tupleValue[3].floatValue = floatValue;
        sdvp.tupleValue[4].boolValue = boolValue;

        // Convert to StatsDimensionsValue and check result
        StatsDimensionsValue sdv = new StatsDimensionsValue(sdvp);

        assertThat(sdv.getField()).isEqualTo(tupleField);
        assertThat(sdv.getValueType()).isEqualTo(StatsDimensionsValue.TUPLE_VALUE_TYPE);
        List<StatsDimensionsValue> sdvChildren = sdv.getTupleValueList();
        assertThat(sdvChildren.size()).isEqualTo(5);

        for (int i = 0; i < 5; i++) {
            assertThat(sdvChildren.get(i).getField()).isEqualTo(i + 1);
        }

        assertThat(sdvChildren.get(0).getValueType())
              .isEqualTo(StatsDimensionsValue.STRING_VALUE_TYPE);
        assertThat(sdvChildren.get(1).getValueType())
              .isEqualTo(StatsDimensionsValue.INT_VALUE_TYPE);
        assertThat(sdvChildren.get(2).getValueType())
              .isEqualTo(StatsDimensionsValue.LONG_VALUE_TYPE);
        assertThat(sdvChildren.get(3).getValueType())
              .isEqualTo(StatsDimensionsValue.FLOAT_VALUE_TYPE);
        assertThat(sdvChildren.get(4).getValueType())
              .isEqualTo(StatsDimensionsValue.BOOLEAN_VALUE_TYPE);

        assertThat(sdvChildren.get(0).getStringValue()).isEqualTo(stringValue);
        assertThat(sdvChildren.get(1).getIntValue()).isEqualTo(intValue);
        assertThat(sdvChildren.get(2).getLongValue()).isEqualTo(longValue);
        assertThat(sdvChildren.get(3).getFloatValue()).isEqualTo(floatValue);
        assertThat(sdvChildren.get(4).getBooleanValue()).isEqualTo(boolValue);

        // Ensure that StatsDimensionsValue and StatsDimensionsValueParcel are
        // parceled equivalently
        Parcel sdvpParcel = Parcel.obtain();
        Parcel sdvParcel = Parcel.obtain();
        sdvp.writeToParcel(sdvpParcel, 0);
        sdv.writeToParcel(sdvParcel, 0);
        assertThat(sdvpParcel.dataSize()).isEqualTo(sdvParcel.dataSize());
    }

    @Test
    public void testNullTupleArray() {
        int tupleField = 100; // atom id

        StatsDimensionsValueParcel parcel = new StatsDimensionsValueParcel();
        parcel.field = tupleField;
        parcel.valueType = StatsDimensionsValue.TUPLE_VALUE_TYPE;
        parcel.tupleValue = null;

        StatsDimensionsValue sdv = new StatsDimensionsValue(parcel);
        assertThat(sdv.getField()).isEqualTo(tupleField);
        assertThat(sdv.getValueType()).isEqualTo(StatsDimensionsValue.TUPLE_VALUE_TYPE);
        List<StatsDimensionsValue> sdvChildren = sdv.getTupleValueList();
        assertThat(sdvChildren.size()).isEqualTo(0);
    }
}