/* * Copyright 2018 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 android.annotation.SystemApi; import android.util.Log; import java.util.ArrayList; import java.util.List; /** * Container for statsd dimension value information, corresponding to a * stats_log.proto's DimensionValue. * * This consists of a field (an int representing a statsd atom field) * and a value (which may be one of a number of types). * *

* Only a single value is held, and it is necessarily one of the following types: * {@link String}, int, long, boolean, float, * or tuple (i.e. {@link List} of {@code StatsDimensionsValue}). * * The type of value held can be retrieved using {@link #getValueType()}, which returns one of the * following ints, depending on the type of value: *

* Alternatively, this can be determined using {@link #isValueType(int)} with one of these constants * as a parameter. * The value itself can be retrieved using the correct get...Value() function for its type. * *

* The field is always an int, and always exists; it can be obtained using {@link #getField()}. * * * @hide */ @SystemApi 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 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. */ public static final int INT_VALUE_TYPE = 3; /** Indicates that this holds a long. */ public static final int LONG_VALUE_TYPE = 4; /** Indicates that this holds a boolean. */ public static final int BOOLEAN_VALUE_TYPE = 5; /** Indicates that this holds a float. */ public static final int FLOAT_VALUE_TYPE = 6; /** Indicates that this holds a List of StatsDimensionsValues. */ public static final int TUPLE_VALUE_TYPE = 7; private final StatsDimensionsValueParcel mInner; /** * Creates a {@code StatsDimensionValue} from a parcel. * * @hide */ public StatsDimensionsValue(Parcel in) { mInner = StatsDimensionsValueParcel.CREATOR.createFromParcel(in); } /** * Creates a {@code StatsDimensionsValue} from a StatsDimensionsValueParcel * * @hide */ public StatsDimensionsValue(StatsDimensionsValueParcel parcel) { mInner = parcel; } /** * Return the field, i.e. the tag of a statsd atom. * * @return the field */ public int getField() { return mInner.field; } /** * Retrieve the String held, if any. * * @return the {@link String} held if {@link #getValueType()} == {@link #STRING_VALUE_TYPE}, * null otherwise */ public String getStringValue() { 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. * * @return the int held if {@link #getValueType()} == {@link #INT_VALUE_TYPE}, 0 otherwise */ public int getIntValue() { 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. * * @return the long held if {@link #getValueType()} == {@link #LONG_VALUE_TYPE}, 0 otherwise */ public long getLongValue() { 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. * * @return the boolean held if {@link #getValueType()} == {@link #BOOLEAN_VALUE_TYPE}, * false otherwise */ public boolean getBooleanValue() { 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. * * @return the float held if {@link #getValueType()} == {@link #FLOAT_VALUE_TYPE}, 0 otherwise */ public float getFloatValue() { 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, * if any. * * @return the {@link List} of {@link StatsDimensionsValue} held * if {@link #getValueType()} == {@link #TUPLE_VALUE_TYPE}, * null otherwise */ public List getTupleValueList() { 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 tupleValues; } else { Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not tuple."); return null; } } /** * Returns the constant representing the type of value stored, namely one of *

* * @return the constant representing the type of value stored */ public int getValueType() { return mInner.valueType; } /** * Returns whether the type of value stored is equal to the given type. * * @param valueType int representing the type of value stored, as used in {@link #getValueType} * @return true if {@link #getValueType()} is equal to {@code valueType}. */ public boolean isValueType(int valueType) { return mInner.valueType == valueType; } /** * Returns a String representing the information in this StatsDimensionValue. * No guarantees are made about the format of this String. * * @return String representation * * @hide */ // Follows the format of statsd's dimension.h toString. public String toString() { 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("{"); 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("}"); break; default: Log.w(TAG, "Incorrect value type"); break; } return sb.toString(); } /** * Parcelable Creator for StatsDimensionsValue. */ public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public StatsDimensionsValue createFromParcel(Parcel in) { return new StatsDimensionsValue(in); } public StatsDimensionsValue[] newArray(int size) { return new StatsDimensionsValue[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { mInner.writeToParcel(out, flags); } /** * Returns a string representation of the type of value stored. */ private String getValueTypeAsString() { switch (mInner.valueType) { case STRING_VALUE_TYPE: return "string"; case INT_VALUE_TYPE: return "int"; case LONG_VALUE_TYPE: return "long"; case BOOLEAN_VALUE_TYPE: return "boolean"; case FLOAT_VALUE_TYPE: return "float"; case TUPLE_VALUE_TYPE: return "tuple"; default: return "unknown"; } } }