blob: da13ea13d5faacb0a502f124c79a76472806a620 [file] [log] [blame]
/*
* 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.Slog;
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).
*
* <p>
* 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:
* <ul>
* <li>{@link #STRING_VALUE_TYPE}</li>
* <li>{@link #INT_VALUE_TYPE}</li>
* <li>{@link #LONG_VALUE_TYPE}</li>
* <li>{@link #BOOLEAN_VALUE_TYPE}</li>
* <li>{@link #FLOAT_VALUE_TYPE}</li>
* <li>{@link #TUPLE_VALUE_TYPE}</li>
* </ul>
* 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.
*
* <p>
* 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 services/include/android/os/StatsDimensionsValue.h.
/** 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;
/** 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
/**
* Creates a {@code StatsDimensionValue} from a parcel.
*
* @hide
*/
public StatsDimensionsValue(Parcel in) {
mField = in.readInt();
mValueType = in.readInt();
mValue = readValueFromParcel(mValueType, in);
}
/**
* Return the field, i.e. the tag of a statsd atom.
*
* @return the field
*/
public int getField() {
return mField;
}
/**
* Retrieve the String held, if any.
*
* @return the {@link String} held if {@link #getValueType()} == {@link #STRING_VALUE_TYPE},
* null otherwise
*/
public String getStringValue() {
try {
if (mValueType == STRING_VALUE_TYPE) return (String) mValue;
} catch (ClassCastException e) {
Slog.w(TAG, "Failed to successfully get value", e);
}
return null;
}
/**
* Retrieve the int held, if any.
*
* @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) {
Slog.w(TAG, "Failed to successfully get value", e);
}
return 0;
}
/**
* Retrieve the long held, if any.
*
* @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) {
Slog.w(TAG, "Failed to successfully get value", e);
}
return 0;
}
/**
* Retrieve the boolean held, if any.
*
* @return the boolean held if {@link #getValueType()} == {@link #BOOLEAN_VALUE_TYPE},
* false otherwise
*/
public boolean getBooleanValue() {
try {
if (mValueType == BOOLEAN_VALUE_TYPE) return (Boolean) mValue;
} catch (ClassCastException e) {
Slog.w(TAG, "Failed to successfully get value", e);
}
return false;
}
/**
* Retrieve the float held, if any.
*
* @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) {
Slog.w(TAG, "Failed to successfully get value", e);
}
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<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]);
}
return copy;
} catch (ClassCastException e) {
Slog.w(TAG, "Failed to successfully get value", e);
return null;
}
}
/**
* Returns the constant representing the type of value stored, namely one of
* <ul>
* <li>{@link #STRING_VALUE_TYPE}</li>
* <li>{@link #INT_VALUE_TYPE}</li>
* <li>{@link #LONG_VALUE_TYPE}</li>
* <li>{@link #BOOLEAN_VALUE_TYPE}</li>
* <li>{@link #FLOAT_VALUE_TYPE}</li>
* <li>{@link #TUPLE_VALUE_TYPE}</li>
* </ul>
*
* @return the constant representing the type of value stored
*/
public int getValueType() {
return mValueType;
}
/**
* 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 mValueType == 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() {
try {
StringBuilder sb = new StringBuilder();
sb.append(mField);
sb.append(":");
if (mValueType == TUPLE_VALUE_TYPE) {
sb.append("{");
StatsDimensionsValue[] sbvs = (StatsDimensionsValue[]) mValue;
for (int i = 0; i < sbvs.length; i++) {
sb.append(sbvs[i].toString());
sb.append("|");
}
sb.append("}");
} else {
sb.append(mValue.toString());
}
return sb.toString();
} catch (ClassCastException e) {
Slog.w(TAG, "Failed to successfully get value", e);
}
return "";
}
/**
* Parcelable Creator for StatsDimensionsValue.
*/
public static final @android.annotation.NonNull Parcelable.Creator<StatsDimensionsValue> CREATOR = new
Parcelable.Creator<StatsDimensionsValue>() {
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) {
out.writeInt(mField);
out.writeInt(mValueType);
writeValueToParcel(mValueType, mValue, 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:
Slog.w(TAG, "readValue of an impossible type " + valueType);
return false;
}
} catch (ClassCastException e) {
Slog.w(TAG, "writeValue cast failed", e);
return false;
}
}
/** Reads mValue from a parcel. */
private static Object readValueFromParcel(int valueType, Parcel parcel) {
switch (valueType) {
case STRING_VALUE_TYPE:
return parcel.readString();
case INT_VALUE_TYPE:
return parcel.readInt();
case LONG_VALUE_TYPE:
return parcel.readLong();
case BOOLEAN_VALUE_TYPE:
return parcel.readBoolean();
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;
}
default:
Slog.w(TAG, "readValue of an impossible type " + valueType);
return null;
}
}
}