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.Log;
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 frameworks/base/cmds/statsd/src/HashableDimensionKey.cpp.
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     private final StatsDimensionsValueParcel mInner;
76 
77     /**
78      * Creates a {@code StatsDimensionValue} from a parcel.
79      *
80      * @hide
81      */
StatsDimensionsValue(Parcel in)82     public StatsDimensionsValue(Parcel in) {
83         mInner = StatsDimensionsValueParcel.CREATOR.createFromParcel(in);
84     }
85 
86     /**
87      * Creates a {@code StatsDimensionsValue} from a StatsDimensionsValueParcel
88      *
89      * @hide
90      */
StatsDimensionsValue(StatsDimensionsValueParcel parcel)91     public StatsDimensionsValue(StatsDimensionsValueParcel parcel) {
92         mInner = parcel;
93     }
94 
95     /**
96      * Return the field, i.e. the tag of a statsd atom.
97      *
98      * @return the field
99      */
getField()100     public int getField() {
101         return mInner.field;
102     }
103 
104     /**
105      * Retrieve the String held, if any.
106      *
107      * @return the {@link String} held if {@link #getValueType()} == {@link #STRING_VALUE_TYPE},
108      *         null otherwise
109      */
getStringValue()110     public String getStringValue() {
111         if (mInner.valueType == STRING_VALUE_TYPE) {
112             return mInner.stringValue;
113         } else {
114             Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not string.");
115             return null;
116         }
117     }
118 
119     /**
120      * Retrieve the int held, if any.
121      *
122      * @return the int held if {@link #getValueType()} == {@link #INT_VALUE_TYPE}, 0 otherwise
123      */
getIntValue()124     public int getIntValue() {
125         if (mInner.valueType == INT_VALUE_TYPE) {
126             return mInner.intValue;
127         } else {
128             Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not int.");
129             return 0;
130         }
131     }
132 
133     /**
134      * Retrieve the long held, if any.
135      *
136      * @return the long held if {@link #getValueType()} == {@link #LONG_VALUE_TYPE}, 0 otherwise
137      */
getLongValue()138     public long getLongValue() {
139         if (mInner.valueType == LONG_VALUE_TYPE) {
140             return mInner.longValue;
141         } else {
142             Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not long.");
143             return 0;
144         }
145     }
146 
147     /**
148      * Retrieve the boolean held, if any.
149      *
150      * @return the boolean held if {@link #getValueType()} == {@link #BOOLEAN_VALUE_TYPE},
151      *         false otherwise
152      */
getBooleanValue()153     public boolean getBooleanValue() {
154         if (mInner.valueType == BOOLEAN_VALUE_TYPE) {
155             return mInner.boolValue;
156         } else {
157             Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not boolean.");
158             return false;
159         }
160     }
161 
162     /**
163      * Retrieve the float held, if any.
164      *
165      * @return the float held if {@link #getValueType()} == {@link #FLOAT_VALUE_TYPE}, 0 otherwise
166      */
getFloatValue()167     public float getFloatValue() {
168         if (mInner.valueType == FLOAT_VALUE_TYPE) {
169             return mInner.floatValue;
170         } else {
171             Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not float.");
172             return 0;
173         }
174     }
175 
176     /**
177      * Retrieve the tuple, in the form of a {@link List} of {@link StatsDimensionsValue}, held,
178      * if any.
179      *
180      * @return the {@link List} of {@link StatsDimensionsValue} held
181      *         if {@link #getValueType()} == {@link #TUPLE_VALUE_TYPE},
182      *         null otherwise
183      */
getTupleValueList()184     public List<StatsDimensionsValue> getTupleValueList() {
185         if (mInner.valueType == TUPLE_VALUE_TYPE) {
186             int length = (mInner.tupleValue == null) ? 0 : mInner.tupleValue.length;
187             List<StatsDimensionsValue> tupleValues = new ArrayList<>(length);
188             for (int i = 0; i < length; i++) {
189                 tupleValues.add(new StatsDimensionsValue(mInner.tupleValue[i]));
190             }
191             return tupleValues;
192         } else {
193             Log.w(TAG, "Value type is " + getValueTypeAsString() + ", not tuple.");
194             return null;
195         }
196     }
197 
198     /**
199      * Returns the constant representing the type of value stored, namely one of
200      * <ul>
201      *   <li>{@link #STRING_VALUE_TYPE}</li>
202      *   <li>{@link #INT_VALUE_TYPE}</li>
203      *   <li>{@link #LONG_VALUE_TYPE}</li>
204      *   <li>{@link #BOOLEAN_VALUE_TYPE}</li>
205      *   <li>{@link #FLOAT_VALUE_TYPE}</li>
206      *   <li>{@link #TUPLE_VALUE_TYPE}</li>
207      * </ul>
208      *
209      * @return the constant representing the type of value stored
210      */
getValueType()211     public int getValueType() {
212         return mInner.valueType;
213     }
214 
215     /**
216      * Returns whether the type of value stored is equal to the given type.
217      *
218      * @param valueType int representing the type of value stored, as used in {@link #getValueType}
219      * @return true if {@link #getValueType()} is equal to {@code valueType}.
220      */
isValueType(int valueType)221     public boolean isValueType(int valueType) {
222         return mInner.valueType == valueType;
223     }
224 
225     /**
226      * Returns a String representing the information in this StatsDimensionValue.
227      * No guarantees are made about the format of this String.
228      *
229      * @return String representation
230      *
231      * @hide
232      */
233     // Follows the format of statsd's dimension.h toString.
toString()234     public String toString() {
235         StringBuilder sb = new StringBuilder();
236         sb.append(mInner.field);
237         sb.append(":");
238         switch (mInner.valueType) {
239             case STRING_VALUE_TYPE:
240                 sb.append(mInner.stringValue);
241                 break;
242             case INT_VALUE_TYPE:
243                 sb.append(String.valueOf(mInner.intValue));
244                 break;
245             case LONG_VALUE_TYPE:
246                 sb.append(String.valueOf(mInner.longValue));
247                 break;
248             case BOOLEAN_VALUE_TYPE:
249                 sb.append(String.valueOf(mInner.boolValue));
250                 break;
251             case FLOAT_VALUE_TYPE:
252                 sb.append(String.valueOf(mInner.floatValue));
253                 break;
254             case TUPLE_VALUE_TYPE:
255                 sb.append("{");
256                 int length = (mInner.tupleValue == null) ? 0 : mInner.tupleValue.length;
257                 for (int i = 0; i < length; i++) {
258                     StatsDimensionsValue child = new StatsDimensionsValue(mInner.tupleValue[i]);
259                     sb.append(child.toString());
260                     sb.append("|");
261                 }
262                 sb.append("}");
263                 break;
264             default:
265                 Log.w(TAG, "Incorrect value type");
266                 break;
267         }
268         return sb.toString();
269     }
270 
271     /**
272      * Parcelable Creator for StatsDimensionsValue.
273      */
274     public static final @android.annotation.NonNull
275             Parcelable.Creator<StatsDimensionsValue> CREATOR = new
276             Parcelable.Creator<StatsDimensionsValue>() {
277                 public StatsDimensionsValue createFromParcel(Parcel in) {
278                     return new StatsDimensionsValue(in);
279                 }
280 
281                 public StatsDimensionsValue[] newArray(int size) {
282                     return new StatsDimensionsValue[size];
283                 }
284             };
285 
286     @Override
describeContents()287     public int describeContents() {
288         return 0;
289     }
290 
291     @Override
writeToParcel(Parcel out, int flags)292     public void writeToParcel(Parcel out, int flags) {
293         mInner.writeToParcel(out, flags);
294     }
295 
296     /**
297      * Returns a string representation of the type of value stored.
298      */
getValueTypeAsString()299     private String getValueTypeAsString() {
300         switch (mInner.valueType) {
301             case STRING_VALUE_TYPE:
302                 return "string";
303             case INT_VALUE_TYPE:
304                 return "int";
305             case LONG_VALUE_TYPE:
306                 return "long";
307             case BOOLEAN_VALUE_TYPE:
308                 return "boolean";
309             case FLOAT_VALUE_TYPE:
310                 return "float";
311             case TUPLE_VALUE_TYPE:
312                 return "tuple";
313             default:
314                 return "unknown";
315         }
316     }
317 }
318