blob: 7cdbe5f06fdde2bb37f637c846a8ff74f3aeab22 [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>
Andy Hung7d391082020-04-18 15:03:51 -070028#include <cutils/properties.h>
Ray Essick3938dc62016-11-01 08:56:56 -070029#include <utils/Errors.h>
30#include <utils/Log.h>
Ray Essick3938dc62016-11-01 08:56:56 -070031#include <utils/SortedVector.h>
32#include <utils/threads.h>
33
Ray Essick3938dc62016-11-01 08:56:56 -070034#include <binder/IServiceManager.h>
Ray Essickf27e9872019-12-07 06:28:46 -080035#include <media/IMediaMetricsService.h>
36#include <media/MediaMetricsItem.h>
Ray Essick79a89ef2017-04-24 15:52:54 -070037#include <private/android_filesystem_config.h>
Ray Essick3938dc62016-11-01 08:56:56 -070038
Andy Hung1efc9c62019-12-03 13:43:33 -080039// Max per-property string size before truncation in toString().
40// Do not make too large, as this is used for dumpsys purposes.
41static constexpr size_t kMaxPropertyStringSize = 4096;
42
Ray Essickf27e9872019-12-07 06:28:46 -080043namespace android::mediametrics {
Ray Essick3938dc62016-11-01 08:56:56 -070044
45#define DEBUG_SERVICEACCESS 0
Ray Essickb5fac8e2016-12-12 11:33:56 -080046#define DEBUG_API 0
47#define DEBUG_ALLOCATIONS 0
48
49// after this many failed attempts, we stop trying [from this process] and just say that
50// the service is off.
51#define SVC_TRIES 2
Ray Essick3938dc62016-11-01 08:56:56 -070052
Ray Essickf27e9872019-12-07 06:28:46 -080053mediametrics::Item* mediametrics::Item::convert(mediametrics_handle_t handle) {
54 mediametrics::Item *item = (android::mediametrics::Item *) handle;
Ray Essickbf536ac2019-08-26 11:04:28 -070055 return item;
56}
57
Ray Essickf27e9872019-12-07 06:28:46 -080058mediametrics_handle_t mediametrics::Item::convert(mediametrics::Item *item ) {
Ray Essickbf536ac2019-08-26 11:04:28 -070059 mediametrics_handle_t handle = (mediametrics_handle_t) item;
60 return handle;
61}
62
Ray Essickf27e9872019-12-07 06:28:46 -080063mediametrics::Item::~Item() {
Ray Essickb5fac8e2016-12-12 11:33:56 -080064 if (DEBUG_ALLOCATIONS) {
Ray Essickf27e9872019-12-07 06:28:46 -080065 ALOGD("Destroy mediametrics::Item @ %p", this);
Ray Essickb5fac8e2016-12-12 11:33:56 -080066 }
Ray Essickb5fac8e2016-12-12 11:33:56 -080067}
68
Ray Essickf27e9872019-12-07 06:28:46 -080069mediametrics::Item &mediametrics::Item::setTimestamp(nsecs_t ts) {
Ray Essick3938dc62016-11-01 08:56:56 -070070 mTimestamp = ts;
71 return *this;
72}
73
Ray Essickf27e9872019-12-07 06:28:46 -080074nsecs_t mediametrics::Item::getTimestamp() const {
Ray Essick3938dc62016-11-01 08:56:56 -070075 return mTimestamp;
76}
77
Ray Essickf27e9872019-12-07 06:28:46 -080078mediametrics::Item &mediametrics::Item::setPid(pid_t pid) {
Ray Essick3938dc62016-11-01 08:56:56 -070079 mPid = pid;
80 return *this;
81}
82
Ray Essickf27e9872019-12-07 06:28:46 -080083pid_t mediametrics::Item::getPid() const {
Ray Essick3938dc62016-11-01 08:56:56 -070084 return mPid;
85}
86
Ray Essickf27e9872019-12-07 06:28:46 -080087mediametrics::Item &mediametrics::Item::setUid(uid_t uid) {
Ray Essick3938dc62016-11-01 08:56:56 -070088 mUid = uid;
89 return *this;
90}
91
Ray Essickf27e9872019-12-07 06:28:46 -080092uid_t mediametrics::Item::getUid() const {
Ray Essick3938dc62016-11-01 08:56:56 -070093 return mUid;
94}
95
Ray Essickf27e9872019-12-07 06:28:46 -080096mediametrics::Item &mediametrics::Item::setPkgName(const std::string &pkgName) {
Ray Essickf65f4212017-08-31 11:41:19 -070097 mPkgName = pkgName;
98 return *this;
99}
100
Ray Essickf27e9872019-12-07 06:28:46 -0800101mediametrics::Item &mediametrics::Item::setPkgVersionCode(int64_t pkgVersionCode) {
Ray Essickf65f4212017-08-31 11:41:19 -0700102 mPkgVersionCode = pkgVersionCode;
103 return *this;
104}
105
Ray Essickf27e9872019-12-07 06:28:46 -0800106int64_t mediametrics::Item::getPkgVersionCode() const {
Ray Essickf65f4212017-08-31 11:41:19 -0700107 return mPkgVersionCode;
108}
109
Ray Essick3938dc62016-11-01 08:56:56 -0700110// remove indicated keys and their values
111// return value is # keys removed
Ray Essickf27e9872019-12-07 06:28:46 -0800112size_t mediametrics::Item::filter(size_t n, const char *attrs[]) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700113 size_t zapped = 0;
114 for (size_t i = 0; i < n; ++i) {
Andy Hung47e58d62019-12-06 18:40:19 -0800115 zapped += mProps.erase(attrs[i]);
Ray Essick3938dc62016-11-01 08:56:56 -0700116 }
117 return zapped;
118}
119
120// remove any keys NOT in the provided list
121// return value is # keys removed
Ray Essickf27e9872019-12-07 06:28:46 -0800122size_t mediametrics::Item::filterNot(size_t n, const char *attrs[]) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700123 std::set<std::string> check(attrs, attrs + n);
124 size_t zapped = 0;
Andy Hung47e58d62019-12-06 18:40:19 -0800125 for (auto it = mProps.begin(); it != mProps.end();) {
126 if (check.find(it->first) != check.end()) {
127 ++it;
Andy Hung3253f2d2019-10-21 14:50:07 -0700128 } else {
Andy Hung47e58d62019-12-06 18:40:19 -0800129 it = mProps.erase(it);
130 ++zapped;
Ray Essick3938dc62016-11-01 08:56:56 -0700131 }
132 }
133 return zapped;
134}
135
Ray Essick3938dc62016-11-01 08:56:56 -0700136// Parcel / serialize things for binder calls
137//
138
Ray Essickf27e9872019-12-07 06:28:46 -0800139status_t mediametrics::Item::readFromParcel(const Parcel& data) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700140 int32_t version;
141 status_t status = data.readInt32(&version);
142 if (status != NO_ERROR) return status;
Ray Essickba8c4842019-01-18 11:35:33 -0800143
Andy Hung3253f2d2019-10-21 14:50:07 -0700144 switch (version) {
145 case 0:
146 return readFromParcel0(data);
147 default:
148 ALOGE("%s: unsupported parcel version: %d", __func__, version);
149 return INVALID_OPERATION;
Ray Essickba8c4842019-01-18 11:35:33 -0800150 }
151}
152
Ray Essickf27e9872019-12-07 06:28:46 -0800153status_t mediametrics::Item::readFromParcel0(const Parcel& data) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700154 const char *s = data.readCString();
155 mKey = s == nullptr ? "" : s;
156 int32_t pid, uid;
157 status_t status = data.readInt32(&pid) ?: data.readInt32(&uid);
158 if (status != NO_ERROR) return status;
159 mPid = (pid_t)pid;
160 mUid = (uid_t)uid;
161 s = data.readCString();
162 mPkgName = s == nullptr ? "" : s;
163 int32_t count;
164 int64_t version, timestamp;
165 status = data.readInt64(&version) ?: data.readInt64(&timestamp) ?: data.readInt32(&count);
166 if (status != NO_ERROR) return status;
167 if (count < 0) return BAD_VALUE;
168 mPkgVersionCode = version;
169 mTimestamp = timestamp;
Andy Hung47e58d62019-12-06 18:40:19 -0800170 for (int i = 0; i < count; i++) {
171 Prop prop;
172 status_t status = prop.readFromParcel(data);
Andy Hung3253f2d2019-10-21 14:50:07 -0700173 if (status != NO_ERROR) return status;
Andy Hung47e58d62019-12-06 18:40:19 -0800174 mProps[prop.getName()] = std::move(prop);
Ray Essick3938dc62016-11-01 08:56:56 -0700175 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700176 return NO_ERROR;
Ray Essick3938dc62016-11-01 08:56:56 -0700177}
178
Ray Essickf27e9872019-12-07 06:28:46 -0800179status_t mediametrics::Item::writeToParcel(Parcel *data) const {
Andy Hung3253f2d2019-10-21 14:50:07 -0700180 if (data == nullptr) return BAD_VALUE;
Ray Essickba8c4842019-01-18 11:35:33 -0800181
Andy Hung3253f2d2019-10-21 14:50:07 -0700182 const int32_t version = 0;
183 status_t status = data->writeInt32(version);
184 if (status != NO_ERROR) return status;
Ray Essick3938dc62016-11-01 08:56:56 -0700185
Andy Hung3253f2d2019-10-21 14:50:07 -0700186 switch (version) {
187 case 0:
188 return writeToParcel0(data);
189 default:
190 ALOGE("%s: unsupported parcel version: %d", __func__, version);
191 return INVALID_OPERATION;
Ray Essickba8c4842019-01-18 11:35:33 -0800192 }
193}
194
Ray Essickf27e9872019-12-07 06:28:46 -0800195status_t mediametrics::Item::writeToParcel0(Parcel *data) const {
Andy Hung3253f2d2019-10-21 14:50:07 -0700196 status_t status =
197 data->writeCString(mKey.c_str())
198 ?: data->writeInt32(mPid)
199 ?: data->writeInt32(mUid)
200 ?: data->writeCString(mPkgName.c_str())
201 ?: data->writeInt64(mPkgVersionCode)
202 ?: data->writeInt64(mTimestamp);
203 if (status != NO_ERROR) return status;
Ray Essick3938dc62016-11-01 08:56:56 -0700204
Andy Hung47e58d62019-12-06 18:40:19 -0800205 data->writeInt32((int32_t)mProps.size());
206 for (auto &prop : *this) {
207 status = prop.writeToParcel(data);
Andy Hung3253f2d2019-10-21 14:50:07 -0700208 if (status != NO_ERROR) return status;
Ray Essick3938dc62016-11-01 08:56:56 -0700209 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700210 return NO_ERROR;
Ray Essick3938dc62016-11-01 08:56:56 -0700211}
212
Ray Essickf27e9872019-12-07 06:28:46 -0800213const char *mediametrics::Item::toCString() {
Andy Hung3b4c1f02020-01-23 18:58:32 -0800214 std::string val = toString();
Ray Essick20147322018-11-17 09:08:39 -0800215 return strdup(val.c_str());
216}
217
Andy Hung3b4c1f02020-01-23 18:58:32 -0800218/*
219 * Similar to audio_utils/clock.h but customized for displaying mediametrics time.
220 */
221
222void nsToString(int64_t ns, char *buffer, size_t bufferSize, PrintFormat format)
223{
224 if (bufferSize == 0) return;
225
226 const int one_second = 1000000000;
227 const time_t sec = ns / one_second;
228 struct tm tm;
229
230 // Supported on bionic, glibc, and macOS, but not mingw.
231 if (localtime_r(&sec, &tm) == NULL) {
232 buffer[0] = '\0';
233 return;
234 }
235
236 switch (format) {
237 default:
238 case kPrintFormatLong:
239 if (snprintf(buffer, bufferSize, "%02d-%02d %02d:%02d:%02d.%03d",
240 tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
241 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
242 (int)(ns % one_second / 1000000)) < 0) {
243 buffer[0] = '\0'; // null terminate on format error, which should not happen
244 }
245 break;
246 case kPrintFormatShort:
247 if (snprintf(buffer, bufferSize, "%02d:%02d:%02d.%03d",
248 tm.tm_hour, tm.tm_min, tm.tm_sec,
249 (int)(ns % one_second / 1000000)) < 0) {
250 buffer[0] = '\0'; // null terminate on format error, which should not happen
251 }
252 break;
253 }
Ray Essickf65f4212017-08-31 11:41:19 -0700254}
Ray Essick3938dc62016-11-01 08:56:56 -0700255
Andy Hung3b4c1f02020-01-23 18:58:32 -0800256std::string mediametrics::Item::toString() const {
Ray Essick783bd0d2018-01-11 11:10:35 -0800257 std::string result;
Andy Hung1efc9c62019-12-03 13:43:33 -0800258 char buffer[kMaxPropertyStringSize];
Ray Essick3938dc62016-11-01 08:56:56 -0700259
Andy Hung3b4c1f02020-01-23 18:58:32 -0800260 snprintf(buffer, sizeof(buffer), "{%s, (%s), (%s, %d, %d)",
261 mKey.c_str(),
262 timeStringFromNs(mTimestamp, kPrintFormatLong).time,
263 mPkgName.c_str(), mPid, mUid
264 );
Ray Essickf65f4212017-08-31 11:41:19 -0700265 result.append(buffer);
Andy Hung3b4c1f02020-01-23 18:58:32 -0800266 bool first = true;
Andy Hung47e58d62019-12-06 18:40:19 -0800267 for (auto &prop : *this) {
Andy Hungb7aadb32019-12-09 19:40:42 -0800268 prop.toStringBuffer(buffer, sizeof(buffer));
Andy Hung3b4c1f02020-01-23 18:58:32 -0800269 result += first ? ", (" : ", ";
270 result += buffer;
271 first = false;
Ray Essick3938dc62016-11-01 08:56:56 -0700272 }
Andy Hung3b4c1f02020-01-23 18:58:32 -0800273 result.append(")}");
Ray Essick3938dc62016-11-01 08:56:56 -0700274 return result;
275}
276
277// for the lazy, we offer methods that finds the service and
278// calls the appropriate daemon
Ray Essickf27e9872019-12-07 06:28:46 -0800279bool mediametrics::Item::selfrecord() {
Andy Hunga87e69c2019-10-18 10:07:40 -0700280 ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
Andy Hung47e58d62019-12-06 18:40:19 -0800281 sp<IMediaMetricsService> svc = getService();
Ray Essick3938dc62016-11-01 08:56:56 -0700282 if (svc != NULL) {
Andy Hunga87e69c2019-10-18 10:07:40 -0700283 status_t status = svc->submit(this);
284 if (status != NO_ERROR) {
285 ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
Ray Essick2ab3c432017-10-02 09:29:49 -0700286 return false;
287 }
Ray Essick3938dc62016-11-01 08:56:56 -0700288 return true;
289 } else {
290 return false;
291 }
292}
293
Ray Essick3938dc62016-11-01 08:56:56 -0700294//static
Andy Hung1efc9c62019-12-03 13:43:33 -0800295bool BaseItem::isEnabled() {
Andy Hunga87e69c2019-10-18 10:07:40 -0700296 // completely skip logging from certain UIDs. We do this here
297 // to avoid the multi-second timeouts while we learn that
298 // sepolicy will not let us find the service.
299 // We do this only for a select set of UIDs
300 // The sepolicy protection is still in place, we just want a faster
301 // response from this specific, small set of uids.
Ray Essick3938dc62016-11-01 08:56:56 -0700302
Andy Hunga87e69c2019-10-18 10:07:40 -0700303 // This is checked only once in the lifetime of the process.
304 const uid_t uid = getuid();
305 switch (uid) {
306 case AID_RADIO: // telephony subsystem, RIL
307 return false;
308 }
309
Ray Essickf27e9872019-12-07 06:28:46 -0800310 int enabled = property_get_int32(Item::EnabledProperty, -1);
Ray Essick3938dc62016-11-01 08:56:56 -0700311 if (enabled == -1) {
Ray Essickf27e9872019-12-07 06:28:46 -0800312 enabled = property_get_int32(Item::EnabledPropertyPersist, -1);
Ray Essick3938dc62016-11-01 08:56:56 -0700313 }
314 if (enabled == -1) {
Ray Essickf27e9872019-12-07 06:28:46 -0800315 enabled = Item::EnabledProperty_default;
Ray Essick3938dc62016-11-01 08:56:56 -0700316 }
Andy Hunga87e69c2019-10-18 10:07:40 -0700317 return enabled > 0;
Ray Essick3938dc62016-11-01 08:56:56 -0700318}
319
Ray Essick2ab3c432017-10-02 09:29:49 -0700320// monitor health of our connection to the metrics service
321class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
322 virtual void binderDied(const wp<IBinder> &) {
323 ALOGW("Reacquire service connection on next request");
Andy Hung1efc9c62019-12-03 13:43:33 -0800324 BaseItem::dropInstance();
Ray Essick2ab3c432017-10-02 09:29:49 -0700325 }
326};
327
Andy Hunga87e69c2019-10-18 10:07:40 -0700328static sp<MediaMetricsDeathNotifier> sNotifier;
329// static
Ray Essickf27e9872019-12-07 06:28:46 -0800330sp<IMediaMetricsService> BaseItem::sMediaMetricsService;
Andy Hunga87e69c2019-10-18 10:07:40 -0700331static std::mutex sServiceMutex;
332static int sRemainingBindAttempts = SVC_TRIES;
Ray Essick2ab3c432017-10-02 09:29:49 -0700333
334// static
Andy Hung1efc9c62019-12-03 13:43:33 -0800335void BaseItem::dropInstance() {
Andy Hunga87e69c2019-10-18 10:07:40 -0700336 std::lock_guard _l(sServiceMutex);
337 sRemainingBindAttempts = SVC_TRIES;
Ray Essickf27e9872019-12-07 06:28:46 -0800338 sMediaMetricsService = nullptr;
Ray Essick2ab3c432017-10-02 09:29:49 -0700339}
340
Andy Hung1efc9c62019-12-03 13:43:33 -0800341// static
342bool BaseItem::submitBuffer(const char *buffer, size_t size) {
343/*
Ray Essickf27e9872019-12-07 06:28:46 -0800344 mediametrics::Item item;
Andy Hung1efc9c62019-12-03 13:43:33 -0800345 status_t status = item.readFromByteString(buffer, size);
346 ALOGD("%s: status:%d, size:%zu, item:%s", __func__, status, size, item.toString().c_str());
347 return item.selfrecord();
348 */
349
350 ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
Andy Hung47e58d62019-12-06 18:40:19 -0800351 sp<IMediaMetricsService> svc = getService();
Andy Hung1efc9c62019-12-03 13:43:33 -0800352 if (svc != nullptr) {
353 const status_t status = svc->submitBuffer(buffer, size);
354 if (status != NO_ERROR) {
355 ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
356 return false;
357 }
358 return true;
359 }
360 return false;
361}
362
Ray Essick3938dc62016-11-01 08:56:56 -0700363//static
Andy Hung47e58d62019-12-06 18:40:19 -0800364sp<IMediaMetricsService> BaseItem::getService() {
Ray Essickd38e1742017-01-23 15:17:06 -0800365 static const char *servicename = "media.metrics";
Andy Hunga87e69c2019-10-18 10:07:40 -0700366 static const bool enabled = isEnabled(); // singleton initialized
Ray Essick3938dc62016-11-01 08:56:56 -0700367
368 if (enabled == false) {
Andy Hunga87e69c2019-10-18 10:07:40 -0700369 ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
370 return nullptr;
Ray Essick3938dc62016-11-01 08:56:56 -0700371 }
Andy Hunga87e69c2019-10-18 10:07:40 -0700372 std::lock_guard _l(sServiceMutex);
373 // think of remainingBindAttempts as telling us whether service == nullptr because
374 // (1) we haven't tried to initialize it yet
375 // (2) we've tried to initialize it, but failed.
Ray Essickf27e9872019-12-07 06:28:46 -0800376 if (sMediaMetricsService == nullptr && sRemainingBindAttempts > 0) {
Ray Essick3938dc62016-11-01 08:56:56 -0700377 const char *badness = "";
Andy Hunga87e69c2019-10-18 10:07:40 -0700378 sp<IServiceManager> sm = defaultServiceManager();
379 if (sm != nullptr) {
380 sp<IBinder> binder = sm->getService(String16(servicename));
381 if (binder != nullptr) {
Ray Essickf27e9872019-12-07 06:28:46 -0800382 sMediaMetricsService = interface_cast<IMediaMetricsService>(binder);
Andy Hunga87e69c2019-10-18 10:07:40 -0700383 sNotifier = new MediaMetricsDeathNotifier();
384 binder->linkToDeath(sNotifier);
Ray Essick3938dc62016-11-01 08:56:56 -0700385 } else {
Andy Hunga87e69c2019-10-18 10:07:40 -0700386 badness = "did not find service";
Ray Essick3938dc62016-11-01 08:56:56 -0700387 }
Andy Hunga87e69c2019-10-18 10:07:40 -0700388 } else {
389 badness = "No Service Manager access";
Ray Essick3938dc62016-11-01 08:56:56 -0700390 }
Ray Essickf27e9872019-12-07 06:28:46 -0800391 if (sMediaMetricsService == nullptr) {
Andy Hunga87e69c2019-10-18 10:07:40 -0700392 if (sRemainingBindAttempts > 0) {
393 sRemainingBindAttempts--;
394 }
395 ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
396 __func__, servicename, badness);
397 }
Ray Essick3938dc62016-11-01 08:56:56 -0700398 }
Ray Essickf27e9872019-12-07 06:28:46 -0800399 return sMediaMetricsService;
Ray Essick3938dc62016-11-01 08:56:56 -0700400}
401
Andy Hung1efc9c62019-12-03 13:43:33 -0800402
Ray Essickf27e9872019-12-07 06:28:46 -0800403status_t mediametrics::Item::writeToByteString(char **pbuffer, size_t *plength) const
Andy Hung3253f2d2019-10-21 14:50:07 -0700404{
405 if (pbuffer == nullptr || plength == nullptr)
406 return BAD_VALUE;
407
408 // get size
409 const size_t keySizeZeroTerminated = strlen(mKey.c_str()) + 1;
410 if (keySizeZeroTerminated > UINT16_MAX) {
411 ALOGW("%s: key size %zu too large", __func__, keySizeZeroTerminated);
412 return INVALID_OPERATION;
413 }
414 const uint16_t version = 0;
Andy Hung1efc9c62019-12-03 13:43:33 -0800415 const uint32_t header_size =
416 sizeof(uint32_t) // total size
417 + sizeof(header_size) // header size
418 + sizeof(version) // encoding version
419 + sizeof(uint16_t) // key size
Andy Hung3253f2d2019-10-21 14:50:07 -0700420 + keySizeZeroTerminated // key, zero terminated
Andy Hung1efc9c62019-12-03 13:43:33 -0800421 + sizeof(int32_t) // pid
422 + sizeof(int32_t) // uid
423 + sizeof(int64_t) // timestamp
Andy Hung3253f2d2019-10-21 14:50:07 -0700424 ;
425
Andy Hung1efc9c62019-12-03 13:43:33 -0800426 uint32_t size = header_size
Andy Hung3253f2d2019-10-21 14:50:07 -0700427 + sizeof(uint32_t) // # properties
428 ;
Andy Hung47e58d62019-12-06 18:40:19 -0800429 for (auto &prop : *this) {
430 const size_t propSize = prop.getByteStringSize();
Andy Hung1efc9c62019-12-03 13:43:33 -0800431 if (propSize > UINT16_MAX) {
Andy Hung47e58d62019-12-06 18:40:19 -0800432 ALOGW("%s: prop %s size %zu too large", __func__, prop.getName(), propSize);
Andy Hung3253f2d2019-10-21 14:50:07 -0700433 return INVALID_OPERATION;
434 }
Andy Hung1efc9c62019-12-03 13:43:33 -0800435 if (__builtin_add_overflow(size, propSize, &size)) {
Andy Hung47e58d62019-12-06 18:40:19 -0800436 ALOGW("%s: item size overflow at property %s", __func__, prop.getName());
Andy Hung1efc9c62019-12-03 13:43:33 -0800437 return INVALID_OPERATION;
438 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700439 }
440
Andy Hung1efc9c62019-12-03 13:43:33 -0800441 // since we fill every byte in the buffer (there is no padding),
442 // malloc is used here instead of calloc.
443 char * const build = (char *)malloc(size);
Andy Hung3253f2d2019-10-21 14:50:07 -0700444 if (build == nullptr) return NO_MEMORY;
445
446 char *filling = build;
Andy Hung1efc9c62019-12-03 13:43:33 -0800447 char *buildmax = build + size;
448 if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
449 || insert(header_size, &filling, buildmax) != NO_ERROR
Andy Hung3253f2d2019-10-21 14:50:07 -0700450 || insert(version, &filling, buildmax) != NO_ERROR
451 || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
452 || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
453 || insert((int32_t)mPid, &filling, buildmax) != NO_ERROR
454 || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
455 || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
Andy Hung47e58d62019-12-06 18:40:19 -0800456 || insert((uint32_t)mProps.size(), &filling, buildmax) != NO_ERROR) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800457 ALOGE("%s:could not write header", __func__); // shouldn't happen
Andy Hung3253f2d2019-10-21 14:50:07 -0700458 free(build);
459 return INVALID_OPERATION;
460 }
Andy Hung47e58d62019-12-06 18:40:19 -0800461 for (auto &prop : *this) {
462 if (prop.writeToByteString(&filling, buildmax) != NO_ERROR) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700463 free(build);
Andy Hung1efc9c62019-12-03 13:43:33 -0800464 // shouldn't happen
Andy Hung47e58d62019-12-06 18:40:19 -0800465 ALOGE("%s:could not write prop %s", __func__, prop.getName());
Andy Hung3253f2d2019-10-21 14:50:07 -0700466 return INVALID_OPERATION;
467 }
468 }
469
470 if (filling != buildmax) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800471 ALOGE("%s: problems populating; wrote=%d planned=%d",
472 __func__, (int)(filling - build), (int)size);
Andy Hung3253f2d2019-10-21 14:50:07 -0700473 free(build);
474 return INVALID_OPERATION;
475 }
476 *pbuffer = build;
Andy Hung1efc9c62019-12-03 13:43:33 -0800477 *plength = size;
Andy Hung3253f2d2019-10-21 14:50:07 -0700478 return NO_ERROR;
479}
480
Ray Essickf27e9872019-12-07 06:28:46 -0800481status_t mediametrics::Item::readFromByteString(const char *bufferptr, size_t length)
Andy Hung3253f2d2019-10-21 14:50:07 -0700482{
483 if (bufferptr == nullptr) return BAD_VALUE;
484
485 const char *read = bufferptr;
486 const char *readend = bufferptr + length;
487
Andy Hung1efc9c62019-12-03 13:43:33 -0800488 uint32_t size;
489 uint32_t header_size;
490 uint16_t version;
491 uint16_t key_size;
Andy Hungb7aadb32019-12-09 19:40:42 -0800492 std::string key;
Andy Hung3253f2d2019-10-21 14:50:07 -0700493 int32_t pid;
494 int32_t uid;
495 int64_t timestamp;
496 uint32_t propCount;
Andy Hung1efc9c62019-12-03 13:43:33 -0800497 if (extract(&size, &read, readend) != NO_ERROR
498 || extract(&header_size, &read, readend) != NO_ERROR
Andy Hung3253f2d2019-10-21 14:50:07 -0700499 || extract(&version, &read, readend) != NO_ERROR
Andy Hung1efc9c62019-12-03 13:43:33 -0800500 || extract(&key_size, &read, readend) != NO_ERROR
Andy Hung3253f2d2019-10-21 14:50:07 -0700501 || extract(&key, &read, readend) != NO_ERROR
502 || extract(&pid, &read, readend) != NO_ERROR
503 || extract(&uid, &read, readend) != NO_ERROR
504 || extract(&timestamp, &read, readend) != NO_ERROR
Andy Hung1efc9c62019-12-03 13:43:33 -0800505 || size > length
Andy Hungb7aadb32019-12-09 19:40:42 -0800506 || key.size() + 1 != key_size
Andy Hung1efc9c62019-12-03 13:43:33 -0800507 || header_size > size) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800508 ALOGW("%s: invalid header", __func__);
Andy Hung3253f2d2019-10-21 14:50:07 -0700509 return INVALID_OPERATION;
510 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800511 mKey = std::move(key);
Andy Hung3253f2d2019-10-21 14:50:07 -0700512 const size_t pos = read - bufferptr;
Andy Hung1efc9c62019-12-03 13:43:33 -0800513 if (pos > header_size) {
514 ALOGW("%s: invalid header pos:%zu > header_size:%u",
515 __func__, pos, header_size);
Andy Hung3253f2d2019-10-21 14:50:07 -0700516 return INVALID_OPERATION;
Andy Hung1efc9c62019-12-03 13:43:33 -0800517 } else if (pos < header_size) {
518 ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
519 __func__, pos, header_size);
520 read += (header_size - pos);
Andy Hung3253f2d2019-10-21 14:50:07 -0700521 }
522 if (extract(&propCount, &read, readend) != NO_ERROR) {
523 ALOGD("%s: cannot read prop count", __func__);
524 return INVALID_OPERATION;
525 }
526 mPid = pid;
527 mUid = uid;
528 mTimestamp = timestamp;
529 for (size_t i = 0; i < propCount; ++i) {
Andy Hung47e58d62019-12-06 18:40:19 -0800530 Prop prop;
531 if (prop.readFromByteString(&read, readend) != NO_ERROR) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800532 ALOGW("%s: cannot read prop %zu", __func__, i);
Andy Hung3253f2d2019-10-21 14:50:07 -0700533 return INVALID_OPERATION;
534 }
Andy Hung47e58d62019-12-06 18:40:19 -0800535 mProps[prop.getName()] = std::move(prop);
Andy Hung3253f2d2019-10-21 14:50:07 -0700536 }
537 return NO_ERROR;
538}
539
Ray Essickf27e9872019-12-07 06:28:46 -0800540status_t mediametrics::Item::Prop::readFromParcel(const Parcel& data)
Andy Hung3253f2d2019-10-21 14:50:07 -0700541{
542 const char *key = data.readCString();
543 if (key == nullptr) return BAD_VALUE;
544 int32_t type;
545 status_t status = data.readInt32(&type);
546 if (status != NO_ERROR) return status;
547 switch (type) {
Andy Hungb7aadb32019-12-09 19:40:42 -0800548 case mediametrics::kTypeInt32: {
549 int32_t value;
550 status = data.readInt32(&value);
551 if (status != NO_ERROR) return status;
552 mElem = value;
553 } break;
554 case mediametrics::kTypeInt64: {
555 int64_t value;
556 status = data.readInt64(&value);
557 if (status != NO_ERROR) return status;
558 mElem = value;
559 } break;
560 case mediametrics::kTypeDouble: {
561 double value;
562 status = data.readDouble(&value);
563 if (status != NO_ERROR) return status;
564 mElem = value;
565 } break;
Andy Hung47e58d62019-12-06 18:40:19 -0800566 case mediametrics::kTypeCString: {
Andy Hung3253f2d2019-10-21 14:50:07 -0700567 const char *s = data.readCString();
568 if (s == nullptr) return BAD_VALUE;
Andy Hungb7aadb32019-12-09 19:40:42 -0800569 mElem = s;
570 } break;
Andy Hung47e58d62019-12-06 18:40:19 -0800571 case mediametrics::kTypeRate: {
Andy Hung3253f2d2019-10-21 14:50:07 -0700572 std::pair<int64_t, int64_t> rate;
573 status = data.readInt64(&rate.first)
574 ?: data.readInt64(&rate.second);
Andy Hungb7aadb32019-12-09 19:40:42 -0800575 if (status != NO_ERROR) return status;
576 mElem = rate;
577 } break;
578 case mediametrics::kTypeNone: {
579 mElem = std::monostate{};
580 } break;
Andy Hung3253f2d2019-10-21 14:50:07 -0700581 default:
Andy Hungb7aadb32019-12-09 19:40:42 -0800582 ALOGE("%s: reading bad item type: %d", __func__, type);
Andy Hung3253f2d2019-10-21 14:50:07 -0700583 return BAD_VALUE;
584 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800585 setName(key);
586 return NO_ERROR;
Andy Hung3253f2d2019-10-21 14:50:07 -0700587}
588
Ray Essickf27e9872019-12-07 06:28:46 -0800589status_t mediametrics::Item::Prop::readFromByteString(
Andy Hung3253f2d2019-10-21 14:50:07 -0700590 const char **bufferpptr, const char *bufferptrmax)
591{
592 uint16_t len;
Andy Hungb7aadb32019-12-09 19:40:42 -0800593 std::string name;
Andy Hung3253f2d2019-10-21 14:50:07 -0700594 uint8_t type;
595 status_t status = extract(&len, bufferpptr, bufferptrmax)
596 ?: extract(&type, bufferpptr, bufferptrmax)
597 ?: extract(&name, bufferpptr, bufferptrmax);
598 if (status != NO_ERROR) return status;
Andy Hungb7aadb32019-12-09 19:40:42 -0800599 switch (type) {
600 case mediametrics::kTypeInt32: {
601 int32_t value;
602 status = extract(&value, bufferpptr, bufferptrmax);
603 if (status != NO_ERROR) return status;
604 mElem = value;
605 } break;
606 case mediametrics::kTypeInt64: {
607 int64_t value;
608 status = extract(&value, bufferpptr, bufferptrmax);
609 if (status != NO_ERROR) return status;
610 mElem = value;
611 } break;
612 case mediametrics::kTypeDouble: {
613 double value;
614 status = extract(&value, bufferpptr, bufferptrmax);
615 if (status != NO_ERROR) return status;
616 mElem = value;
617 } break;
618 case mediametrics::kTypeRate: {
619 std::pair<int64_t, int64_t> value;
620 status = extract(&value.first, bufferpptr, bufferptrmax)
621 ?: extract(&value.second, bufferpptr, bufferptrmax);
622 if (status != NO_ERROR) return status;
623 mElem = value;
624 } break;
625 case mediametrics::kTypeCString: {
626 std::string value;
627 status = extract(&value, bufferpptr, bufferptrmax);
628 if (status != NO_ERROR) return status;
629 mElem = std::move(value);
630 } break;
631 case mediametrics::kTypeNone: {
632 mElem = std::monostate{};
633 } break;
Andy Hung3253f2d2019-10-21 14:50:07 -0700634 default:
Andy Hung3253f2d2019-10-21 14:50:07 -0700635 ALOGE("%s: found bad prop type: %d, name %s",
Andy Hungb7aadb32019-12-09 19:40:42 -0800636 __func__, (int)type, mName.c_str()); // no payload sent
Andy Hung3253f2d2019-10-21 14:50:07 -0700637 return BAD_VALUE;
638 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800639 mName = name;
640 return NO_ERROR;
Andy Hung3253f2d2019-10-21 14:50:07 -0700641}
642
Ray Essickf27e9872019-12-07 06:28:46 -0800643} // namespace android::mediametrics