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