blob: 4371668404b54a3a934c30b96546df5308d8c580 [file] [log] [blame]
Ray Essick3938dc62016-11-01 08:56:56 -07001/*
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
Ray Essickf27e9872019-12-07 06:28:46 -080017#define LOG_TAG "mediametrics::Item"
Ray Essick3938dc62016-11-01 08:56:56 -070018
Ray Essick3938dc62016-11-01 08:56:56 -070019#include <inttypes.h>
Ray Essickb5fac8e2016-12-12 11:33:56 -080020#include <stdlib.h>
21#include <string.h>
22#include <sys/types.h>
Ray Essick3938dc62016-11-01 08:56:56 -070023
Andy Hunga87e69c2019-10-18 10:07:40 -070024#include <mutex>
Andy Hung3253f2d2019-10-21 14:50:07 -070025#include <set>
Andy Hunga87e69c2019-10-18 10:07:40 -070026
Ray Essick3938dc62016-11-01 08:56:56 -070027#include <binder/Parcel.h>
28#include <utils/Errors.h>
29#include <utils/Log.h>
Ray Essick3938dc62016-11-01 08:56:56 -070030#include <utils/SortedVector.h>
31#include <utils/threads.h>
32
Ray Essick3938dc62016-11-01 08:56:56 -070033#include <binder/IServiceManager.h>
Ray Essickf27e9872019-12-07 06:28:46 -080034#include <media/IMediaMetricsService.h>
35#include <media/MediaMetricsItem.h>
Ray Essick79a89ef2017-04-24 15:52:54 -070036#include <private/android_filesystem_config.h>
Ray Essick3938dc62016-11-01 08:56:56 -070037
Andy Hung1efc9c62019-12-03 13:43:33 -080038// Max per-property string size before truncation in toString().
39// Do not make too large, as this is used for dumpsys purposes.
40static constexpr size_t kMaxPropertyStringSize = 4096;
41
Ray Essickf27e9872019-12-07 06:28:46 -080042namespace android::mediametrics {
Ray Essick3938dc62016-11-01 08:56:56 -070043
44#define DEBUG_SERVICEACCESS 0
Ray Essickb5fac8e2016-12-12 11:33:56 -080045#define DEBUG_API 0
46#define DEBUG_ALLOCATIONS 0
47
48// after this many failed attempts, we stop trying [from this process] and just say that
49// the service is off.
50#define SVC_TRIES 2
Ray Essick3938dc62016-11-01 08:56:56 -070051
Ray Essickf27e9872019-12-07 06:28:46 -080052mediametrics::Item* mediametrics::Item::convert(mediametrics_handle_t handle) {
53 mediametrics::Item *item = (android::mediametrics::Item *) handle;
Ray Essickbf536ac2019-08-26 11:04:28 -070054 return item;
55}
56
Ray Essickf27e9872019-12-07 06:28:46 -080057mediametrics_handle_t mediametrics::Item::convert(mediametrics::Item *item ) {
Ray Essickbf536ac2019-08-26 11:04:28 -070058 mediametrics_handle_t handle = (mediametrics_handle_t) item;
59 return handle;
60}
61
Ray Essickf27e9872019-12-07 06:28:46 -080062mediametrics::Item::~Item() {
Ray Essickb5fac8e2016-12-12 11:33:56 -080063 if (DEBUG_ALLOCATIONS) {
Ray Essickf27e9872019-12-07 06:28:46 -080064 ALOGD("Destroy mediametrics::Item @ %p", this);
Ray Essickb5fac8e2016-12-12 11:33:56 -080065 }
Ray Essickb5fac8e2016-12-12 11:33:56 -080066}
67
Ray Essickf27e9872019-12-07 06:28:46 -080068mediametrics::Item &mediametrics::Item::setTimestamp(nsecs_t ts) {
Ray Essick3938dc62016-11-01 08:56:56 -070069 mTimestamp = ts;
70 return *this;
71}
72
Ray Essickf27e9872019-12-07 06:28:46 -080073nsecs_t mediametrics::Item::getTimestamp() const {
Ray Essick3938dc62016-11-01 08:56:56 -070074 return mTimestamp;
75}
76
Ray Essickf27e9872019-12-07 06:28:46 -080077mediametrics::Item &mediametrics::Item::setPid(pid_t pid) {
Ray Essick3938dc62016-11-01 08:56:56 -070078 mPid = pid;
79 return *this;
80}
81
Ray Essickf27e9872019-12-07 06:28:46 -080082pid_t mediametrics::Item::getPid() const {
Ray Essick3938dc62016-11-01 08:56:56 -070083 return mPid;
84}
85
Ray Essickf27e9872019-12-07 06:28:46 -080086mediametrics::Item &mediametrics::Item::setUid(uid_t uid) {
Ray Essick3938dc62016-11-01 08:56:56 -070087 mUid = uid;
88 return *this;
89}
90
Ray Essickf27e9872019-12-07 06:28:46 -080091uid_t mediametrics::Item::getUid() const {
Ray Essick3938dc62016-11-01 08:56:56 -070092 return mUid;
93}
94
Ray Essickf27e9872019-12-07 06:28:46 -080095mediametrics::Item &mediametrics::Item::setPkgName(const std::string &pkgName) {
Ray Essickf65f4212017-08-31 11:41:19 -070096 mPkgName = pkgName;
97 return *this;
98}
99
Ray Essickf27e9872019-12-07 06:28:46 -0800100mediametrics::Item &mediametrics::Item::setPkgVersionCode(int64_t pkgVersionCode) {
Ray Essickf65f4212017-08-31 11:41:19 -0700101 mPkgVersionCode = pkgVersionCode;
102 return *this;
103}
104
Ray Essickf27e9872019-12-07 06:28:46 -0800105int64_t mediametrics::Item::getPkgVersionCode() const {
Ray Essickf65f4212017-08-31 11:41:19 -0700106 return mPkgVersionCode;
107}
108
Ray Essick3938dc62016-11-01 08:56:56 -0700109// remove indicated keys and their values
110// return value is # keys removed
Ray Essickf27e9872019-12-07 06:28:46 -0800111size_t mediametrics::Item::filter(size_t n, const char *attrs[]) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700112 size_t zapped = 0;
113 for (size_t i = 0; i < n; ++i) {
Andy Hung47e58d62019-12-06 18:40:19 -0800114 zapped += mProps.erase(attrs[i]);
Ray Essick3938dc62016-11-01 08:56:56 -0700115 }
116 return zapped;
117}
118
119// remove any keys NOT in the provided list
120// return value is # keys removed
Ray Essickf27e9872019-12-07 06:28:46 -0800121size_t mediametrics::Item::filterNot(size_t n, const char *attrs[]) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700122 std::set<std::string> check(attrs, attrs + n);
123 size_t zapped = 0;
Andy Hung47e58d62019-12-06 18:40:19 -0800124 for (auto it = mProps.begin(); it != mProps.end();) {
125 if (check.find(it->first) != check.end()) {
126 ++it;
Andy Hung3253f2d2019-10-21 14:50:07 -0700127 } else {
Andy Hung47e58d62019-12-06 18:40:19 -0800128 it = mProps.erase(it);
129 ++zapped;
Ray Essick3938dc62016-11-01 08:56:56 -0700130 }
131 }
132 return zapped;
133}
134
Ray Essick3938dc62016-11-01 08:56:56 -0700135// Parcel / serialize things for binder calls
136//
137
Ray Essickf27e9872019-12-07 06:28:46 -0800138status_t mediametrics::Item::readFromParcel(const Parcel& data) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700139 int32_t version;
140 status_t status = data.readInt32(&version);
141 if (status != NO_ERROR) return status;
Ray Essickba8c4842019-01-18 11:35:33 -0800142
Andy Hung3253f2d2019-10-21 14:50:07 -0700143 switch (version) {
144 case 0:
145 return readFromParcel0(data);
146 default:
147 ALOGE("%s: unsupported parcel version: %d", __func__, version);
148 return INVALID_OPERATION;
Ray Essickba8c4842019-01-18 11:35:33 -0800149 }
150}
151
Ray Essickf27e9872019-12-07 06:28:46 -0800152status_t mediametrics::Item::readFromParcel0(const Parcel& data) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700153 const char *s = data.readCString();
154 mKey = s == nullptr ? "" : s;
155 int32_t pid, uid;
156 status_t status = data.readInt32(&pid) ?: data.readInt32(&uid);
157 if (status != NO_ERROR) return status;
158 mPid = (pid_t)pid;
159 mUid = (uid_t)uid;
160 s = data.readCString();
161 mPkgName = s == nullptr ? "" : s;
162 int32_t count;
163 int64_t version, timestamp;
164 status = data.readInt64(&version) ?: data.readInt64(&timestamp) ?: data.readInt32(&count);
165 if (status != NO_ERROR) return status;
166 if (count < 0) return BAD_VALUE;
167 mPkgVersionCode = version;
168 mTimestamp = timestamp;
Andy Hung47e58d62019-12-06 18:40:19 -0800169 for (int i = 0; i < count; i++) {
170 Prop prop;
171 status_t status = prop.readFromParcel(data);
Andy Hung3253f2d2019-10-21 14:50:07 -0700172 if (status != NO_ERROR) return status;
Andy Hung47e58d62019-12-06 18:40:19 -0800173 mProps[prop.getName()] = std::move(prop);
Ray Essick3938dc62016-11-01 08:56:56 -0700174 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700175 return NO_ERROR;
Ray Essick3938dc62016-11-01 08:56:56 -0700176}
177
Ray Essickf27e9872019-12-07 06:28:46 -0800178status_t mediametrics::Item::writeToParcel(Parcel *data) const {
Andy Hung3253f2d2019-10-21 14:50:07 -0700179 if (data == nullptr) return BAD_VALUE;
Ray Essickba8c4842019-01-18 11:35:33 -0800180
Andy Hung3253f2d2019-10-21 14:50:07 -0700181 const int32_t version = 0;
182 status_t status = data->writeInt32(version);
183 if (status != NO_ERROR) return status;
Ray Essick3938dc62016-11-01 08:56:56 -0700184
Andy Hung3253f2d2019-10-21 14:50:07 -0700185 switch (version) {
186 case 0:
187 return writeToParcel0(data);
188 default:
189 ALOGE("%s: unsupported parcel version: %d", __func__, version);
190 return INVALID_OPERATION;
Ray Essickba8c4842019-01-18 11:35:33 -0800191 }
192}
193
Ray Essickf27e9872019-12-07 06:28:46 -0800194status_t mediametrics::Item::writeToParcel0(Parcel *data) const {
Andy Hung3253f2d2019-10-21 14:50:07 -0700195 status_t status =
196 data->writeCString(mKey.c_str())
197 ?: data->writeInt32(mPid)
198 ?: data->writeInt32(mUid)
199 ?: data->writeCString(mPkgName.c_str())
200 ?: data->writeInt64(mPkgVersionCode)
201 ?: data->writeInt64(mTimestamp);
202 if (status != NO_ERROR) return status;
Ray Essick3938dc62016-11-01 08:56:56 -0700203
Andy Hung47e58d62019-12-06 18:40:19 -0800204 data->writeInt32((int32_t)mProps.size());
205 for (auto &prop : *this) {
206 status = prop.writeToParcel(data);
Andy Hung3253f2d2019-10-21 14:50:07 -0700207 if (status != NO_ERROR) return status;
Ray Essick3938dc62016-11-01 08:56:56 -0700208 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700209 return NO_ERROR;
Ray Essick3938dc62016-11-01 08:56:56 -0700210}
211
Ray Essickf27e9872019-12-07 06:28:46 -0800212const char *mediametrics::Item::toCString() {
Andy Hung3b4c1f02020-01-23 18:58:32 -0800213 std::string val = toString();
Ray Essick20147322018-11-17 09:08:39 -0800214 return strdup(val.c_str());
215}
216
Andy Hung3b4c1f02020-01-23 18:58:32 -0800217/*
218 * Similar to audio_utils/clock.h but customized for displaying mediametrics time.
219 */
220
221void nsToString(int64_t ns, char *buffer, size_t bufferSize, PrintFormat format)
222{
223 if (bufferSize == 0) return;
224
225 const int one_second = 1000000000;
226 const time_t sec = ns / one_second;
227 struct tm tm;
228
229 // Supported on bionic, glibc, and macOS, but not mingw.
230 if (localtime_r(&sec, &tm) == NULL) {
231 buffer[0] = '\0';
232 return;
233 }
234
235 switch (format) {
236 default:
237 case kPrintFormatLong:
238 if (snprintf(buffer, bufferSize, "%02d-%02d %02d:%02d:%02d.%03d",
239 tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
240 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
241 (int)(ns % one_second / 1000000)) < 0) {
242 buffer[0] = '\0'; // null terminate on format error, which should not happen
243 }
244 break;
245 case kPrintFormatShort:
246 if (snprintf(buffer, bufferSize, "%02d:%02d:%02d.%03d",
247 tm.tm_hour, tm.tm_min, tm.tm_sec,
248 (int)(ns % one_second / 1000000)) < 0) {
249 buffer[0] = '\0'; // null terminate on format error, which should not happen
250 }
251 break;
252 }
Ray Essickf65f4212017-08-31 11:41:19 -0700253}
Ray Essick3938dc62016-11-01 08:56:56 -0700254
Andy Hung3b4c1f02020-01-23 18:58:32 -0800255std::string mediametrics::Item::toString() const {
Ray Essick783bd0d2018-01-11 11:10:35 -0800256 std::string result;
Andy Hung1efc9c62019-12-03 13:43:33 -0800257 char buffer[kMaxPropertyStringSize];
Ray Essick3938dc62016-11-01 08:56:56 -0700258
Andy Hung3b4c1f02020-01-23 18:58:32 -0800259 snprintf(buffer, sizeof(buffer), "{%s, (%s), (%s, %d, %d)",
260 mKey.c_str(),
261 timeStringFromNs(mTimestamp, kPrintFormatLong).time,
262 mPkgName.c_str(), mPid, mUid
263 );
Ray Essickf65f4212017-08-31 11:41:19 -0700264 result.append(buffer);
Andy Hung3b4c1f02020-01-23 18:58:32 -0800265 bool first = true;
Andy Hung47e58d62019-12-06 18:40:19 -0800266 for (auto &prop : *this) {
Andy Hungb7aadb32019-12-09 19:40:42 -0800267 prop.toStringBuffer(buffer, sizeof(buffer));
Andy Hung3b4c1f02020-01-23 18:58:32 -0800268 result += first ? ", (" : ", ";
269 result += buffer;
270 first = false;
Ray Essick3938dc62016-11-01 08:56:56 -0700271 }
Andy Hung3b4c1f02020-01-23 18:58:32 -0800272 result.append(")}");
Ray Essick3938dc62016-11-01 08:56:56 -0700273 return result;
274}
275
276// for the lazy, we offer methods that finds the service and
277// calls the appropriate daemon
Ray Essickf27e9872019-12-07 06:28:46 -0800278bool mediametrics::Item::selfrecord() {
Andy Hunga87e69c2019-10-18 10:07:40 -0700279 ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
Andy Hung47e58d62019-12-06 18:40:19 -0800280 sp<IMediaMetricsService> svc = getService();
Ray Essick3938dc62016-11-01 08:56:56 -0700281 if (svc != NULL) {
Andy Hunga87e69c2019-10-18 10:07:40 -0700282 status_t status = svc->submit(this);
283 if (status != NO_ERROR) {
284 ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
Ray Essick2ab3c432017-10-02 09:29:49 -0700285 return false;
286 }
Ray Essick3938dc62016-11-01 08:56:56 -0700287 return true;
288 } else {
289 return false;
290 }
291}
292
Ray Essick3938dc62016-11-01 08:56:56 -0700293//static
Andy Hung1efc9c62019-12-03 13:43:33 -0800294bool BaseItem::isEnabled() {
Andy Hunga87e69c2019-10-18 10:07:40 -0700295 // completely skip logging from certain UIDs. We do this here
296 // to avoid the multi-second timeouts while we learn that
297 // sepolicy will not let us find the service.
298 // We do this only for a select set of UIDs
299 // The sepolicy protection is still in place, we just want a faster
300 // response from this specific, small set of uids.
Ray Essick3938dc62016-11-01 08:56:56 -0700301
Andy Hunga87e69c2019-10-18 10:07:40 -0700302 // This is checked only once in the lifetime of the process.
303 const uid_t uid = getuid();
304 switch (uid) {
305 case AID_RADIO: // telephony subsystem, RIL
306 return false;
307 }
308
Ray Essickf27e9872019-12-07 06:28:46 -0800309 int enabled = property_get_int32(Item::EnabledProperty, -1);
Ray Essick3938dc62016-11-01 08:56:56 -0700310 if (enabled == -1) {
Ray Essickf27e9872019-12-07 06:28:46 -0800311 enabled = property_get_int32(Item::EnabledPropertyPersist, -1);
Ray Essick3938dc62016-11-01 08:56:56 -0700312 }
313 if (enabled == -1) {
Ray Essickf27e9872019-12-07 06:28:46 -0800314 enabled = Item::EnabledProperty_default;
Ray Essick3938dc62016-11-01 08:56:56 -0700315 }
Andy Hunga87e69c2019-10-18 10:07:40 -0700316 return enabled > 0;
Ray Essick3938dc62016-11-01 08:56:56 -0700317}
318
Ray Essick2ab3c432017-10-02 09:29:49 -0700319// monitor health of our connection to the metrics service
320class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
321 virtual void binderDied(const wp<IBinder> &) {
322 ALOGW("Reacquire service connection on next request");
Andy Hung1efc9c62019-12-03 13:43:33 -0800323 BaseItem::dropInstance();
Ray Essick2ab3c432017-10-02 09:29:49 -0700324 }
325};
326
Andy Hunga87e69c2019-10-18 10:07:40 -0700327static sp<MediaMetricsDeathNotifier> sNotifier;
328// static
Ray Essickf27e9872019-12-07 06:28:46 -0800329sp<IMediaMetricsService> BaseItem::sMediaMetricsService;
Andy Hunga87e69c2019-10-18 10:07:40 -0700330static std::mutex sServiceMutex;
331static int sRemainingBindAttempts = SVC_TRIES;
Ray Essick2ab3c432017-10-02 09:29:49 -0700332
333// static
Andy Hung1efc9c62019-12-03 13:43:33 -0800334void BaseItem::dropInstance() {
Andy Hunga87e69c2019-10-18 10:07:40 -0700335 std::lock_guard _l(sServiceMutex);
336 sRemainingBindAttempts = SVC_TRIES;
Ray Essickf27e9872019-12-07 06:28:46 -0800337 sMediaMetricsService = nullptr;
Ray Essick2ab3c432017-10-02 09:29:49 -0700338}
339
Andy Hung1efc9c62019-12-03 13:43:33 -0800340// static
341bool BaseItem::submitBuffer(const char *buffer, size_t size) {
342/*
Ray Essickf27e9872019-12-07 06:28:46 -0800343 mediametrics::Item item;
Andy Hung1efc9c62019-12-03 13:43:33 -0800344 status_t status = item.readFromByteString(buffer, size);
345 ALOGD("%s: status:%d, size:%zu, item:%s", __func__, status, size, item.toString().c_str());
346 return item.selfrecord();
347 */
348
349 ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
Andy Hung47e58d62019-12-06 18:40:19 -0800350 sp<IMediaMetricsService> svc = getService();
Andy Hung1efc9c62019-12-03 13:43:33 -0800351 if (svc != nullptr) {
352 const status_t status = svc->submitBuffer(buffer, size);
353 if (status != NO_ERROR) {
354 ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
355 return false;
356 }
357 return true;
358 }
359 return false;
360}
361
Ray Essick3938dc62016-11-01 08:56:56 -0700362//static
Andy Hung47e58d62019-12-06 18:40:19 -0800363sp<IMediaMetricsService> BaseItem::getService() {
Ray Essickd38e1742017-01-23 15:17:06 -0800364 static const char *servicename = "media.metrics";
Andy Hunga87e69c2019-10-18 10:07:40 -0700365 static const bool enabled = isEnabled(); // singleton initialized
Ray Essick3938dc62016-11-01 08:56:56 -0700366
367 if (enabled == false) {
Andy Hunga87e69c2019-10-18 10:07:40 -0700368 ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
369 return nullptr;
Ray Essick3938dc62016-11-01 08:56:56 -0700370 }
Andy Hunga87e69c2019-10-18 10:07:40 -0700371 std::lock_guard _l(sServiceMutex);
372 // think of remainingBindAttempts as telling us whether service == nullptr because
373 // (1) we haven't tried to initialize it yet
374 // (2) we've tried to initialize it, but failed.
Ray Essickf27e9872019-12-07 06:28:46 -0800375 if (sMediaMetricsService == nullptr && sRemainingBindAttempts > 0) {
Ray Essick3938dc62016-11-01 08:56:56 -0700376 const char *badness = "";
Andy Hunga87e69c2019-10-18 10:07:40 -0700377 sp<IServiceManager> sm = defaultServiceManager();
378 if (sm != nullptr) {
379 sp<IBinder> binder = sm->getService(String16(servicename));
380 if (binder != nullptr) {
Ray Essickf27e9872019-12-07 06:28:46 -0800381 sMediaMetricsService = interface_cast<IMediaMetricsService>(binder);
Andy Hunga87e69c2019-10-18 10:07:40 -0700382 sNotifier = new MediaMetricsDeathNotifier();
383 binder->linkToDeath(sNotifier);
Ray Essick3938dc62016-11-01 08:56:56 -0700384 } else {
Andy Hunga87e69c2019-10-18 10:07:40 -0700385 badness = "did not find service";
Ray Essick3938dc62016-11-01 08:56:56 -0700386 }
Andy Hunga87e69c2019-10-18 10:07:40 -0700387 } else {
388 badness = "No Service Manager access";
Ray Essick3938dc62016-11-01 08:56:56 -0700389 }
Ray Essickf27e9872019-12-07 06:28:46 -0800390 if (sMediaMetricsService == nullptr) {
Andy Hunga87e69c2019-10-18 10:07:40 -0700391 if (sRemainingBindAttempts > 0) {
392 sRemainingBindAttempts--;
393 }
394 ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
395 __func__, servicename, badness);
396 }
Ray Essick3938dc62016-11-01 08:56:56 -0700397 }
Ray Essickf27e9872019-12-07 06:28:46 -0800398 return sMediaMetricsService;
Ray Essick3938dc62016-11-01 08:56:56 -0700399}
400
Andy Hung1efc9c62019-12-03 13:43:33 -0800401
Ray Essickf27e9872019-12-07 06:28:46 -0800402status_t mediametrics::Item::writeToByteString(char **pbuffer, size_t *plength) const
Andy Hung3253f2d2019-10-21 14:50:07 -0700403{
404 if (pbuffer == nullptr || plength == nullptr)
405 return BAD_VALUE;
406
407 // get size
408 const size_t keySizeZeroTerminated = strlen(mKey.c_str()) + 1;
409 if (keySizeZeroTerminated > UINT16_MAX) {
410 ALOGW("%s: key size %zu too large", __func__, keySizeZeroTerminated);
411 return INVALID_OPERATION;
412 }
413 const uint16_t version = 0;
Andy Hung1efc9c62019-12-03 13:43:33 -0800414 const uint32_t header_size =
415 sizeof(uint32_t) // total size
416 + sizeof(header_size) // header size
417 + sizeof(version) // encoding version
418 + sizeof(uint16_t) // key size
Andy Hung3253f2d2019-10-21 14:50:07 -0700419 + keySizeZeroTerminated // key, zero terminated
Andy Hung1efc9c62019-12-03 13:43:33 -0800420 + sizeof(int32_t) // pid
421 + sizeof(int32_t) // uid
422 + sizeof(int64_t) // timestamp
Andy Hung3253f2d2019-10-21 14:50:07 -0700423 ;
424
Andy Hung1efc9c62019-12-03 13:43:33 -0800425 uint32_t size = header_size
Andy Hung3253f2d2019-10-21 14:50:07 -0700426 + sizeof(uint32_t) // # properties
427 ;
Andy Hung47e58d62019-12-06 18:40:19 -0800428 for (auto &prop : *this) {
429 const size_t propSize = prop.getByteStringSize();
Andy Hung1efc9c62019-12-03 13:43:33 -0800430 if (propSize > UINT16_MAX) {
Andy Hung47e58d62019-12-06 18:40:19 -0800431 ALOGW("%s: prop %s size %zu too large", __func__, prop.getName(), propSize);
Andy Hung3253f2d2019-10-21 14:50:07 -0700432 return INVALID_OPERATION;
433 }
Andy Hung1efc9c62019-12-03 13:43:33 -0800434 if (__builtin_add_overflow(size, propSize, &size)) {
Andy Hung47e58d62019-12-06 18:40:19 -0800435 ALOGW("%s: item size overflow at property %s", __func__, prop.getName());
Andy Hung1efc9c62019-12-03 13:43:33 -0800436 return INVALID_OPERATION;
437 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700438 }
439
Andy Hung1efc9c62019-12-03 13:43:33 -0800440 // since we fill every byte in the buffer (there is no padding),
441 // malloc is used here instead of calloc.
442 char * const build = (char *)malloc(size);
Andy Hung3253f2d2019-10-21 14:50:07 -0700443 if (build == nullptr) return NO_MEMORY;
444
445 char *filling = build;
Andy Hung1efc9c62019-12-03 13:43:33 -0800446 char *buildmax = build + size;
447 if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
448 || insert(header_size, &filling, buildmax) != NO_ERROR
Andy Hung3253f2d2019-10-21 14:50:07 -0700449 || insert(version, &filling, buildmax) != NO_ERROR
450 || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
451 || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
452 || insert((int32_t)mPid, &filling, buildmax) != NO_ERROR
453 || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
454 || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
Andy Hung47e58d62019-12-06 18:40:19 -0800455 || insert((uint32_t)mProps.size(), &filling, buildmax) != NO_ERROR) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800456 ALOGE("%s:could not write header", __func__); // shouldn't happen
Andy Hung3253f2d2019-10-21 14:50:07 -0700457 free(build);
458 return INVALID_OPERATION;
459 }
Andy Hung47e58d62019-12-06 18:40:19 -0800460 for (auto &prop : *this) {
461 if (prop.writeToByteString(&filling, buildmax) != NO_ERROR) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700462 free(build);
Andy Hung1efc9c62019-12-03 13:43:33 -0800463 // shouldn't happen
Andy Hung47e58d62019-12-06 18:40:19 -0800464 ALOGE("%s:could not write prop %s", __func__, prop.getName());
Andy Hung3253f2d2019-10-21 14:50:07 -0700465 return INVALID_OPERATION;
466 }
467 }
468
469 if (filling != buildmax) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800470 ALOGE("%s: problems populating; wrote=%d planned=%d",
471 __func__, (int)(filling - build), (int)size);
Andy Hung3253f2d2019-10-21 14:50:07 -0700472 free(build);
473 return INVALID_OPERATION;
474 }
475 *pbuffer = build;
Andy Hung1efc9c62019-12-03 13:43:33 -0800476 *plength = size;
Andy Hung3253f2d2019-10-21 14:50:07 -0700477 return NO_ERROR;
478}
479
Ray Essickf27e9872019-12-07 06:28:46 -0800480status_t mediametrics::Item::readFromByteString(const char *bufferptr, size_t length)
Andy Hung3253f2d2019-10-21 14:50:07 -0700481{
482 if (bufferptr == nullptr) return BAD_VALUE;
483
484 const char *read = bufferptr;
485 const char *readend = bufferptr + length;
486
Andy Hung1efc9c62019-12-03 13:43:33 -0800487 uint32_t size;
488 uint32_t header_size;
489 uint16_t version;
490 uint16_t key_size;
Andy Hungb7aadb32019-12-09 19:40:42 -0800491 std::string key;
Andy Hung3253f2d2019-10-21 14:50:07 -0700492 int32_t pid;
493 int32_t uid;
494 int64_t timestamp;
495 uint32_t propCount;
Andy Hung1efc9c62019-12-03 13:43:33 -0800496 if (extract(&size, &read, readend) != NO_ERROR
497 || extract(&header_size, &read, readend) != NO_ERROR
Andy Hung3253f2d2019-10-21 14:50:07 -0700498 || extract(&version, &read, readend) != NO_ERROR
Andy Hung1efc9c62019-12-03 13:43:33 -0800499 || extract(&key_size, &read, readend) != NO_ERROR
Andy Hung3253f2d2019-10-21 14:50:07 -0700500 || extract(&key, &read, readend) != NO_ERROR
501 || extract(&pid, &read, readend) != NO_ERROR
502 || extract(&uid, &read, readend) != NO_ERROR
503 || extract(&timestamp, &read, readend) != NO_ERROR
Andy Hung1efc9c62019-12-03 13:43:33 -0800504 || size > length
Andy Hungb7aadb32019-12-09 19:40:42 -0800505 || key.size() + 1 != key_size
Andy Hung1efc9c62019-12-03 13:43:33 -0800506 || header_size > size) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800507 ALOGW("%s: invalid header", __func__);
Andy Hung3253f2d2019-10-21 14:50:07 -0700508 return INVALID_OPERATION;
509 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800510 mKey = std::move(key);
Andy Hung3253f2d2019-10-21 14:50:07 -0700511 const size_t pos = read - bufferptr;
Andy Hung1efc9c62019-12-03 13:43:33 -0800512 if (pos > header_size) {
513 ALOGW("%s: invalid header pos:%zu > header_size:%u",
514 __func__, pos, header_size);
Andy Hung3253f2d2019-10-21 14:50:07 -0700515 return INVALID_OPERATION;
Andy Hung1efc9c62019-12-03 13:43:33 -0800516 } else if (pos < header_size) {
517 ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
518 __func__, pos, header_size);
519 read += (header_size - pos);
Andy Hung3253f2d2019-10-21 14:50:07 -0700520 }
521 if (extract(&propCount, &read, readend) != NO_ERROR) {
522 ALOGD("%s: cannot read prop count", __func__);
523 return INVALID_OPERATION;
524 }
525 mPid = pid;
526 mUid = uid;
527 mTimestamp = timestamp;
528 for (size_t i = 0; i < propCount; ++i) {
Andy Hung47e58d62019-12-06 18:40:19 -0800529 Prop prop;
530 if (prop.readFromByteString(&read, readend) != NO_ERROR) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800531 ALOGW("%s: cannot read prop %zu", __func__, i);
Andy Hung3253f2d2019-10-21 14:50:07 -0700532 return INVALID_OPERATION;
533 }
Andy Hung47e58d62019-12-06 18:40:19 -0800534 mProps[prop.getName()] = std::move(prop);
Andy Hung3253f2d2019-10-21 14:50:07 -0700535 }
536 return NO_ERROR;
537}
538
Ray Essickf27e9872019-12-07 06:28:46 -0800539status_t mediametrics::Item::Prop::readFromParcel(const Parcel& data)
Andy Hung3253f2d2019-10-21 14:50:07 -0700540{
541 const char *key = data.readCString();
542 if (key == nullptr) return BAD_VALUE;
543 int32_t type;
544 status_t status = data.readInt32(&type);
545 if (status != NO_ERROR) return status;
546 switch (type) {
Andy Hungb7aadb32019-12-09 19:40:42 -0800547 case mediametrics::kTypeInt32: {
548 int32_t value;
549 status = data.readInt32(&value);
550 if (status != NO_ERROR) return status;
551 mElem = value;
552 } break;
553 case mediametrics::kTypeInt64: {
554 int64_t value;
555 status = data.readInt64(&value);
556 if (status != NO_ERROR) return status;
557 mElem = value;
558 } break;
559 case mediametrics::kTypeDouble: {
560 double value;
561 status = data.readDouble(&value);
562 if (status != NO_ERROR) return status;
563 mElem = value;
564 } break;
Andy Hung47e58d62019-12-06 18:40:19 -0800565 case mediametrics::kTypeCString: {
Andy Hung3253f2d2019-10-21 14:50:07 -0700566 const char *s = data.readCString();
567 if (s == nullptr) return BAD_VALUE;
Andy Hungb7aadb32019-12-09 19:40:42 -0800568 mElem = s;
569 } break;
Andy Hung47e58d62019-12-06 18:40:19 -0800570 case mediametrics::kTypeRate: {
Andy Hung3253f2d2019-10-21 14:50:07 -0700571 std::pair<int64_t, int64_t> rate;
572 status = data.readInt64(&rate.first)
573 ?: data.readInt64(&rate.second);
Andy Hungb7aadb32019-12-09 19:40:42 -0800574 if (status != NO_ERROR) return status;
575 mElem = rate;
576 } break;
577 case mediametrics::kTypeNone: {
578 mElem = std::monostate{};
579 } break;
Andy Hung3253f2d2019-10-21 14:50:07 -0700580 default:
Andy Hungb7aadb32019-12-09 19:40:42 -0800581 ALOGE("%s: reading bad item type: %d", __func__, type);
Andy Hung3253f2d2019-10-21 14:50:07 -0700582 return BAD_VALUE;
583 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800584 setName(key);
585 return NO_ERROR;
Andy Hung3253f2d2019-10-21 14:50:07 -0700586}
587
Ray Essickf27e9872019-12-07 06:28:46 -0800588status_t mediametrics::Item::Prop::readFromByteString(
Andy Hung3253f2d2019-10-21 14:50:07 -0700589 const char **bufferpptr, const char *bufferptrmax)
590{
591 uint16_t len;
Andy Hungb7aadb32019-12-09 19:40:42 -0800592 std::string name;
Andy Hung3253f2d2019-10-21 14:50:07 -0700593 uint8_t type;
594 status_t status = extract(&len, bufferpptr, bufferptrmax)
595 ?: extract(&type, bufferpptr, bufferptrmax)
596 ?: extract(&name, bufferpptr, bufferptrmax);
597 if (status != NO_ERROR) return status;
Andy Hungb7aadb32019-12-09 19:40:42 -0800598 switch (type) {
599 case mediametrics::kTypeInt32: {
600 int32_t value;
601 status = extract(&value, bufferpptr, bufferptrmax);
602 if (status != NO_ERROR) return status;
603 mElem = value;
604 } break;
605 case mediametrics::kTypeInt64: {
606 int64_t value;
607 status = extract(&value, bufferpptr, bufferptrmax);
608 if (status != NO_ERROR) return status;
609 mElem = value;
610 } break;
611 case mediametrics::kTypeDouble: {
612 double value;
613 status = extract(&value, bufferpptr, bufferptrmax);
614 if (status != NO_ERROR) return status;
615 mElem = value;
616 } break;
617 case mediametrics::kTypeRate: {
618 std::pair<int64_t, int64_t> value;
619 status = extract(&value.first, bufferpptr, bufferptrmax)
620 ?: extract(&value.second, bufferpptr, bufferptrmax);
621 if (status != NO_ERROR) return status;
622 mElem = value;
623 } break;
624 case mediametrics::kTypeCString: {
625 std::string value;
626 status = extract(&value, bufferpptr, bufferptrmax);
627 if (status != NO_ERROR) return status;
628 mElem = std::move(value);
629 } break;
630 case mediametrics::kTypeNone: {
631 mElem = std::monostate{};
632 } break;
Andy Hung3253f2d2019-10-21 14:50:07 -0700633 default:
Andy Hung3253f2d2019-10-21 14:50:07 -0700634 ALOGE("%s: found bad prop type: %d, name %s",
Andy Hungb7aadb32019-12-09 19:40:42 -0800635 __func__, (int)type, mName.c_str()); // no payload sent
Andy Hung3253f2d2019-10-21 14:50:07 -0700636 return BAD_VALUE;
637 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800638 mName = name;
639 return NO_ERROR;
Andy Hung3253f2d2019-10-21 14:50:07 -0700640}
641
Ray Essickf27e9872019-12-07 06:28:46 -0800642} // namespace android::mediametrics