• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&timestamp, &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