Merge "Wrap StatsDimensionsValue over structured parcel" into rvc-dev
diff --git a/apex/aidl/android/os/StatsDimensionsValueParcel.aidl b/apex/aidl/android/os/StatsDimensionsValueParcel.aidl
index a8685e3..05f78d0 100644
--- a/apex/aidl/android/os/StatsDimensionsValueParcel.aidl
+++ b/apex/aidl/android/os/StatsDimensionsValueParcel.aidl
@@ -4,12 +4,12 @@
* @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/framework/java/android/os/StatsDimensionsValue.java b/apex/framework/java/android/os/StatsDimensionsValue.java
index 35273da..7d9349c 100644
--- a/apex/framework/java/android/os/StatsDimensionsValue.java
+++ b/apex/framework/java/android/os/StatsDimensionsValue.java
@@ -58,7 +58,7 @@
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 @@
/** 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 @@
* @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 @@
* 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 @@
* @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 @@
* @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 @@
* 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 @@
* @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 @@
* 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 @@
* @return the constant representing the type of value stored
*/
public int getValueType() {
- return mValueType;
+ return mInner.valueType;
}
/**
@@ -267,7 +219,7 @@
* @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 @@
*/
// 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 @@
@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/framework/test/src/android/os/StatsDimensionsValueTest.java b/apex/framework/test/src/android/os/StatsDimensionsValueTest.java
new file mode 100644
index 0000000..db25911
--- /dev/null
+++ b/apex/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<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);
+ }
+}