diff --git a/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl b/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl index a8685e34dd52a..05f78d00348e6 100644 --- a/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl +++ b/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl @@ -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; diff --git a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java index 35273da964134..7d9349cefa48f 100644 --- a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java +++ b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java @@ -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,59 +80,25 @@ 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. * * @return the field */ public int getField() { - return mField; + return mInner.field; } /** @@ -152,12 +108,12 @@ 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; } - return null; } /** @@ -166,12 +122,12 @@ 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; } - return 0; } /** @@ -180,12 +136,12 @@ 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; } - return 0; } /** @@ -195,12 +151,12 @@ 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; } - return false; } /** @@ -209,12 +165,12 @@ 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; } - return 0; } /** @@ -226,19 +182,15 @@ public final class StatsDimensionsValue implements Parcelable { * null otherwise */ public List getTupleValueList() { - if (mValueType != TUPLE_VALUE_TYPE) { - return null; - } - try { - StatsDimensionsValue[] orig = (StatsDimensionsValue[]) mValue; - List 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 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(":"); - if (mValueType == TUPLE_VALUE_TYPE) { + StringBuilder sb = new StringBuilder(); + sb.append(mInner.field); + sb.append(":"); + 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()); - } - return sb.toString(); - } catch (ClassCastException e) { - Log.w(TAG, "Failed to successfully get value", e); + break; + default: + Log.w(TAG, "Incorrect value type"); + break; } - return ""; + return sb.toString(); } /** @@ -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"; } } } diff --git a/apex/statsd/framework/test/src/android/os/StatsDimensionsValueTest.java b/apex/statsd/framework/test/src/android/os/StatsDimensionsValueTest.java new file mode 100644 index 0000000000000..db25911e6eee7 --- /dev/null +++ b/apex/statsd/framework/test/src/android/os/StatsDimensionsValueTest.java @@ -0,0 +1,115 @@ +/* + * 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 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 sdvChildren = sdv.getTupleValueList(); + assertThat(sdvChildren.size()).isEqualTo(0); + } +}