blob: f844c82aa9159f38bf88b351619132b370cfa6b6 [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
Andy Hung47e58d62019-12-06 18:40:19 -080017#ifndef ANDROID_MEDIA_MEDIAMETRICSITEM_H
18#define ANDROID_MEDIA_MEDIAMETRICSITEM_H
Ray Essick3938dc62016-11-01 08:56:56 -070019
Ray Essickbf536ac2019-08-26 11:04:28 -070020#include "MediaMetrics.h"
21
Andy Hung3253f2d2019-10-21 14:50:07 -070022#include <algorithm>
Andy Hung47e58d62019-12-06 18:40:19 -080023#include <map>
Ray Essick783bd0d2018-01-11 11:10:35 -080024#include <string>
Ray Essick3938dc62016-11-01 08:56:56 -070025#include <sys/types.h>
Andy Hungb7aadb32019-12-09 19:40:42 -080026#include <variant>
Ray Essickba8c4842019-01-18 11:35:33 -080027
Andy Hungb7aadb32019-12-09 19:40:42 -080028#include <binder/Parcel.h>
Ray Essickba8c4842019-01-18 11:35:33 -080029#include <cutils/properties.h>
Ray Essick3938dc62016-11-01 08:56:56 -070030#include <utils/Errors.h>
31#include <utils/KeyedVector.h>
32#include <utils/RefBase.h>
33#include <utils/StrongPointer.h>
34#include <utils/Timers.h>
35
Ray Essick3938dc62016-11-01 08:56:56 -070036namespace android {
37
Ray Essickf27e9872019-12-07 06:28:46 -080038class IMediaMetricsService;
Ray Essick783bd0d2018-01-11 11:10:35 -080039class Parcel;
Ray Essick3938dc62016-11-01 08:56:56 -070040
Andy Hung3253f2d2019-10-21 14:50:07 -070041/*
Andy Hung1efc9c62019-12-03 13:43:33 -080042 * MediaMetrics Item
Andy Hung3253f2d2019-10-21 14:50:07 -070043 *
Andy Hung611268d2019-12-19 13:54:02 -080044 * The MediaMetrics Item allows get/set operations and recording to the service.
45 *
46 * The MediaMetrics LogItem is a faster logging variant. It allows set operations only,
47 * and then recording to the service.
48 *
49 * The Byte String format is as follows:
Andy Hung1efc9c62019-12-03 13:43:33 -080050 *
51 * For Java
52 * int64 corresponds to long
53 * int32, uint32 corresponds to int
54 * uint16 corresponds to char
55 * uint8, int8 corresponds to byte
56 *
Andy Hung611268d2019-12-19 13:54:02 -080057 * For items transmitted from Java, uint8 and uint32 values are limited
58 * to INT8_MAX and INT32_MAX. This constrains the size of large items
59 * to 2GB, which is consistent with ByteBuffer max size. A native item
60 * can conceivably have size of 4GB.
Andy Hung1efc9c62019-12-03 13:43:33 -080061 *
62 * Physical layout of integers and doubles within the MediaMetrics byte string
Andy Hung611268d2019-12-19 13:54:02 -080063 * is in Native / host order, which is usually little endian.
64 *
65 * Note that primitive data (ints, doubles) within a Byte String has
66 * no extra padding or alignment requirements, like ByteBuffer.
Andy Hung1efc9c62019-12-03 13:43:33 -080067 *
68 * -- begin of item
69 * -- begin of header
70 * (uint32) item size: including the item size field
71 * (uint32) header size, including the item size and header size fields.
72 * (uint16) version: exactly 0
73 * (uint16) key size, that is key strlen + 1 for zero termination.
Andy Hung611268d2019-12-19 13:54:02 -080074 * (int8)+ key, a string which is 0 terminated (UTF-8).
Andy Hung3253f2d2019-10-21 14:50:07 -070075 * (int32) pid
76 * (int32) uid
77 * (int64) timestamp
Andy Hung1efc9c62019-12-03 13:43:33 -080078 * -- end of header
79 * -- begin body
80 * (uint32) number of properties
81 * -- repeat for number of properties
82 * (uint16) property size, including property size field itself
Andy Hung3253f2d2019-10-21 14:50:07 -070083 * (uint8) type of property
84 * (int8)+ key string, including 0 termination
Andy Hung1efc9c62019-12-03 13:43:33 -080085 * based on type of property (given above), one of:
Andy Hung3253f2d2019-10-21 14:50:07 -070086 * (int32)
87 * (int64)
88 * (double)
Andy Hung611268d2019-12-19 13:54:02 -080089 * (int8)+ for TYPE_CSTRING, including 0 termination
Andy Hung3253f2d2019-10-21 14:50:07 -070090 * (int64, int64) for rate
Andy Hung1efc9c62019-12-03 13:43:33 -080091 * -- end body
92 * -- end of item
Andy Hung611268d2019-12-19 13:54:02 -080093 *
94 * The Byte String format must match MediaMetrics.java.
Andy Hung3253f2d2019-10-21 14:50:07 -070095 */
96
Andy Hung1efc9c62019-12-03 13:43:33 -080097namespace mediametrics {
98
99// Type must match MediaMetrics.java
100enum Type {
101 kTypeNone = 0,
102 kTypeInt32 = 1,
103 kTypeInt64 = 2,
104 kTypeDouble = 3,
105 kTypeCString = 4,
106 kTypeRate = 5,
107};
108
Andy Hung611268d2019-12-19 13:54:02 -0800109/**
110 * The MediaMetrics Item has special Item properties,
111 * derived internally or through dedicated setters.
112 *
113 * For consistency we use the following keys to represent
114 * these special Item properties when in a generic Bundle
115 * or in a std::map.
116 *
117 * These values must match MediaMetrics.java
118 */
119static inline constexpr const char *BUNDLE_TOTAL_SIZE = "_totalSize";
120static inline constexpr const char *BUNDLE_HEADER_SIZE = "_headerSize";
121static inline constexpr const char *BUNDLE_VERSION = "_version";
122static inline constexpr const char *BUNDLE_KEY_SIZE = "_keySize";
123static inline constexpr const char *BUNDLE_KEY = "_key";
124static inline constexpr const char *BUNDLE_PID = "_pid";
125static inline constexpr const char *BUNDLE_UID = "_uid";
126static inline constexpr const char *BUNDLE_TIMESTAMP = "_timestamp";
127static inline constexpr const char *BUNDLE_PROPERTY_COUNT = "_propertyCount";
128
Andy Hung1efc9c62019-12-03 13:43:33 -0800129template<size_t N>
130static inline bool startsWith(const std::string &s, const char (&comp)[N]) {
131 return !strncmp(s.c_str(), comp, N-1);
132}
133
134/**
135 * Media Metrics BaseItem
136 *
137 * A base class which contains utility static functions to write to a byte stream
138 * and access the Media Metrics service.
139 */
140
141class BaseItem {
142 friend class MediaMetricsDeathNotifier; // for dropInstance
143 // enabled 1, disabled 0
144public:
Andy Hung47e58d62019-12-06 18:40:19 -0800145 // are we collecting metrics data
Andy Hung1efc9c62019-12-03 13:43:33 -0800146 static bool isEnabled();
Andy Hung47e58d62019-12-06 18:40:19 -0800147 static sp<IMediaMetricsService> getService();
Andy Hung1efc9c62019-12-03 13:43:33 -0800148
149protected:
150 static constexpr const char * const EnabledProperty = "media.metrics.enabled";
151 static constexpr const char * const EnabledPropertyPersist = "persist.media.metrics.enabled";
152 static const int EnabledProperty_default = 1;
153
154 // let's reuse a binder connection
Ray Essickf27e9872019-12-07 06:28:46 -0800155 static sp<IMediaMetricsService> sMediaMetricsService;
Andy Hung47e58d62019-12-06 18:40:19 -0800156
Andy Hung1efc9c62019-12-03 13:43:33 -0800157 static void dropInstance();
158 static bool submitBuffer(const char *buffer, size_t len);
159
Andy Hungb7aadb32019-12-09 19:40:42 -0800160 template <typename T>
161 struct is_item_type {
162 static constexpr inline bool value =
163 std::is_same<T, int32_t>::value
164 || std::is_same<T, int64_t>::value
165 || std::is_same<T, double>::value
166 || std::is_same<T, std::pair<int64_t, int64_t>>:: value
167 || std::is_same<T, std::string>::value
168 || std::is_same<T, std::monostate>::value;
169 };
Andy Hung1efc9c62019-12-03 13:43:33 -0800170
Andy Hungb7aadb32019-12-09 19:40:42 -0800171 template <typename T>
172 struct get_type_of {
173 static_assert(is_item_type<T>::value);
174 static constexpr inline Type value =
175 std::is_same<T, int32_t>::value ? kTypeInt32
176 : std::is_same<T, int64_t>::value ? kTypeInt64
177 : std::is_same<T, double>::value ? kTypeDouble
178 : std::is_same<T, std::pair<int64_t, int64_t>>:: value ? kTypeRate
179 : std::is_same<T, std::string>::value ? kTypeCString
180 : std::is_same<T, std::monostate>::value ? kTypeNone
181 : kTypeNone;
182 };
183
184 template <typename T>
185 static size_t sizeOfByteString(const char *name, const T& value) {
186 static_assert(is_item_type<T>::value);
Andy Hung1efc9c62019-12-03 13:43:33 -0800187 return 2 + 1 + strlen(name) + 1 + sizeof(value);
188 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800189 template <> // static
190 size_t sizeOfByteString(const char *name, const std::string& value) {
191 return 2 + 1 + strlen(name) + 1 + value.size() + 1;
Andy Hung1efc9c62019-12-03 13:43:33 -0800192 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800193 template <> // static
194 size_t sizeOfByteString(const char *name, const std::monostate&) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800195 return 2 + 1 + strlen(name) + 1;
196 }
Andy Hungb7aadb32019-12-09 19:40:42 -0800197 // for speed
198 static size_t sizeOfByteString(const char *name, const char *value) {
199 return 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
200 }
201
202 template <typename T>
203 static status_t insert(const T& val, char **bufferpptr, char *bufferptrmax) {
204 static_assert(std::is_trivially_constructible<T>::value);
205 const size_t size = sizeof(val);
206 if (*bufferpptr + size > bufferptrmax) {
207 ALOGE("%s: buffer exceeded with size %zu", __func__, size);
208 return BAD_VALUE;
209 }
210 memcpy(*bufferpptr, &val, size);
211 *bufferpptr += size;
212 return NO_ERROR;
213 }
214 template <> // static
215 status_t insert(const std::string& val, char **bufferpptr, char *bufferptrmax) {
216 const size_t size = val.size() + 1;
217 if (size > UINT16_MAX || *bufferpptr + size > bufferptrmax) {
218 ALOGE("%s: buffer exceeded with size %zu", __func__, size);
219 return BAD_VALUE;
220 }
221 memcpy(*bufferpptr, val.c_str(), size);
222 *bufferpptr += size;
223 return NO_ERROR;
224 }
225 template <> // static
226 status_t insert(const std::pair<int64_t, int64_t>& val,
227 char **bufferpptr, char *bufferptrmax) {
228 const size_t size = sizeof(val.first) + sizeof(val.second);
229 if (*bufferpptr + size > bufferptrmax) {
230 ALOGE("%s: buffer exceeded with size %zu", __func__, size);
231 return BAD_VALUE;
232 }
233 memcpy(*bufferpptr, &val.first, sizeof(val.first));
234 memcpy(*bufferpptr + sizeof(val.first), &val.second, sizeof(val.second));
235 *bufferpptr += size;
236 return NO_ERROR;
237 }
238 template <> // static
239 status_t insert(const std::monostate&, char **, char *) {
240 return NO_ERROR;
241 }
242 // for speed
243 static status_t insert(const char *val, char **bufferpptr, char *bufferptrmax) {
244 const size_t size = strlen(val) + 1;
245 if (size > UINT16_MAX || *bufferpptr + size > bufferptrmax) {
246 ALOGE("%s: buffer exceeded with size %zu", __func__, size);
247 return BAD_VALUE;
248 }
249 memcpy(*bufferpptr, val, size);
250 *bufferpptr += size;
251 return NO_ERROR;
252 }
253
254 template <typename T>
255 static status_t writeToByteString(
256 const char *name, const T& value, char **bufferpptr, char *bufferptrmax) {
257 static_assert(is_item_type<T>::value);
258 const size_t len = sizeOfByteString(name, value);
259 if (len > UINT16_MAX) return BAD_VALUE;
260 return insert((uint16_t)len, bufferpptr, bufferptrmax)
261 ?: insert((uint8_t)get_type_of<T>::value, bufferpptr, bufferptrmax)
262 ?: insert(name, bufferpptr, bufferptrmax)
263 ?: insert(value, bufferpptr, bufferptrmax);
264 }
265 // for speed
266 static status_t writeToByteString(
267 const char *name, const char *value, char **bufferpptr, char *bufferptrmax) {
268 const size_t len = sizeOfByteString(name, value);
269 if (len > UINT16_MAX) return BAD_VALUE;
270 return insert((uint16_t)len, bufferpptr, bufferptrmax)
271 ?: insert((uint8_t)kTypeCString, bufferpptr, bufferptrmax)
272 ?: insert(name, bufferpptr, bufferptrmax)
273 ?: insert(value, bufferpptr, bufferptrmax);
274 }
275
276 template <typename T>
277 static void toStringBuffer(
278 const char *name, const T& value, char *buffer, size_t length) = delete;
279 template <> // static
280 void toStringBuffer(
281 const char *name, const int32_t& value, char *buffer, size_t length) {
282 snprintf(buffer, length, "%s=%d:", name, value);
283 }
284 template <> // static
285 void toStringBuffer(
286 const char *name, const int64_t& value, char *buffer, size_t length) {
287 snprintf(buffer, length, "%s=%lld:", name, (long long)value);
288 }
289 template <> // static
290 void toStringBuffer(
291 const char *name, const double& value, char *buffer, size_t length) {
292 snprintf(buffer, length, "%s=%e:", name, value);
293 }
294 template <> // static
295 void toStringBuffer(
296 const char *name, const std::pair<int64_t, int64_t>& value,
297 char *buffer, size_t length) {
298 snprintf(buffer, length, "%s=%lld/%lld:",
299 name, (long long)value.first, (long long)value.second);
300 }
301 template <> // static
302 void toStringBuffer(
303 const char *name, const std::string& value, char *buffer, size_t length) {
304 // TODO sanitize string for ':' '='
305 snprintf(buffer, length, "%s=%s:", name, value.c_str());
306 }
307 template <> // static
308 void toStringBuffer(
309 const char *name, const std::monostate&, char *buffer, size_t length) {
310 snprintf(buffer, length, "%s=():", name);
311 }
312
313 template <typename T>
314 static status_t writeToParcel(
315 const char *name, const T& value, Parcel *parcel) = delete;
316 template <> // static
317 status_t writeToParcel(
318 const char *name, const int32_t& value, Parcel *parcel) {
319 return parcel->writeCString(name)
320 ?: parcel->writeInt32(get_type_of<int32_t>::value)
321 ?: parcel->writeInt32(value);
322 }
323 template <> // static
324 status_t writeToParcel(
325 const char *name, const int64_t& value, Parcel *parcel) {
326 return parcel->writeCString(name)
327 ?: parcel->writeInt32(get_type_of<int64_t>::value)
328 ?: parcel->writeInt64(value);
329 }
330 template <> // static
331 status_t writeToParcel(
332 const char *name, const double& value, Parcel *parcel) {
333 return parcel->writeCString(name)
334 ?: parcel->writeInt32(get_type_of<double>::value)
335 ?: parcel->writeDouble(value);
336 }
337 template <> // static
338 status_t writeToParcel(
339 const char *name, const std::pair<int64_t, int64_t>& value, Parcel *parcel) {
340 return parcel->writeCString(name)
341 ?: parcel->writeInt32(get_type_of< std::pair<int64_t, int64_t>>::value)
342 ?: parcel->writeInt64(value.first)
343 ?: parcel->writeInt64(value.second);
344 }
345 template <> // static
346 status_t writeToParcel(
347 const char *name, const std::string& value, Parcel *parcel) {
348 return parcel->writeCString(name)
349 ?: parcel->writeInt32(get_type_of<std::string>::value)
350 ?: parcel->writeCString(value.c_str());
351 }
352 template <> // static
353 status_t writeToParcel(
354 const char *name, const std::monostate&, Parcel *parcel) {
355 return parcel->writeCString(name)
356 ?: parcel->writeInt32(get_type_of<std::monostate>::value);
357 }
358
359 template <typename T>
360 static status_t extract(T *val, const char **bufferpptr, const char *bufferptrmax) {
361 static_assert(std::is_trivially_constructible<T>::value);
362 const size_t size = sizeof(*val);
363 if (*bufferpptr + size > bufferptrmax) {
364 ALOGE("%s: buffer exceeded with size %zu", __func__, size);
365 return BAD_VALUE;
366 }
367 memcpy(val, *bufferpptr, size);
368 *bufferpptr += size;
369 return NO_ERROR;
370 }
371 template <> // static
372 status_t extract(std::string *val, const char **bufferpptr, const char *bufferptrmax) {
373 const char *ptr = *bufferpptr;
374 while (*ptr != 0) {
375 if (ptr >= bufferptrmax) {
376 ALOGE("%s: buffer exceeded", __func__);
377 return BAD_VALUE;
378 }
379 ++ptr;
380 }
381 const size_t size = (ptr - *bufferpptr) + 1;
382 *val = *bufferpptr;
383 *bufferpptr += size;
384 return NO_ERROR;
385 }
386 template <> // static
387 status_t extract(std::pair<int64_t, int64_t> *val,
388 const char **bufferpptr, const char *bufferptrmax) {
389 const size_t size = sizeof(val->first) + sizeof(val->second);
390 if (*bufferpptr + size > bufferptrmax) {
391 ALOGE("%s: buffer exceeded with size %zu", __func__, size);
392 return BAD_VALUE;
393 }
394 memcpy(&val->first, *bufferpptr, sizeof(val->first));
395 memcpy(&val->second, *bufferpptr + sizeof(val->first), sizeof(val->second));
396 *bufferpptr += size;
397 return NO_ERROR;
398 }
399 template <> // static
400 status_t extract(std::monostate *, const char **, const char *) {
401 return NO_ERROR;
402 }
Andy Hung1efc9c62019-12-03 13:43:33 -0800403};
404
405/**
406 * Media Metrics BufferedItem
407 *
408 * A base class which represents a put-only Media Metrics item, storing
409 * the Media Metrics data in a buffer with begin and end pointers.
410 *
411 * If a property key is entered twice, it will be stored in the buffer twice,
412 * and (implementation defined) the last value for that key will be used
413 * by the Media Metrics service.
414 *
415 * For realloc, a baseRealloc pointer must be passed in either explicitly
416 * or implicitly in the constructor. This will be updated with the value used on realloc.
417 */
418class BufferedItem : public BaseItem {
419public:
420 static inline constexpr uint16_t kVersion = 0;
421
422 virtual ~BufferedItem() = default;
423 BufferedItem(const BufferedItem&) = delete;
424 BufferedItem& operator=(const BufferedItem&) = delete;
425
426 BufferedItem(const std::string key, char *begin, char *end)
427 : BufferedItem(key.c_str(), begin, end) { }
428
429 BufferedItem(const char *key, char *begin, char *end)
430 : BufferedItem(key, begin, end, nullptr) { }
431
432 BufferedItem(const char *key, char **begin, char *end)
433 : BufferedItem(key, *begin, end, begin) { }
434
435 BufferedItem(const char *key, char *begin, char *end, char **baseRealloc)
436 : mBegin(begin)
437 , mEnd(end)
438 , mBaseRealloc(baseRealloc)
439 {
440 init(key);
441 }
442
443 template<typename T>
444 BufferedItem &set(const char *key, const T& value) {
445 reallocFor(sizeOfByteString(key, value));
446 if (mStatus == NO_ERROR) {
447 mStatus = BaseItem::writeToByteString(key, value, &mBptr, mEnd);
448 ++mPropCount;
449 }
450 return *this;
451 }
452
453 template<typename T>
454 BufferedItem &set(const std::string& key, const T& value) {
455 return set(key.c_str(), value);
456 }
457
458 BufferedItem &setPid(pid_t pid) {
459 if (mStatus == NO_ERROR) {
460 copyTo(mBegin + mHeaderLen - 16, (int32_t)pid);
461 }
462 return *this;
463 }
464
465 BufferedItem &setUid(uid_t uid) {
466 if (mStatus == NO_ERROR) {
467 copyTo(mBegin + mHeaderLen - 12, (int32_t)uid);
468 }
469 return *this;
470 }
471
472 BufferedItem &setTimestamp(nsecs_t timestamp) {
473 if (mStatus == NO_ERROR) {
474 copyTo(mBegin + mHeaderLen - 8, (int64_t)timestamp);
475 }
476 return *this;
477 }
478
479 bool record() {
480 return updateHeader()
481 && BaseItem::submitBuffer(getBuffer(), getLength());
482 }
483
484 bool isValid () const {
485 return mStatus == NO_ERROR;
486 }
487
488 char *getBuffer() const { return mBegin; }
489 size_t getLength() const { return mBptr - mBegin; }
490 size_t getRemaining() const { return mEnd - mBptr; }
491 size_t getCapacity() const { return mEnd - mBegin; }
492
493 bool updateHeader() {
494 if (mStatus != NO_ERROR) return false;
495 copyTo(mBegin + 0, (uint32_t)getLength());
496 copyTo(mBegin + 4, (uint32_t)mHeaderLen);
497 copyTo(mBegin + mHeaderLen, (uint32_t)mPropCount);
498 return true;
499 }
500
501protected:
502 BufferedItem() = default;
503
504 void reallocFor(size_t required) {
505 if (mStatus != NO_ERROR) return;
506 const size_t remaining = getRemaining();
507 if (required <= remaining) return;
508 if (mBaseRealloc == nullptr) {
509 mStatus = NO_MEMORY;
510 return;
511 }
512
513 const size_t current = getLength();
514 size_t minimum = current + required;
515 if (minimum > SSIZE_MAX >> 1) {
516 mStatus = NO_MEMORY;
517 return;
518 }
519 minimum <<= 1;
520 void *newptr = realloc(*mBaseRealloc, minimum);
521 if (newptr == nullptr) {
522 mStatus = NO_MEMORY;
523 return;
524 }
525 if (newptr != *mBaseRealloc) {
526 // ALOGD("base changed! current:%zu new size %zu", current, minimum);
527 if (*mBaseRealloc == nullptr) {
528 memcpy(newptr, mBegin, current);
529 }
530 mBegin = (char *)newptr;
531 *mBaseRealloc = mBegin;
532 mEnd = mBegin + minimum;
533 mBptr = mBegin + current;
534 } else {
535 // ALOGD("base kept! current:%zu new size %zu", current, minimum);
536 mEnd = mBegin + minimum;
537 }
538 }
539 template<typename T>
540 void copyTo(char *ptr, const T& value) {
541 memcpy(ptr, &value, sizeof(value));
542 }
543
544 void init(const char *key) {
545 mBptr = mBegin;
546 const size_t keylen = strlen(key) + 1;
547 mHeaderLen = 4 + 4 + 2 + 2 + keylen + 4 + 4 + 8;
548 reallocFor(mHeaderLen);
549 if (mStatus != NO_ERROR) return;
550 mBptr = mBegin + mHeaderLen + 4; // this includes propcount.
551
552 if (mEnd < mBptr || keylen > UINT16_MAX) {
553 mStatus = NO_MEMORY;
554 mBptr = mEnd;
555 return;
556 }
557 copyTo(mBegin + 8, kVersion);
558 copyTo(mBegin + 10, (uint16_t)keylen);
559 strcpy(mBegin + 12, key);
560
561 // initialize some parameters (that could be overridden)
562 setPid(-1);
563 setUid(-1);
564 setTimestamp(0);
565 }
566
567 char *mBegin = nullptr;
568 char *mEnd = nullptr;
569 char **mBaseRealloc = nullptr; // set to an address if realloc should be done.
570 // upon return, that pointer is updated with
571 // whatever needs to be freed.
572 char *mBptr = nullptr;
573 status_t mStatus = NO_ERROR;
574 uint32_t mPropCount = 0;
575 uint32_t mHeaderLen = 0;
576};
577
578/**
Andy Hung47e58d62019-12-06 18:40:19 -0800579 * MediaMetrics LogItem is a stack allocated mediametrics item used for
Andy Hung1efc9c62019-12-03 13:43:33 -0800580 * fast logging. It falls over to a malloc if needed.
581 *
582 * This is templated with a buffer size to allocate on the stack.
583 */
584template <size_t N = 4096>
Ray Essickf27e9872019-12-07 06:28:46 -0800585class LogItem : public BufferedItem {
Andy Hung1efc9c62019-12-03 13:43:33 -0800586public:
Ray Essickf27e9872019-12-07 06:28:46 -0800587 explicit LogItem(const std::string key) : LogItem(key.c_str()) { }
Andy Hung1efc9c62019-12-03 13:43:33 -0800588
589 // Since this class will not be defined before the base class, we initialize variables
590 // in our own order.
Ray Essickf27e9872019-12-07 06:28:46 -0800591 explicit LogItem(const char *key) {
Andy Hung1efc9c62019-12-03 13:43:33 -0800592 mBegin = mBuffer;
593 mEnd = mBuffer + N;
594 mBaseRealloc = &mReallocPtr;
595 init(key);
596 }
597
Ray Essickf27e9872019-12-07 06:28:46 -0800598 ~LogItem() override {
Andy Hung1efc9c62019-12-03 13:43:33 -0800599 if (mReallocPtr != nullptr) { // do the check before calling free to avoid overhead.
600 free(mReallocPtr);
601 }
602 }
603
604private:
605 char *mReallocPtr = nullptr; // set non-null by base class if realloc happened.
606 char mBuffer[N];
607};
608
Andy Hung1efc9c62019-12-03 13:43:33 -0800609
Andy Hung3253f2d2019-10-21 14:50:07 -0700610/**
Ray Essickf27e9872019-12-07 06:28:46 -0800611 * Media Metrics Item
Andy Hung3253f2d2019-10-21 14:50:07 -0700612 *
613 * A mutable item representing an event or record that will be
Andy Hung1efc9c62019-12-03 13:43:33 -0800614 * logged with the Media Metrics service. For client logging, one should
615 * use the mediametrics::Item.
Andy Hung3253f2d2019-10-21 14:50:07 -0700616 *
Ray Essickf27e9872019-12-07 06:28:46 -0800617 * The Item is designed for the service as it has getters.
Andy Hung3253f2d2019-10-21 14:50:07 -0700618 */
Ray Essickf27e9872019-12-07 06:28:46 -0800619class Item : public mediametrics::BaseItem {
Andy Hungaeef7882019-10-18 15:18:14 -0700620public:
Ray Essick3938dc62016-11-01 08:56:56 -0700621
Ray Essickf65f4212017-08-31 11:41:19 -0700622 enum {
623 PROTO_V0 = 0,
624 PROTO_FIRST = PROTO_V0,
625 PROTO_V1 = 1,
626 PROTO_LAST = PROTO_V1,
627 };
628
Andy Hungaeef7882019-10-18 15:18:14 -0700629 // T must be convertible to mKey
630 template <typename T>
Ray Essickf27e9872019-12-07 06:28:46 -0800631 explicit Item(T key)
Andy Hungaeef7882019-10-18 15:18:14 -0700632 : mKey(key) { }
Ray Essickf27e9872019-12-07 06:28:46 -0800633 Item() = default;
Andy Hung3253f2d2019-10-21 14:50:07 -0700634
Ray Essickf27e9872019-12-07 06:28:46 -0800635 bool operator==(const Item& other) const {
Andy Hung47e58d62019-12-06 18:40:19 -0800636 if (mPid != other.mPid
Andy Hung3253f2d2019-10-21 14:50:07 -0700637 || mUid != other.mUid
638 || mPkgName != other.mPkgName
639 || mPkgVersionCode != other.mPkgVersionCode
640 || mKey != other.mKey
Andy Hung47e58d62019-12-06 18:40:19 -0800641 || mTimestamp != other.mTimestamp
642 || mProps != other.mProps) return false;
Andy Hung3253f2d2019-10-21 14:50:07 -0700643 return true;
644 }
Ray Essickf27e9872019-12-07 06:28:46 -0800645 bool operator!=(const Item& other) const {
Andy Hung3253f2d2019-10-21 14:50:07 -0700646 return !(*this == other);
647 }
648
649 template <typename T>
Ray Essickf27e9872019-12-07 06:28:46 -0800650 static Item* create(T key) {
651 return new Item(key);
Andy Hung3253f2d2019-10-21 14:50:07 -0700652 }
Ray Essickf27e9872019-12-07 06:28:46 -0800653 static Item* create() {
654 return new Item();
Andy Hung3253f2d2019-10-21 14:50:07 -0700655 }
Ray Essickba8c4842019-01-18 11:35:33 -0800656
Ray Essickf27e9872019-12-07 06:28:46 -0800657 static Item* convert(mediametrics_handle_t);
658 static mediametrics_handle_t convert(Item *);
Ray Essickbf536ac2019-08-26 11:04:28 -0700659
Ray Essick3938dc62016-11-01 08:56:56 -0700660 // access functions for the class
Ray Essickf27e9872019-12-07 06:28:46 -0800661 ~Item();
Ray Essick3938dc62016-11-01 08:56:56 -0700662
Andy Hung47e58d62019-12-06 18:40:19 -0800663 void clear() {
664 mPid = -1;
665 mUid = -1;
666 mPkgName.clear();
667 mPkgVersionCode = 0;
668 mTimestamp = 0;
669 mKey.clear();
670 mProps.clear();
671 }
672
673 Item *dup() const { return new Item(*this); }
Ray Essick3938dc62016-11-01 08:56:56 -0700674
Ray Essickf27e9872019-12-07 06:28:46 -0800675 Item &setKey(const char *key) {
Andy Hung3253f2d2019-10-21 14:50:07 -0700676 mKey = key;
677 return *this;
678 }
679 const std::string& getKey() const { return mKey; }
Ray Essick3938dc62016-11-01 08:56:56 -0700680
Andy Hung3253f2d2019-10-21 14:50:07 -0700681 // # of properties in the record
Andy Hung47e58d62019-12-06 18:40:19 -0800682 size_t count() const { return mProps.size(); }
Ray Essick3938dc62016-11-01 08:56:56 -0700683
Andy Hungaeef7882019-10-18 15:18:14 -0700684 template<typename S, typename T>
Ray Essickf27e9872019-12-07 06:28:46 -0800685 Item &set(S key, T value) {
Andy Hung47e58d62019-12-06 18:40:19 -0800686 findOrAllocateProp(key).set(value);
Andy Hungaeef7882019-10-18 15:18:14 -0700687 return *this;
688 }
Ray Essick3938dc62016-11-01 08:56:56 -0700689
Andy Hungaeef7882019-10-18 15:18:14 -0700690 // set values appropriately
Ray Essickf27e9872019-12-07 06:28:46 -0800691 Item &setInt32(const char *key, int32_t value) {
Andy Hungaeef7882019-10-18 15:18:14 -0700692 return set(key, value);
693 }
Ray Essickf27e9872019-12-07 06:28:46 -0800694 Item &setInt64(const char *key, int64_t value) {
Andy Hungaeef7882019-10-18 15:18:14 -0700695 return set(key, value);
696 }
Ray Essickf27e9872019-12-07 06:28:46 -0800697 Item &setDouble(const char *key, double value) {
Andy Hungaeef7882019-10-18 15:18:14 -0700698 return set(key, value);
699 }
Ray Essickf27e9872019-12-07 06:28:46 -0800700 Item &setRate(const char *key, int64_t count, int64_t duration) {
Andy Hungaeef7882019-10-18 15:18:14 -0700701 return set(key, std::make_pair(count, duration));
702 }
Ray Essickf27e9872019-12-07 06:28:46 -0800703 Item &setCString(const char *key, const char *value) {
Andy Hungaeef7882019-10-18 15:18:14 -0700704 return set(key, value);
705 }
Ray Essick3938dc62016-11-01 08:56:56 -0700706
Andy Hungaeef7882019-10-18 15:18:14 -0700707 // fused get/add/set; if attr wasn't there, it's a simple set.
708 // type-mismatch counts as "wasn't there".
709 template<typename S, typename T>
Ray Essickf27e9872019-12-07 06:28:46 -0800710 Item &add(S key, T value) {
Andy Hung47e58d62019-12-06 18:40:19 -0800711 findOrAllocateProp(key).add(value);
Andy Hungaeef7882019-10-18 15:18:14 -0700712 return *this;
713 }
714
Ray Essickf27e9872019-12-07 06:28:46 -0800715 Item &addInt32(const char *key, int32_t value) {
Andy Hungaeef7882019-10-18 15:18:14 -0700716 return add(key, value);
717 }
Ray Essickf27e9872019-12-07 06:28:46 -0800718 Item &addInt64(const char *key, int64_t value) {
Andy Hungaeef7882019-10-18 15:18:14 -0700719 return add(key, value);
720 }
Ray Essickf27e9872019-12-07 06:28:46 -0800721 Item &addDouble(const char *key, double value) {
Andy Hungaeef7882019-10-18 15:18:14 -0700722 return add(key, value);
723 }
Ray Essickf27e9872019-12-07 06:28:46 -0800724 Item &addRate(const char *key, int64_t count, int64_t duration) {
Andy Hungaeef7882019-10-18 15:18:14 -0700725 return add(key, std::make_pair(count, duration));
726 }
727
728 // find & extract values
729 // return indicates whether attr exists (and thus value filled in)
730 // NULL parameter value suppresses storage of value.
731 template<typename S, typename T>
732 bool get(S key, T *value) const {
Andy Hung47e58d62019-12-06 18:40:19 -0800733 const Prop *prop = findProp(key);
Andy Hungaeef7882019-10-18 15:18:14 -0700734 return prop != nullptr && prop->get(value);
735 }
736
Andy Hung3253f2d2019-10-21 14:50:07 -0700737 bool getInt32(const char *key, int32_t *value) const {
Andy Hungaeef7882019-10-18 15:18:14 -0700738 return get(key, value);
739 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700740 bool getInt64(const char *key, int64_t *value) const {
Andy Hungaeef7882019-10-18 15:18:14 -0700741 return get(key, value);
742 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700743 bool getDouble(const char *key, double *value) const {
Andy Hungaeef7882019-10-18 15:18:14 -0700744 return get(key, value);
745 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700746 bool getRate(const char *key, int64_t *count, int64_t *duration, double *rate) const {
Andy Hungaeef7882019-10-18 15:18:14 -0700747 std::pair<int64_t, int64_t> value;
748 if (!get(key, &value)) return false;
749 if (count != nullptr) *count = value.first;
750 if (duration != nullptr) *duration = value.second;
751 if (rate != nullptr) {
752 if (value.second != 0) {
753 *rate = (double)value.first / value.second; // TODO: isn't INF OK?
754 } else {
755 *rate = 0.;
756 }
757 }
758 return true;
759 }
760 // Caller owns the returned string
Andy Hung3253f2d2019-10-21 14:50:07 -0700761 bool getCString(const char *key, char **value) const {
Andy Hungb7aadb32019-12-09 19:40:42 -0800762 std::string s;
763 if (get(key, &s)) {
764 *value = strdup(s.c_str());
Andy Hung3253f2d2019-10-21 14:50:07 -0700765 return true;
766 }
767 return false;
Andy Hungaeef7882019-10-18 15:18:14 -0700768 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700769 bool getString(const char *key, std::string *value) const {
Andy Hungaeef7882019-10-18 15:18:14 -0700770 return get(key, value);
771 }
Ray Essick3938dc62016-11-01 08:56:56 -0700772
Andy Hunga87e69c2019-10-18 10:07:40 -0700773 // Deliver the item to MediaMetrics
Ray Essick3938dc62016-11-01 08:56:56 -0700774 bool selfrecord();
775
Andy Hung3253f2d2019-10-21 14:50:07 -0700776 // remove indicated attributes and their values
777 // filterNot() could also be called keepOnly()
778 // return value is # attributes removed
779 // XXX: perhaps 'remove' instead of 'filter'
780 // XXX: filterNot would become 'keep'
781 size_t filter(size_t count, const char *attrs[]);
782 size_t filterNot(size_t count, const char *attrs[]);
783 size_t filter(const char *attr) { return filter(1, &attr); }
Ray Essick3938dc62016-11-01 08:56:56 -0700784
785 // below here are used on server side or to talk to server
786 // clients need not worry about these.
787
788 // timestamp, pid, and uid only used on server side
Ray Essickb5fac8e2016-12-12 11:33:56 -0800789 // timestamp is in 'nanoseconds, unix time'
Ray Essickf27e9872019-12-07 06:28:46 -0800790 Item &setTimestamp(nsecs_t);
Ray Essick3938dc62016-11-01 08:56:56 -0700791 nsecs_t getTimestamp() const;
792
Ray Essickf27e9872019-12-07 06:28:46 -0800793 Item &setPid(pid_t);
Ray Essick3938dc62016-11-01 08:56:56 -0700794 pid_t getPid() const;
795
Ray Essickf27e9872019-12-07 06:28:46 -0800796 Item &setUid(uid_t);
Ray Essick3938dc62016-11-01 08:56:56 -0700797 uid_t getUid() const;
798
Ray Essickf27e9872019-12-07 06:28:46 -0800799 Item &setPkgName(const std::string &pkgName);
Ray Essick783bd0d2018-01-11 11:10:35 -0800800 std::string getPkgName() const { return mPkgName; }
Ray Essickf65f4212017-08-31 11:41:19 -0700801
Ray Essickf27e9872019-12-07 06:28:46 -0800802 Item &setPkgVersionCode(int64_t);
Dianne Hackborn4e2eeff2017-11-27 14:01:29 -0800803 int64_t getPkgVersionCode() const;
Ray Essickf65f4212017-08-31 11:41:19 -0700804
Andy Hung3253f2d2019-10-21 14:50:07 -0700805 // our serialization code for binder calls
806 status_t writeToParcel(Parcel *) const;
807 status_t readFromParcel(const Parcel&);
Ray Essick3938dc62016-11-01 08:56:56 -0700808
Andy Hung3253f2d2019-10-21 14:50:07 -0700809 status_t writeToByteString(char **bufferptr, size_t *length) const;
810 status_t readFromByteString(const char *bufferptr, size_t length);
811
Ray Essickba8c4842019-01-18 11:35:33 -0800812
Andy Hung17dbaf22019-10-11 14:06:31 -0700813 std::string toString() const;
814 std::string toString(int version) const;
Ray Essick20147322018-11-17 09:08:39 -0800815 const char *toCString();
816 const char *toCString(int version);
Ray Essick3938dc62016-11-01 08:56:56 -0700817
Andy Hung3253f2d2019-10-21 14:50:07 -0700818private:
819 // handle Parcel version 0
820 int32_t writeToParcel0(Parcel *) const;
821 int32_t readFromParcel0(const Parcel&);
822
Andy Hung3253f2d2019-10-21 14:50:07 -0700823public:
Andy Hungb7aadb32019-12-09 19:40:42 -0800824
Andy Hungaeef7882019-10-18 15:18:14 -0700825 class Prop {
Andy Hungaeef7882019-10-18 15:18:14 -0700826 public:
Andy Hungb7aadb32019-12-09 19:40:42 -0800827 using Elem = std::variant<
828 std::monostate, // kTypeNone
829 int32_t, // kTypeInt32
830 int64_t, // kTypeInt64
831 double, // kTypeDouble
832 std::string, // kTypeCString
833 std::pair<int64_t, int64_t> // kTypeRate
834 >;
835
Andy Hungaeef7882019-10-18 15:18:14 -0700836 Prop() = default;
837 Prop(const Prop& other) {
838 *this = other;
839 }
840 Prop& operator=(const Prop& other) {
Andy Hung47e58d62019-12-06 18:40:19 -0800841 mName = other.mName;
Andy Hungb7aadb32019-12-09 19:40:42 -0800842 mElem = other.mElem;
Andy Hungaeef7882019-10-18 15:18:14 -0700843 return *this;
844 }
Andy Hung47e58d62019-12-06 18:40:19 -0800845 Prop(Prop&& other) {
846 *this = std::move(other);
847 }
848 Prop& operator=(Prop&& other) {
849 mName = std::move(other.mName);
Andy Hungb7aadb32019-12-09 19:40:42 -0800850 mElem = std::move(other.mElem);
Andy Hung47e58d62019-12-06 18:40:19 -0800851 return *this;
852 }
853
Andy Hung3253f2d2019-10-21 14:50:07 -0700854 bool operator==(const Prop& other) const {
Andy Hungb7aadb32019-12-09 19:40:42 -0800855 return mName == other.mName && mElem == other.mElem;
Andy Hung3253f2d2019-10-21 14:50:07 -0700856 }
857 bool operator!=(const Prop& other) const {
858 return !(*this == other);
859 }
Ray Essick3938dc62016-11-01 08:56:56 -0700860
Andy Hungaeef7882019-10-18 15:18:14 -0700861 void clear() {
Andy Hung47e58d62019-12-06 18:40:19 -0800862 mName.clear();
Andy Hungb7aadb32019-12-09 19:40:42 -0800863 mElem = std::monostate{};
Andy Hungaeef7882019-10-18 15:18:14 -0700864 }
865 void clearValue() {
Andy Hungb7aadb32019-12-09 19:40:42 -0800866 mElem = std::monostate{};
Andy Hungaeef7882019-10-18 15:18:14 -0700867 }
Ray Essick3938dc62016-11-01 08:56:56 -0700868
Andy Hungaeef7882019-10-18 15:18:14 -0700869 const char *getName() const {
Andy Hung47e58d62019-12-06 18:40:19 -0800870 return mName.c_str();
Andy Hungaeef7882019-10-18 15:18:14 -0700871 }
Ray Essick3938dc62016-11-01 08:56:56 -0700872
Andy Hungaeef7882019-10-18 15:18:14 -0700873 void swap(Prop& other) {
874 std::swap(mName, other.mName);
Andy Hungb7aadb32019-12-09 19:40:42 -0800875 std::swap(mElem, other.mElem);
Andy Hungaeef7882019-10-18 15:18:14 -0700876 }
Ray Essickb5fac8e2016-12-12 11:33:56 -0800877
Andy Hung3253f2d2019-10-21 14:50:07 -0700878 void setName(const char *name) {
Andy Hung47e58d62019-12-06 18:40:19 -0800879 mName = name;
Andy Hungaeef7882019-10-18 15:18:14 -0700880 }
881
Andy Hungaeef7882019-10-18 15:18:14 -0700882 bool isNamed(const char *name) const {
Andy Hung47e58d62019-12-06 18:40:19 -0800883 return mName == name;
Andy Hung1efc9c62019-12-03 13:43:33 -0800884 }
885
886 template <typename T> void visit(T f) const {
Andy Hungb7aadb32019-12-09 19:40:42 -0800887 std::visit(f, mElem);
888 }
889
890 template <typename T> bool get(T *value) const {
891 auto pval = std::get_if<T>(&mElem);
892 if (pval != nullptr) {
893 *value = *pval;
894 return true;
895 }
896 return false;
897 }
898
899 template <typename T> void set(const T& value) {
900 mElem = value;
901 }
902
903 template <typename T> void add(const T& value) {
904 auto pval = std::get_if<T>(&mElem);
905 if (pval != nullptr) {
906 *pval += value;
907 } else {
908 mElem = value;
Andy Hung1efc9c62019-12-03 13:43:33 -0800909 }
Andy Hungaeef7882019-10-18 15:18:14 -0700910 }
911
Andy Hungb7aadb32019-12-09 19:40:42 -0800912 template <> void add(const std::pair<int64_t, int64_t>& value) {
913 auto pval = std::get_if<std::pair<int64_t, int64_t>>(&mElem);
914 if (pval != nullptr) {
915 pval->first += value.first;
916 pval->second += value.second;
Andy Hungaeef7882019-10-18 15:18:14 -0700917 } else {
Andy Hungb7aadb32019-12-09 19:40:42 -0800918 mElem = value;
Andy Hungaeef7882019-10-18 15:18:14 -0700919 }
920 }
921
Andy Hungb7aadb32019-12-09 19:40:42 -0800922 status_t writeToParcel(Parcel *parcel) const {
923 return std::visit([this, parcel](auto &value) {
924 return BaseItem::writeToParcel(mName.c_str(), value, parcel);}, mElem);
925 }
926
927 void toStringBuffer(char *buffer, size_t length) const {
928 return std::visit([this, buffer, length](auto &value) {
929 BaseItem::toStringBuffer(mName.c_str(), value, buffer, length);}, mElem);
930 }
931
932 size_t getByteStringSize() const {
933 return std::visit([this](auto &value) {
934 return BaseItem::sizeOfByteString(mName.c_str(), value);}, mElem);
935 }
936
937 status_t writeToByteString(char **bufferpptr, char *bufferptrmax) const {
938 return std::visit([this, bufferpptr, bufferptrmax](auto &value) {
939 return BaseItem::writeToByteString(mName.c_str(), value, bufferpptr, bufferptrmax);
940 }, mElem);
941 }
942
Andy Hung3253f2d2019-10-21 14:50:07 -0700943 status_t readFromParcel(const Parcel& data);
Andy Hungb7aadb32019-12-09 19:40:42 -0800944
Andy Hung3253f2d2019-10-21 14:50:07 -0700945 status_t readFromByteString(const char **bufferpptr, const char *bufferptrmax);
Andy Hungaeef7882019-10-18 15:18:14 -0700946
Andy Hung47e58d62019-12-06 18:40:19 -0800947 private:
948 std::string mName;
Andy Hungb7aadb32019-12-09 19:40:42 -0800949 Elem mElem;
Andy Hungaeef7882019-10-18 15:18:14 -0700950 };
951
Andy Hung47e58d62019-12-06 18:40:19 -0800952 // Iteration of props within item
Andy Hung3253f2d2019-10-21 14:50:07 -0700953 class iterator {
954 public:
Andy Hung47e58d62019-12-06 18:40:19 -0800955 iterator(const std::map<std::string, Prop>::const_iterator &_it) : it(_it) { }
956 iterator &operator++() {
957 ++it;
958 return *this;
959 }
960 bool operator!=(iterator &other) const {
961 return it != other.it;
962 }
963 const Prop &operator*() const {
964 return it->second;
965 }
Andy Hung3253f2d2019-10-21 14:50:07 -0700966
967 private:
Andy Hung47e58d62019-12-06 18:40:19 -0800968 std::map<std::string, Prop>::const_iterator it;
Andy Hung3253f2d2019-10-21 14:50:07 -0700969 };
970
971 iterator begin() const {
Andy Hung47e58d62019-12-06 18:40:19 -0800972 return iterator(mProps.cbegin());
Andy Hung3253f2d2019-10-21 14:50:07 -0700973 }
Andy Hung47e58d62019-12-06 18:40:19 -0800974
Andy Hung3253f2d2019-10-21 14:50:07 -0700975 iterator end() const {
Andy Hung47e58d62019-12-06 18:40:19 -0800976 return iterator(mProps.cend());
Andy Hung3253f2d2019-10-21 14:50:07 -0700977 }
978
979private:
980
Andy Hung47e58d62019-12-06 18:40:19 -0800981 const Prop *findProp(const char *key) const {
982 auto it = mProps.find(key);
983 return it != mProps.end() ? &it->second : nullptr;
984 }
Andy Hungaeef7882019-10-18 15:18:14 -0700985
Andy Hung47e58d62019-12-06 18:40:19 -0800986 Prop &findOrAllocateProp(const char *key) {
987 auto it = mProps.find(key);
988 if (it != mProps.end()) return it->second;
989 Prop &prop = mProps[key];
990 prop.setName(key);
991 return prop;
992 }
Andy Hungaeef7882019-10-18 15:18:14 -0700993
994 pid_t mPid = -1;
995 uid_t mUid = -1;
996 std::string mPkgName;
997 int64_t mPkgVersionCode = 0;
Andy Hung47e58d62019-12-06 18:40:19 -0800998 std::string mKey;
Andy Hungaeef7882019-10-18 15:18:14 -0700999 nsecs_t mTimestamp = 0;
Andy Hung47e58d62019-12-06 18:40:19 -08001000 std::map<std::string, Prop> mProps;
Ray Essick3938dc62016-11-01 08:56:56 -07001001};
1002
Ray Essickf27e9872019-12-07 06:28:46 -08001003} // namespace mediametrics
Ray Essick3938dc62016-11-01 08:56:56 -07001004} // namespace android
1005
Andy Hung47e58d62019-12-06 18:40:19 -08001006#endif // ANDROID_MEDIA_MEDIAMETRICSITEM_H