1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.os; 17 18 import android.annotation.SystemApi; 19 import android.util.Slog; 20 21 import java.util.ArrayList; 22 import java.util.List; 23 24 /** 25 * Container for statsd dimension value information, corresponding to a 26 * stats_log.proto's DimensionValue. 27 * 28 * This consists of a field (an int representing a statsd atom field) 29 * and a value (which may be one of a number of types). 30 * 31 * <p> 32 * Only a single value is held, and it is necessarily one of the following types: 33 * {@link String}, int, long, boolean, float, 34 * or tuple (i.e. {@link List} of {@code StatsDimensionsValue}). 35 * 36 * The type of value held can be retrieved using {@link #getValueType()}, which returns one of the 37 * following ints, depending on the type of value: 38 * <ul> 39 * <li>{@link #STRING_VALUE_TYPE}</li> 40 * <li>{@link #INT_VALUE_TYPE}</li> 41 * <li>{@link #LONG_VALUE_TYPE}</li> 42 * <li>{@link #BOOLEAN_VALUE_TYPE}</li> 43 * <li>{@link #FLOAT_VALUE_TYPE}</li> 44 * <li>{@link #TUPLE_VALUE_TYPE}</li> 45 * </ul> 46 * Alternatively, this can be determined using {@link #isValueType(int)} with one of these constants 47 * as a parameter. 48 * The value itself can be retrieved using the correct get...Value() function for its type. 49 * 50 * <p> 51 * The field is always an int, and always exists; it can be obtained using {@link #getField()}. 52 * 53 * 54 * @hide 55 */ 56 @SystemApi 57 public final class StatsDimensionsValue implements Parcelable { 58 private static final String TAG = "StatsDimensionsValue"; 59 60 // Values of the value type correspond to stats_log.proto's DimensionValue fields. 61 // Keep constants in sync with services/include/android/os/StatsDimensionsValue.h. 62 /** Indicates that this holds a String. */ 63 public static final int STRING_VALUE_TYPE = 2; 64 /** Indicates that this holds an int. */ 65 public static final int INT_VALUE_TYPE = 3; 66 /** Indicates that this holds a long. */ 67 public static final int LONG_VALUE_TYPE = 4; 68 /** Indicates that this holds a boolean. */ 69 public static final int BOOLEAN_VALUE_TYPE = 5; 70 /** Indicates that this holds a float. */ 71 public static final int FLOAT_VALUE_TYPE = 6; 72 /** Indicates that this holds a List of StatsDimensionsValues. */ 73 public static final int TUPLE_VALUE_TYPE = 7; 74 75 /** Value of a stats_log.proto DimensionsValue.field. */ 76 private final int mField; 77 78 /** Type of stats_log.proto DimensionsValue.value, according to the VALUE_TYPEs above. */ 79 private final int mValueType; 80 81 /** 82 * Value of a stats_log.proto DimensionsValue.value. 83 * String, Integer, Long, Boolean, Float, or StatsDimensionsValue[]. 84 */ 85 private final Object mValue; // immutable or array of immutables 86 87 /** 88 * Creates a {@code StatsDimensionValue} from a parcel. 89 * 90 * @hide 91 */ StatsDimensionsValue(Parcel in)92 public StatsDimensionsValue(Parcel in) { 93 mField = in.readInt(); 94 mValueType = in.readInt(); 95 mValue = readValueFromParcel(mValueType, in); 96 } 97 98 /** 99 * Return the field, i.e. the tag of a statsd atom. 100 * 101 * @return the field 102 */ getField()103 public int getField() { 104 return mField; 105 } 106 107 /** 108 * Retrieve the String held, if any. 109 * 110 * @return the {@link String} held if {@link #getValueType()} == {@link #STRING_VALUE_TYPE}, 111 * null otherwise 112 */ getStringValue()113 public String getStringValue() { 114 try { 115 if (mValueType == STRING_VALUE_TYPE) return (String) mValue; 116 } catch (ClassCastException e) { 117 Slog.w(TAG, "Failed to successfully get value", e); 118 } 119 return null; 120 } 121 122 /** 123 * Retrieve the int held, if any. 124 * 125 * @return the int held if {@link #getValueType()} == {@link #INT_VALUE_TYPE}, 0 otherwise 126 */ getIntValue()127 public int getIntValue() { 128 try { 129 if (mValueType == INT_VALUE_TYPE) return (Integer) mValue; 130 } catch (ClassCastException e) { 131 Slog.w(TAG, "Failed to successfully get value", e); 132 } 133 return 0; 134 } 135 136 /** 137 * Retrieve the long held, if any. 138 * 139 * @return the long held if {@link #getValueType()} == {@link #LONG_VALUE_TYPE}, 0 otherwise 140 */ getLongValue()141 public long getLongValue() { 142 try { 143 if (mValueType == LONG_VALUE_TYPE) return (Long) mValue; 144 } catch (ClassCastException e) { 145 Slog.w(TAG, "Failed to successfully get value", e); 146 } 147 return 0; 148 } 149 150 /** 151 * Retrieve the boolean held, if any. 152 * 153 * @return the boolean held if {@link #getValueType()} == {@link #BOOLEAN_VALUE_TYPE}, 154 * false otherwise 155 */ getBooleanValue()156 public boolean getBooleanValue() { 157 try { 158 if (mValueType == BOOLEAN_VALUE_TYPE) return (Boolean) mValue; 159 } catch (ClassCastException e) { 160 Slog.w(TAG, "Failed to successfully get value", e); 161 } 162 return false; 163 } 164 165 /** 166 * Retrieve the float held, if any. 167 * 168 * @return the float held if {@link #getValueType()} == {@link #FLOAT_VALUE_TYPE}, 0 otherwise 169 */ getFloatValue()170 public float getFloatValue() { 171 try { 172 if (mValueType == FLOAT_VALUE_TYPE) return (Float) mValue; 173 } catch (ClassCastException e) { 174 Slog.w(TAG, "Failed to successfully get value", e); 175 } 176 return 0; 177 } 178 179 /** 180 * Retrieve the tuple, in the form of a {@link List} of {@link StatsDimensionsValue}, held, 181 * if any. 182 * 183 * @return the {@link List} of {@link StatsDimensionsValue} held 184 * if {@link #getValueType()} == {@link #TUPLE_VALUE_TYPE}, 185 * null otherwise 186 */ getTupleValueList()187 public List<StatsDimensionsValue> getTupleValueList() { 188 if (mValueType != TUPLE_VALUE_TYPE) { 189 return null; 190 } 191 try { 192 StatsDimensionsValue[] orig = (StatsDimensionsValue[]) mValue; 193 List<StatsDimensionsValue> copy = new ArrayList<>(orig.length); 194 // Shallow copy since StatsDimensionsValue is immutable anyway 195 for (int i = 0; i < orig.length; i++) { 196 copy.add(orig[i]); 197 } 198 return copy; 199 } catch (ClassCastException e) { 200 Slog.w(TAG, "Failed to successfully get value", e); 201 return null; 202 } 203 } 204 205 /** 206 * Returns the constant representing the type of value stored, namely one of 207 * <ul> 208 * <li>{@link #STRING_VALUE_TYPE}</li> 209 * <li>{@link #INT_VALUE_TYPE}</li> 210 * <li>{@link #LONG_VALUE_TYPE}</li> 211 * <li>{@link #BOOLEAN_VALUE_TYPE}</li> 212 * <li>{@link #FLOAT_VALUE_TYPE}</li> 213 * <li>{@link #TUPLE_VALUE_TYPE}</li> 214 * </ul> 215 * 216 * @return the constant representing the type of value stored 217 */ getValueType()218 public int getValueType() { 219 return mValueType; 220 } 221 222 /** 223 * Returns whether the type of value stored is equal to the given type. 224 * 225 * @param valueType int representing the type of value stored, as used in {@link #getValueType} 226 * @return true if {@link #getValueType()} is equal to {@code valueType}. 227 */ isValueType(int valueType)228 public boolean isValueType(int valueType) { 229 return mValueType == valueType; 230 } 231 232 /** 233 * Returns a String representing the information in this StatsDimensionValue. 234 * No guarantees are made about the format of this String. 235 * 236 * @return String representation 237 * 238 * @hide 239 */ 240 // Follows the format of statsd's dimension.h toString. toString()241 public String toString() { 242 try { 243 StringBuilder sb = new StringBuilder(); 244 sb.append(mField); 245 sb.append(":"); 246 if (mValueType == TUPLE_VALUE_TYPE) { 247 sb.append("{"); 248 StatsDimensionsValue[] sbvs = (StatsDimensionsValue[]) mValue; 249 for (int i = 0; i < sbvs.length; i++) { 250 sb.append(sbvs[i].toString()); 251 sb.append("|"); 252 } 253 sb.append("}"); 254 } else { 255 sb.append(mValue.toString()); 256 } 257 return sb.toString(); 258 } catch (ClassCastException e) { 259 Slog.w(TAG, "Failed to successfully get value", e); 260 } 261 return ""; 262 } 263 264 /** 265 * Parcelable Creator for StatsDimensionsValue. 266 */ 267 public static final Parcelable.Creator<StatsDimensionsValue> CREATOR = new 268 Parcelable.Creator<StatsDimensionsValue>() { 269 public StatsDimensionsValue createFromParcel(Parcel in) { 270 return new StatsDimensionsValue(in); 271 } 272 273 public StatsDimensionsValue[] newArray(int size) { 274 return new StatsDimensionsValue[size]; 275 } 276 }; 277 278 @Override describeContents()279 public int describeContents() { 280 return 0; 281 } 282 283 @Override writeToParcel(Parcel out, int flags)284 public void writeToParcel(Parcel out, int flags) { 285 out.writeInt(mField); 286 out.writeInt(mValueType); 287 writeValueToParcel(mValueType, mValue, out, flags); 288 } 289 290 /** Writes mValue to a parcel. Returns true if succeeds. */ writeValueToParcel(int valueType, Object value, Parcel out, int flags)291 private static boolean writeValueToParcel(int valueType, Object value, Parcel out, int flags) { 292 try { 293 switch (valueType) { 294 case STRING_VALUE_TYPE: 295 out.writeString((String) value); 296 return true; 297 case INT_VALUE_TYPE: 298 out.writeInt((Integer) value); 299 return true; 300 case LONG_VALUE_TYPE: 301 out.writeLong((Long) value); 302 return true; 303 case BOOLEAN_VALUE_TYPE: 304 out.writeBoolean((Boolean) value); 305 return true; 306 case FLOAT_VALUE_TYPE: 307 out.writeFloat((Float) value); 308 return true; 309 case TUPLE_VALUE_TYPE: { 310 StatsDimensionsValue[] values = (StatsDimensionsValue[]) value; 311 out.writeInt(values.length); 312 for (int i = 0; i < values.length; i++) { 313 values[i].writeToParcel(out, flags); 314 } 315 return true; 316 } 317 default: 318 Slog.w(TAG, "readValue of an impossible type " + valueType); 319 return false; 320 } 321 } catch (ClassCastException e) { 322 Slog.w(TAG, "writeValue cast failed", e); 323 return false; 324 } 325 } 326 327 /** Reads mValue from a parcel. */ readValueFromParcel(int valueType, Parcel parcel)328 private static Object readValueFromParcel(int valueType, Parcel parcel) { 329 switch (valueType) { 330 case STRING_VALUE_TYPE: 331 return parcel.readString(); 332 case INT_VALUE_TYPE: 333 return parcel.readInt(); 334 case LONG_VALUE_TYPE: 335 return parcel.readLong(); 336 case BOOLEAN_VALUE_TYPE: 337 return parcel.readBoolean(); 338 case FLOAT_VALUE_TYPE: 339 return parcel.readFloat(); 340 case TUPLE_VALUE_TYPE: { 341 final int sz = parcel.readInt(); 342 StatsDimensionsValue[] values = new StatsDimensionsValue[sz]; 343 for (int i = 0; i < sz; i++) { 344 values[i] = new StatsDimensionsValue(parcel); 345 } 346 return values; 347 } 348 default: 349 Slog.w(TAG, "readValue of an impossible type " + valueType); 350 return null; 351 } 352 } 353 } 354