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