1 /*
2  * Copyright (C) 2017 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.util.Slog;
19 
20 import java.util.ArrayList;
21 import java.util.List;
22 
23 /**
24  * Wrapper class for sending data from Android OS to StatsD.
25  *
26  * @hide
27  */
28 public final class StatsLogEventWrapper implements Parcelable {
29     static final boolean DEBUG = false;
30     static final String TAG = "StatsLogEventWrapper";
31 
32     // Keep in sync with FieldValue.h enums
33     private static final int EVENT_TYPE_UNKNOWN = 0;
34     private static final int EVENT_TYPE_INT = 1; /* int32_t */
35     private static final int EVENT_TYPE_LONG = 2; /* int64_t */
36     private static final int EVENT_TYPE_FLOAT = 3;
37     private static final int EVENT_TYPE_DOUBLE = 4;
38     private static final int EVENT_TYPE_STRING = 5;
39     private static final int EVENT_TYPE_STORAGE = 6;
40 
41     List<Integer> mTypes = new ArrayList<>();
42     List<Object> mValues = new ArrayList<>();
43     int mTag;
44     long mElapsedTimeNs;
45     long mWallClockTimeNs;
46     WorkSource mWorkSource = null;
47 
StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs)48     public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
49         this.mTag = tag;
50         this.mElapsedTimeNs = elapsedTimeNs;
51         this.mWallClockTimeNs = wallClockTimeNs;
52     }
53 
54     /**
55      * Boilerplate for Parcel.
56      */
57     public static final @android.annotation.NonNull Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
58             Parcelable.Creator<StatsLogEventWrapper>() {
59                 public StatsLogEventWrapper createFromParcel(Parcel in) {
60                     return new StatsLogEventWrapper(in);
61                 }
62 
63                 public StatsLogEventWrapper[] newArray(int size) {
64                     return new StatsLogEventWrapper[size];
65                 }
66             };
67 
StatsLogEventWrapper(Parcel in)68     private StatsLogEventWrapper(Parcel in) {
69         readFromParcel(in);
70     }
71 
72     /**
73      * Set work source if any.
74      */
setWorkSource(WorkSource ws)75     public void setWorkSource(WorkSource ws) {
76         if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) {
77             Slog.w(TAG, "Empty worksource!");
78             return;
79         }
80         mWorkSource = ws;
81     }
82 
83     /**
84      * Write a int value.
85      */
writeInt(int val)86     public void writeInt(int val) {
87         mTypes.add(EVENT_TYPE_INT);
88         mValues.add(val);
89     }
90 
91     /**
92      * Write a long value.
93      */
writeLong(long val)94     public void writeLong(long val) {
95         mTypes.add(EVENT_TYPE_LONG);
96         mValues.add(val);
97     }
98 
99     /**
100      * Write a string value.
101      */
writeString(String val)102     public void writeString(String val) {
103         mTypes.add(EVENT_TYPE_STRING);
104         // use empty string for null
105         mValues.add(val == null ? "" : val);
106     }
107 
108     /**
109      * Write a float value.
110      */
writeFloat(float val)111     public void writeFloat(float val) {
112         mTypes.add(EVENT_TYPE_FLOAT);
113         mValues.add(val);
114     }
115 
116     /**
117      * Write a storage value.
118      */
writeStorage(byte[] val)119     public void writeStorage(byte[] val) {
120         mTypes.add(EVENT_TYPE_STORAGE);
121         mValues.add(val);
122     }
123 
124     /**
125      * Write a boolean value.
126      */
writeBoolean(boolean val)127     public void writeBoolean(boolean val) {
128         mTypes.add(EVENT_TYPE_INT);
129         mValues.add(val ? 1 : 0);
130     }
131 
writeToParcel(Parcel out, int flags)132     public void writeToParcel(Parcel out, int flags) {
133         if (DEBUG) {
134             Slog.d(TAG,
135                     "Writing " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs + " and "
136                             + mTypes.size() + " elements.");
137         }
138         out.writeInt(mTag);
139         out.writeLong(mElapsedTimeNs);
140         out.writeLong(mWallClockTimeNs);
141         if (mWorkSource != null) {
142             ArrayList<android.os.WorkSource.WorkChain> workChains = mWorkSource.getWorkChains();
143             // number of chains
144             out.writeInt(workChains.size());
145             for (int i = 0; i < workChains.size(); i++) {
146                 android.os.WorkSource.WorkChain wc = workChains.get(i);
147                 if (wc.getSize() == 0) {
148                     Slog.w(TAG, "Empty work chain.");
149                     out.writeInt(0);
150                     continue;
151                 }
152                 if (wc.getUids().length != wc.getTags().length
153                         || wc.getUids().length != wc.getSize()) {
154                     Slog.w(TAG, "Malformated work chain.");
155                     out.writeInt(0);
156                     continue;
157                 }
158                 // number of nodes
159                 out.writeInt(wc.getSize());
160                 for (int j = 0; j < wc.getSize(); j++) {
161                     out.writeInt(wc.getUids()[j]);
162                     out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]);
163                 }
164             }
165         } else {
166             // no chains
167             out.writeInt(0);
168         }
169         out.writeInt(mTypes.size());
170         for (int i = 0; i < mTypes.size(); i++) {
171             out.writeInt(mTypes.get(i));
172             switch (mTypes.get(i)) {
173                 case EVENT_TYPE_INT:
174                     out.writeInt((int) mValues.get(i));
175                     break;
176                 case EVENT_TYPE_LONG:
177                     out.writeLong((long) mValues.get(i));
178                     break;
179                 case EVENT_TYPE_FLOAT:
180                     out.writeFloat((float) mValues.get(i));
181                     break;
182                 case EVENT_TYPE_DOUBLE:
183                     out.writeDouble((double) mValues.get(i));
184                     break;
185                 case EVENT_TYPE_STRING:
186                     out.writeString((String) mValues.get(i));
187                     break;
188                 case EVENT_TYPE_STORAGE:
189                     out.writeByteArray((byte[]) mValues.get(i));
190                     break;
191                 default:
192                     break;
193             }
194         }
195     }
196 
197     /**
198      * Reads from parcel and appropriately fills member fields.
199      */
readFromParcel(Parcel in)200     public void readFromParcel(Parcel in) {
201         mTypes = new ArrayList<>();
202         mValues = new ArrayList<>();
203         mWorkSource = null;
204 
205         mTag = in.readInt();
206         mElapsedTimeNs = in.readLong();
207         mWallClockTimeNs = in.readLong();
208 
209         // Clear any data.
210         if (DEBUG) {
211             Slog.d(TAG, "Reading " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs);
212         }
213         // Set up worksource if present.
214         int numWorkChains = in.readInt();
215         if (numWorkChains > 0) {
216             mWorkSource = new WorkSource();
217             for (int i = 0; i < numWorkChains; i++) {
218                 android.os.WorkSource.WorkChain workChain = mWorkSource.createWorkChain();
219                 int workChainSize = in.readInt();
220                 for (int j = 0; j < workChainSize; j++) {
221                     int uid = in.readInt();
222                     String tag = in.readString();
223                     workChain.addNode(uid, tag);
224                 }
225             }
226         }
227 
228         // Do the rest of the types.
229         int numTypes = in.readInt();
230         if (DEBUG) {
231             Slog.d(TAG, "Reading " + numTypes + " elements");
232         }
233         for (int i = 0; i < numTypes; i++) {
234             int type = in.readInt();
235             mTypes.add(type);
236             switch (type) {
237                 case EVENT_TYPE_INT:
238                     mValues.add(in.readInt());
239                     break;
240                 case EVENT_TYPE_LONG:
241                     mValues.add(in.readLong());
242                     break;
243                 case EVENT_TYPE_FLOAT:
244                     mValues.add(in.readFloat());
245                     break;
246                 case EVENT_TYPE_DOUBLE:
247                     mValues.add(in.readDouble());
248                     break;
249                 case EVENT_TYPE_STRING:
250                     mValues.add(in.readString());
251                     break;
252                 case EVENT_TYPE_STORAGE:
253                     mValues.add(in.createByteArray());
254                     break;
255                 default:
256                     break;
257             }
258         }
259     }
260 
261     /**
262      * Boilerplate for Parcel.
263      */
describeContents()264     public int describeContents() {
265         return 0;
266     }
267 }
268