1 /*
2 * Copyright (C) 2016 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
17 #define LOG_TAG "mediametrics::Item-Serialization"
18
19 #include <inttypes.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/endian.h>
23 #include <sys/types.h>
24
25 #include <cutils/multiuser.h>
26 #include <cutils/properties.h>
27 #include <utils/Errors.h>
28 #include <utils/Log.h>
29 #include <utils/SortedVector.h>
30 #include <utils/threads.h>
31
32 #include <media/MediaMetricsItem.h>
33 #include <private/android_filesystem_config.h>
34
35 // Max per-property string size before truncation in toString().
36 // Do not make too large, as this is used for dumpsys purposes.
37 static constexpr size_t kMaxPropertyStringSize = 4096;
38
39 namespace android::mediametrics {
40
writeToByteString(char ** pbuffer,size_t * plength) const41 status_t mediametrics::Item::writeToByteString(char **pbuffer, size_t *plength) const
42 {
43 if (pbuffer == nullptr || plength == nullptr)
44 return BAD_VALUE;
45
46 // get size
47 const size_t keySizeZeroTerminated = strlen(mKey.c_str()) + 1;
48 if (keySizeZeroTerminated > UINT16_MAX) {
49 ALOGW("%s: key size %zu too large", __func__, keySizeZeroTerminated);
50 return INVALID_OPERATION;
51 }
52 const uint16_t version = 0;
53 const uint32_t header_size =
54 sizeof(uint32_t) // total size
55 + sizeof(header_size) // header size
56 + sizeof(version) // encoding version
57 + sizeof(uint16_t) // key size
58 + keySizeZeroTerminated // key, zero terminated
59 + sizeof(int32_t) // pid
60 + sizeof(int32_t) // uid
61 + sizeof(int64_t) // timestamp
62 ;
63
64 uint32_t size = header_size
65 + sizeof(uint32_t) // # properties
66 ;
67 for (auto &prop : *this) {
68 const size_t propSize = prop.getByteStringSize();
69 if (propSize > UINT16_MAX) {
70 ALOGW("%s: prop %s size %zu too large", __func__, prop.getName(), propSize);
71 return INVALID_OPERATION;
72 }
73 if (__builtin_add_overflow(size, propSize, &size)) {
74 ALOGW("%s: item size overflow at property %s", __func__, prop.getName());
75 return INVALID_OPERATION;
76 }
77 }
78
79 // since we fill every byte in the buffer (there is no padding),
80 // malloc is used here instead of calloc.
81 char * const build = (char *)malloc(size);
82 if (build == nullptr) return NO_MEMORY;
83
84 // we write in host byte-order; we think this is always little-endian
85 // for the interesting devices (arm-based android, x86-based android).
86 // we know the reader is running on the same host, so we expect the same
87 // byte order on the consumption side.
88
89 char *filling = build;
90 char *buildmax = build + size;
91 if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
92 || insert(header_size, &filling, buildmax) != NO_ERROR
93 || insert(version, &filling, buildmax) != NO_ERROR
94 || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
95 || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
96 || insert((int32_t)mPid, &filling, buildmax) != NO_ERROR
97 || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
98 || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
99 || insert((uint32_t)mProps.size(), &filling, buildmax) != NO_ERROR) {
100 ALOGE("%s:could not write header", __func__); // shouldn't happen
101 free(build);
102 return INVALID_OPERATION;
103 }
104 for (auto &prop : *this) {
105 if (prop.writeToByteString(&filling, buildmax) != NO_ERROR) {
106 free(build);
107 // shouldn't happen
108 ALOGE("%s:could not write prop %s", __func__, prop.getName());
109 return INVALID_OPERATION;
110 }
111 }
112
113 if (filling != buildmax) {
114 ALOGE("%s: problems populating; wrote=%d planned=%d",
115 __func__, (int)(filling - build), (int)size);
116 free(build);
117 return INVALID_OPERATION;
118 }
119 *pbuffer = build;
120 *plength = size;
121 return NO_ERROR;
122 }
123
readFromByteString(const char * bufferptr,size_t length)124 status_t mediametrics::Item::readFromByteString(const char *bufferptr, size_t length)
125 {
126 if (bufferptr == nullptr) return BAD_VALUE;
127
128 // we read assuming host byte-order; we think this is always little-endian
129 // for the interesting devices (arm-based android, x86-based android).
130 // we know the writer is running on the same host,
131 // and therefore should have this same byte order.
132
133 const char *read = bufferptr;
134 const char *readend = bufferptr + length;
135
136 uint32_t size;
137 uint32_t header_size;
138 uint16_t version;
139 uint16_t key_size;
140 std::string key;
141 int32_t pid;
142 int32_t uid;
143 int64_t timestamp;
144 uint32_t propCount;
145 if (extract(&size, &read, readend) != NO_ERROR
146 || extract(&header_size, &read, readend) != NO_ERROR
147 || extract(&version, &read, readend) != NO_ERROR
148 || extract(&key_size, &read, readend) != NO_ERROR
149 || extract(&key, &read, readend) != NO_ERROR
150 || extract(&pid, &read, readend) != NO_ERROR
151 || extract(&uid, &read, readend) != NO_ERROR
152 || extract(×tamp, &read, readend) != NO_ERROR
153 || size > length
154 || key.size() + 1 != key_size
155 || header_size > size) {
156 ALOGW("%s: invalid header", __func__);
157 return INVALID_OPERATION;
158 }
159 mKey = std::move(key);
160 const size_t pos = read - bufferptr;
161 if (pos > header_size) {
162 ALOGW("%s: invalid header pos:%zu > header_size:%u",
163 __func__, pos, header_size);
164 return INVALID_OPERATION;
165 } else if (pos < header_size) {
166 ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
167 __func__, pos, header_size);
168 read += (header_size - pos);
169 }
170 if (extract(&propCount, &read, readend) != NO_ERROR) {
171 ALOGD("%s: cannot read prop count", __func__);
172 return INVALID_OPERATION;
173 }
174 mPid = pid;
175 mUid = uid;
176 mTimestamp = timestamp;
177 for (size_t i = 0; i < propCount; ++i) {
178 Prop prop;
179 if (prop.readFromByteString(&read, readend) != NO_ERROR) {
180 ALOGW("%s: cannot read prop %zu", __func__, i);
181 return INVALID_OPERATION;
182 }
183 mProps[prop.getName()] = std::move(prop);
184 }
185 return NO_ERROR;
186 }
187
readFromByteString(const char ** bufferpptr,const char * bufferptrmax)188 status_t mediametrics::Item::Prop::readFromByteString(
189 const char **bufferpptr, const char *bufferptrmax)
190 {
191 uint16_t len;
192 std::string name;
193 uint8_t type;
194 status_t status = extract(&len, bufferpptr, bufferptrmax)
195 ?: extract(&type, bufferpptr, bufferptrmax)
196 ?: extract(&name, bufferpptr, bufferptrmax);
197 if (status != NO_ERROR) return status;
198 switch (type) {
199 case mediametrics::kTypeInt32: {
200 int32_t value;
201 status = extract(&value, bufferpptr, bufferptrmax);
202 if (status != NO_ERROR) return status;
203 mElem = value;
204 } break;
205 case mediametrics::kTypeInt64: {
206 int64_t value;
207 status = extract(&value, bufferpptr, bufferptrmax);
208 if (status != NO_ERROR) return status;
209 mElem = value;
210 } break;
211 case mediametrics::kTypeDouble: {
212 double value;
213 status = extract(&value, bufferpptr, bufferptrmax);
214 if (status != NO_ERROR) return status;
215 mElem = value;
216 } break;
217 case mediametrics::kTypeRate: {
218 std::pair<int64_t, int64_t> value;
219 status = extract(&value.first, bufferpptr, bufferptrmax)
220 ?: extract(&value.second, bufferpptr, bufferptrmax);
221 if (status != NO_ERROR) return status;
222 mElem = value;
223 } break;
224 case mediametrics::kTypeCString: {
225 std::string value;
226 status = extract(&value, bufferpptr, bufferptrmax);
227 if (status != NO_ERROR) return status;
228 mElem = std::move(value);
229 } break;
230 case mediametrics::kTypeNone: {
231 mElem = std::monostate{};
232 } break;
233 default:
234 ALOGE("%s: found bad prop type: %d, name %s",
235 __func__, (int)type, mName.c_str()); // no payload sent
236 return BAD_VALUE;
237 }
238 mName = name;
239 return NO_ERROR;
240 }
241
242 } // namespace android::mediametrics
243