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.car.storagemonitoring;
17 
18 import android.annotation.SystemApi;
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 import android.util.JsonWriter;
22 
23 import org.json.JSONException;
24 import org.json.JSONObject;
25 
26 import java.io.IOException;
27 import java.util.Objects;
28 
29 /**
30  * uid_io stats about one user ID.
31  *
32  * Contains information about I/O activity that can be attributed to processes running on
33  * behalf of one user of the system, as collected by the kernel.
34  *
35  * @hide
36  */
37 @SystemApi
38 public final class IoStatsEntry implements Parcelable {
39 
40     public static final Parcelable.Creator<IoStatsEntry> CREATOR =
41         new Parcelable.Creator<IoStatsEntry>() {
42             public IoStatsEntry createFromParcel(Parcel in) {
43                 return new IoStatsEntry(in);
44             }
45 
46             public IoStatsEntry[] newArray(int size) {
47                 return new IoStatsEntry[size];
48             }
49         };
50 
51     /**
52      * The user id that this object contains metrics for.
53      *
54      * In many cases this can be converted to a list of Java app packages installed on the device.
55      * In other cases, the user id can refer to either the kernel itself (uid 0), or low-level
56      * system services that are running entirely natively.
57      */
58     public final int uid;
59 
60     /**
61      * How long any process running on behalf of this user id running for, in milliseconds.
62      *
63      * This field is allowed to be an approximation and it does not provide any way to
64      * relate uptime to specific processes.
65      */
66     public final long runtimeMillis;
67 
68     /**
69      * Statistics for apps running in foreground.
70      */
71     public final IoStatsEntry.Metrics foreground;
72 
73     /**
74      * Statistics for apps running in background.
75      */
76     public final IoStatsEntry.Metrics background;
77 
IoStatsEntry(int uid, long runtimeMillis, IoStatsEntry.Metrics foreground, IoStatsEntry.Metrics background)78     public IoStatsEntry(int uid,
79             long runtimeMillis, IoStatsEntry.Metrics foreground, IoStatsEntry.Metrics background) {
80         this.uid = uid;
81         this.runtimeMillis = runtimeMillis;
82         this.foreground = Objects.requireNonNull(foreground);
83         this.background = Objects.requireNonNull(background);
84     }
85 
IoStatsEntry(Parcel in)86     public IoStatsEntry(Parcel in) {
87         uid = in.readInt();
88         runtimeMillis = in.readLong();
89         foreground = in.readParcelable(IoStatsEntry.Metrics.class.getClassLoader());
90         background = in.readParcelable(IoStatsEntry.Metrics.class.getClassLoader());
91     }
92 
IoStatsEntry(UidIoRecord record, long runtimeMillis)93     public IoStatsEntry(UidIoRecord record, long runtimeMillis) {
94         uid = record.uid;
95         this.runtimeMillis = runtimeMillis;
96         foreground = new IoStatsEntry.Metrics(record.foreground_rchar,
97                 record.foreground_wchar,
98                 record.foreground_read_bytes,
99                 record.foreground_write_bytes,
100                 record.foreground_fsync);
101         background = new IoStatsEntry.Metrics(record.background_rchar,
102             record.background_wchar,
103             record.background_read_bytes,
104             record.background_write_bytes,
105             record.background_fsync);
106     }
107 
108     @Override
describeContents()109     public int describeContents() {
110         return 0;
111     }
112 
113     @Override
writeToParcel(Parcel dest, int flags)114     public void writeToParcel(Parcel dest, int flags) {
115         dest.writeInt(uid);
116         dest.writeLong(runtimeMillis);
117         dest.writeParcelable(foreground, flags);
118         dest.writeParcelable(background, flags);
119     }
120 
121     /**
122      * @hide
123      */
writeToJson(JsonWriter jsonWriter)124     public void writeToJson(JsonWriter jsonWriter) throws IOException {
125         jsonWriter.beginObject();
126         jsonWriter.name("uid").value(uid);
127         jsonWriter.name("runtimeMillis").value(runtimeMillis);
128         jsonWriter.name("foreground"); foreground.writeToJson(jsonWriter);
129         jsonWriter.name("background"); background.writeToJson(jsonWriter);
130         jsonWriter.endObject();
131     }
132 
133     /**
134      * @hide
135      */
IoStatsEntry(JSONObject in)136     public IoStatsEntry(JSONObject in) throws JSONException {
137         uid = in.getInt("uid");
138         runtimeMillis = in.getLong("runtimeMillis");
139         foreground = new IoStatsEntry.Metrics(in.getJSONObject("foreground"));
140         background = new IoStatsEntry.Metrics(in.getJSONObject("background"));
141     }
142 
143     /**
144      * Returns the difference between the values stored in this object vs. those
145      * stored in other.
146      *
147      * It is the same as doing a delta() on foreground and background, plus verifying that
148      * both objects refer to the same uid.
149      *
150      * @hide
151      */
delta(IoStatsEntry other)152     public IoStatsEntry delta(IoStatsEntry other) {
153         if (uid != other.uid) {
154             throw new IllegalArgumentException("cannot calculate delta between different user IDs");
155         }
156         return new IoStatsEntry(uid,
157                 runtimeMillis - other.runtimeMillis,
158                 foreground.delta(other.foreground), background.delta(other.background));
159     }
160 
161     @Override
equals(Object other)162     public boolean equals(Object other) {
163         if (other instanceof IoStatsEntry) {
164             IoStatsEntry uidIoStatEntry = (IoStatsEntry)other;
165 
166             return uid == uidIoStatEntry.uid &&
167                     runtimeMillis == uidIoStatEntry.runtimeMillis &&
168                     foreground.equals(uidIoStatEntry.foreground) &&
169                     background.equals(uidIoStatEntry.background);
170         }
171 
172         return false;
173     }
174 
175     @Override
hashCode()176     public int hashCode() {
177         return Objects.hash(uid, runtimeMillis, foreground, background);
178     }
179 
180     @Override
toString()181     public String toString() {
182         return String.format("uid = %d, runtime = %d, foreground = %s, background = %s",
183             uid, runtimeMillis, foreground, background);
184     }
185 
186     /**
187      * Validates that this object contains the same I/O metrics as a UidIoStatsRecord.
188      *
189      * It matches UID, and I/O activity values, but ignores runtime.
190      * @hide
191      */
representsSameMetrics(UidIoRecord record)192     public boolean representsSameMetrics(UidIoRecord record) {
193         return record.uid == uid &&
194                record.foreground_rchar == foreground.bytesRead &&
195                record.foreground_wchar == foreground.bytesWritten &&
196                record.foreground_read_bytes == foreground.bytesReadFromStorage &&
197                record.foreground_write_bytes == foreground.bytesWrittenToStorage &&
198                record.foreground_fsync == foreground.fsyncCalls &&
199                record.background_rchar == background.bytesRead &&
200                record.background_wchar == background.bytesWritten &&
201                record.background_read_bytes == background.bytesReadFromStorage &&
202                record.background_write_bytes == background.bytesWrittenToStorage &&
203                record.background_fsync == background.fsyncCalls;
204     }
205 
206     /**
207      * I/O activity metrics that pertain to either the foreground or the background state.
208      */
209     public static final class Metrics implements Parcelable {
210 
211         public static final Parcelable.Creator<IoStatsEntry.Metrics> CREATOR =
212             new Parcelable.Creator<IoStatsEntry.Metrics>() {
213                 public IoStatsEntry.Metrics createFromParcel(Parcel in) {
214                     return new IoStatsEntry.Metrics(in);
215                 }
216 
217                 public IoStatsEntry.Metrics[] newArray(int size) {
218                     return new IoStatsEntry.Metrics[size];
219                 }
220             };
221 
222         /**
223          * Total bytes that processes running on behalf of this user obtained
224          * via read() system calls.
225          */
226         public final long bytesRead;
227 
228         /**
229          * Total bytes that processes running on behalf of this user transferred
230          * via write() system calls.
231          */
232         public final long bytesWritten;
233 
234         /**
235          * Total bytes that processes running on behalf of this user obtained
236          * via read() system calls that actually were served by physical storage.
237          */
238         public final long bytesReadFromStorage;
239 
240         /**
241          * Total bytes that processes running on behalf of this user transferred
242          * via write() system calls that were actually sent to physical storage.
243          */
244         public final long bytesWrittenToStorage;
245 
246         /**
247          * Total number of fsync() system calls that processes running on behalf of this user made.
248          */
249         public final long fsyncCalls;
250 
Metrics(long bytesRead, long bytesWritten, long bytesReadFromStorage, long bytesWrittenToStorage, long fsyncCalls)251         public Metrics(long bytesRead, long bytesWritten, long bytesReadFromStorage,
252             long bytesWrittenToStorage, long fsyncCalls) {
253             this.bytesRead = bytesRead;
254             this.bytesWritten = bytesWritten;
255             this.bytesReadFromStorage = bytesReadFromStorage;
256             this.bytesWrittenToStorage = bytesWrittenToStorage;
257             this.fsyncCalls = fsyncCalls;
258         }
259 
260         @Override
describeContents()261         public int describeContents() {
262             return 0;
263         }
264 
265         @Override
writeToParcel(Parcel dest, int flags)266         public void writeToParcel(Parcel dest, int flags) {
267             dest.writeLong(bytesRead);
268             dest.writeLong(bytesWritten);
269             dest.writeLong(bytesReadFromStorage);
270             dest.writeLong(bytesWrittenToStorage);
271             dest.writeLong(fsyncCalls);
272         }
273 
274         /**
275          * @hide
276          */
writeToJson(JsonWriter jsonWriter)277         public void writeToJson(JsonWriter jsonWriter) throws IOException {
278             jsonWriter.beginObject();
279             jsonWriter.name("bytesRead").value(bytesRead);
280             jsonWriter.name("bytesWritten").value(bytesWritten);
281             jsonWriter.name("bytesReadFromStorage").value(bytesReadFromStorage);
282             jsonWriter.name("bytesWrittenToStorage").value(bytesWrittenToStorage);
283             jsonWriter.name("fsyncCalls").value(fsyncCalls);
284             jsonWriter.endObject();
285         }
286 
Metrics(Parcel in)287         public Metrics(Parcel in) {
288             bytesRead = in.readLong();
289             bytesWritten = in.readLong();
290             bytesReadFromStorage = in.readLong();
291             bytesWrittenToStorage = in.readLong();
292             fsyncCalls = in.readLong();
293         }
294 
295         /**
296          * @hide
297          */
Metrics(JSONObject in)298         public Metrics(JSONObject in) throws JSONException {
299             bytesRead = in.getLong("bytesRead");
300             bytesWritten = in.getLong("bytesWritten");
301             bytesReadFromStorage = in.getLong("bytesReadFromStorage");
302             bytesWrittenToStorage = in.getLong("bytesWrittenToStorage");
303             fsyncCalls = in.getLong("fsyncCalls");
304         }
305 
306         /**
307          * Computes the difference between the values stored in this object
308          * vs. those stored in other
309          *
310          * It is the same as doing
311          * new PerStateMetrics(bytesRead-other.bytesRead,bytesWritten-other.bytesWritten, ...)
312          *
313          * @hide
314          */
delta(Metrics other)315         public Metrics delta(Metrics other) {
316             return new Metrics(bytesRead-other.bytesRead,
317                 bytesWritten-other.bytesWritten,
318                 bytesReadFromStorage-other.bytesReadFromStorage,
319                 bytesWrittenToStorage-other.bytesWrittenToStorage,
320                 fsyncCalls-other.fsyncCalls);
321         }
322 
323         @Override
equals(Object other)324         public boolean equals(Object other) {
325             if (other instanceof Metrics) {
326                 Metrics metrics = (Metrics)other;
327 
328                 return (bytesRead == metrics.bytesRead) &&
329                     (bytesWritten == metrics.bytesWritten) &&
330                     (bytesReadFromStorage == metrics.bytesReadFromStorage) &&
331                     (bytesWrittenToStorage == metrics.bytesWrittenToStorage) &&
332                     (fsyncCalls == metrics.fsyncCalls);
333             }
334             return false;
335         }
336 
337         @Override
hashCode()338         public int hashCode() {
339             return Objects.hash(bytesRead, bytesWritten, bytesReadFromStorage,
340                 bytesWrittenToStorage, fsyncCalls);
341         }
342 
343         @Override
toString()344         public String toString() {
345             return String.format("bytesRead=%d, bytesWritten=%d, bytesReadFromStorage=%d, bytesWrittenToStorage=%d, fsyncCalls=%d",
346                 bytesRead, bytesWritten, bytesReadFromStorage, bytesWrittenToStorage, fsyncCalls);
347         }
348     }
349 }
350