1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "mediametrics::Item"
18 
19 #include <inttypes.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/types.h>
23 
24 #include <mutex>
25 #include <set>
26 #include <unordered_map>
27 
28 #include <binder/Parcel.h>
29 #include <cutils/multiuser.h>
30 #include <cutils/properties.h>
31 #include <utils/Errors.h>
32 #include <utils/Log.h>
33 #include <utils/SortedVector.h>
34 #include <utils/threads.h>
35 
36 #include <android/media/BnMediaMetricsService.h> // for direct Binder access
37 #include <android/media/IMediaMetricsService.h>
38 #include <binder/IServiceManager.h>
39 #include <media/MediaMetricsItem.h>
40 #include <private/android_filesystem_config.h>
41 
42 // Max per-property string size before truncation in toString().
43 // Do not make too large, as this is used for dumpsys purposes.
44 static constexpr size_t kMaxPropertyStringSize = 4096;
45 
46 namespace android::mediametrics {
47 
48 #define DEBUG_SERVICEACCESS     0
49 #define DEBUG_API               0
50 #define DEBUG_ALLOCATIONS       0
51 
52 // after this many failed attempts, we stop trying [from this process] and just say that
53 // the service is off.
54 #define SVC_TRIES               2
55 
getErrorStringMap()56 static const std::unordered_map<std::string, int32_t>& getErrorStringMap() {
57     // DO NOT MODIFY VALUES (OK to add new ones).
58     // This may be found in frameworks/av/media/libmediametrics/include/MediaMetricsConstants.h
59     static std::unordered_map<std::string, int32_t> map{
60         {"",                                      NO_ERROR},
61         {AMEDIAMETRICS_PROP_STATUS_VALUE_OK,       NO_ERROR},
62         {AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT, BAD_VALUE},
63         {AMEDIAMETRICS_PROP_STATUS_VALUE_IO,       DEAD_OBJECT},
64         {AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY,   NO_MEMORY},
65         {AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY, PERMISSION_DENIED},
66         {AMEDIAMETRICS_PROP_STATUS_VALUE_STATE,    INVALID_OPERATION},
67         {AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT,  WOULD_BLOCK},
68         {AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN,  UNKNOWN_ERROR},
69     };
70     return map;
71 }
72 
statusStringToStatus(const char * error)73 status_t statusStringToStatus(const char *error) {
74     const auto& map = getErrorStringMap();
75     if (error == nullptr || error[0] == '\0') return NO_ERROR;
76     auto it = map.find(error);
77     if (it != map.end()) {
78         return it->second;
79     }
80     return UNKNOWN_ERROR;
81 }
82 
convert(mediametrics_handle_t handle)83 mediametrics::Item* mediametrics::Item::convert(mediametrics_handle_t handle) {
84     mediametrics::Item *item = (android::mediametrics::Item *) handle;
85     return item;
86 }
87 
convert(mediametrics::Item * item)88 mediametrics_handle_t mediametrics::Item::convert(mediametrics::Item *item ) {
89     mediametrics_handle_t handle = (mediametrics_handle_t) item;
90     return handle;
91 }
92 
~Item()93 mediametrics::Item::~Item() {
94     if (DEBUG_ALLOCATIONS) {
95         ALOGD("Destroy  mediametrics::Item @ %p", this);
96     }
97 }
98 
setTimestamp(nsecs_t ts)99 mediametrics::Item &mediametrics::Item::setTimestamp(nsecs_t ts) {
100     mTimestamp = ts;
101     return *this;
102 }
103 
getTimestamp() const104 nsecs_t mediametrics::Item::getTimestamp() const {
105     return mTimestamp;
106 }
107 
setPid(pid_t pid)108 mediametrics::Item &mediametrics::Item::setPid(pid_t pid) {
109     mPid = pid;
110     return *this;
111 }
112 
getPid() const113 pid_t mediametrics::Item::getPid() const {
114     return mPid;
115 }
116 
setUid(uid_t uid)117 mediametrics::Item &mediametrics::Item::setUid(uid_t uid) {
118     mUid = uid;
119     return *this;
120 }
121 
getUid() const122 uid_t mediametrics::Item::getUid() const {
123     return mUid;
124 }
125 
setPkgName(const std::string & pkgName)126 mediametrics::Item &mediametrics::Item::setPkgName(const std::string &pkgName) {
127     mPkgName = pkgName;
128     return *this;
129 }
130 
setPkgVersionCode(int64_t pkgVersionCode)131 mediametrics::Item &mediametrics::Item::setPkgVersionCode(int64_t pkgVersionCode) {
132     mPkgVersionCode = pkgVersionCode;
133     return *this;
134 }
135 
getPkgVersionCode() const136 int64_t mediametrics::Item::getPkgVersionCode() const {
137     return mPkgVersionCode;
138 }
139 
140 // remove indicated keys and their values
141 // return value is # keys removed
filter(size_t n,const char * attrs[])142 size_t mediametrics::Item::filter(size_t n, const char *attrs[]) {
143     size_t zapped = 0;
144     for (size_t i = 0; i < n; ++i) {
145         zapped += mProps.erase(attrs[i]);
146     }
147     return zapped;
148 }
149 
150 // remove any keys NOT in the provided list
151 // return value is # keys removed
filterNot(size_t n,const char * attrs[])152 size_t mediametrics::Item::filterNot(size_t n, const char *attrs[]) {
153     std::set<std::string> check(attrs, attrs + n);
154     size_t zapped = 0;
155     for (auto it = mProps.begin(); it != mProps.end();) {
156         if (check.find(it->first) != check.end()) {
157             ++it;
158         } else {
159            it = mProps.erase(it);
160            ++zapped;
161         }
162     }
163     return zapped;
164 }
165 
166 // Parcel / serialize things for binder calls
167 //
168 
readFromParcel(const Parcel & data)169 status_t mediametrics::Item::readFromParcel(const Parcel& data) {
170     int32_t version;
171     status_t status = data.readInt32(&version);
172     if (status != NO_ERROR) return status;
173 
174     switch (version) {
175     case 0:
176       return readFromParcel0(data);
177     default:
178       ALOGE("%s: unsupported parcel version: %d", __func__, version);
179       return INVALID_OPERATION;
180     }
181 }
182 
readFromParcel0(const Parcel & data)183 status_t mediametrics::Item::readFromParcel0(const Parcel& data) {
184     const char *s = data.readCString();
185     mKey = s == nullptr ? "" : s;
186     int32_t pid, uid;
187     status_t status = data.readInt32(&pid) ?: data.readInt32(&uid);
188     if (status != NO_ERROR) return status;
189     mPid = (pid_t)pid;
190     mUid = (uid_t)uid;
191     s = data.readCString();
192     mPkgName = s == nullptr ? "" : s;
193     int32_t count;
194     int64_t version, timestamp;
195     status = data.readInt64(&version) ?: data.readInt64(&timestamp) ?: data.readInt32(&count);
196     if (status != NO_ERROR) return status;
197     if (count < 0) return BAD_VALUE;
198     mPkgVersionCode = version;
199     mTimestamp = timestamp;
200     for (int i = 0; i < count; i++) {
201         Prop prop;
202         status_t status = prop.readFromParcel(data);
203         if (status != NO_ERROR) return status;
204         mProps[prop.getName()] = std::move(prop);
205     }
206     return NO_ERROR;
207 }
208 
writeToParcel(Parcel * data) const209 status_t mediametrics::Item::writeToParcel(Parcel *data) const {
210     if (data == nullptr) return BAD_VALUE;
211 
212     const int32_t version = 0;
213     status_t status = data->writeInt32(version);
214     if (status != NO_ERROR) return status;
215 
216     switch (version) {
217     case 0:
218       return writeToParcel0(data);
219     default:
220       ALOGE("%s: unsupported parcel version: %d", __func__, version);
221       return INVALID_OPERATION;
222     }
223 }
224 
writeToParcel0(Parcel * data) const225 status_t mediametrics::Item::writeToParcel0(Parcel *data) const {
226     status_t status =
227         data->writeCString(mKey.c_str())
228         ?: data->writeInt32(mPid)
229         ?: data->writeInt32(mUid)
230         ?: data->writeCString(mPkgName.c_str())
231         ?: data->writeInt64(mPkgVersionCode)
232         ?: data->writeInt64(mTimestamp);
233     if (status != NO_ERROR) return status;
234 
235     data->writeInt32((int32_t)mProps.size());
236     for (auto &prop : *this) {
237         status = prop.writeToParcel(data);
238         if (status != NO_ERROR) return status;
239     }
240     return NO_ERROR;
241 }
242 
toCString()243 const char *mediametrics::Item::toCString() {
244     std::string val = toString();
245     return strdup(val.c_str());
246 }
247 
248 /*
249  * Similar to audio_utils/clock.h but customized for displaying mediametrics time.
250  */
251 
nsToString(int64_t ns,char * buffer,size_t bufferSize,PrintFormat format)252 void nsToString(int64_t ns, char *buffer, size_t bufferSize, PrintFormat format)
253 {
254     if (bufferSize == 0) return;
255 
256     const int one_second = 1000000000;
257     const time_t sec = ns / one_second;
258     struct tm tm;
259 
260     // Supported on bionic, glibc, and macOS, but not mingw.
261     if (localtime_r(&sec, &tm) == NULL) {
262         buffer[0] = '\0';
263         return;
264     }
265 
266     switch (format) {
267     default:
268     case kPrintFormatLong:
269         if (snprintf(buffer, bufferSize, "%02d-%02d %02d:%02d:%02d.%03d",
270             tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
271             tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
272             (int)(ns % one_second / 1000000)) < 0) {
273             buffer[0] = '\0'; // null terminate on format error, which should not happen
274         }
275         break;
276     case kPrintFormatShort:
277         if (snprintf(buffer, bufferSize, "%02d:%02d:%02d.%03d",
278             tm.tm_hour, tm.tm_min, tm.tm_sec,
279             (int)(ns % one_second / 1000000)) < 0) {
280             buffer[0] = '\0'; // null terminate on format error, which should not happen
281         }
282         break;
283     }
284 }
285 
toString() const286 std::string mediametrics::Item::toString() const {
287     std::string result;
288     char buffer[kMaxPropertyStringSize];
289 
290     snprintf(buffer, sizeof(buffer), "{%s, (%s), (%s, %d, %d)",
291             mKey.c_str(),
292             timeStringFromNs(mTimestamp, kPrintFormatLong).time,
293             mPkgName.c_str(), mPid, mUid
294            );
295     result.append(buffer);
296     bool first = true;
297     for (auto &prop : *this) {
298         prop.toStringBuffer(buffer, sizeof(buffer));
299         result += first ? ", (" : ", ";
300         result += buffer;
301         first = false;
302     }
303     result.append(")}");
304     return result;
305 }
306 
307 // for the lazy, we offer methods that finds the service and
308 // calls the appropriate daemon
selfrecord()309 bool mediametrics::Item::selfrecord() {
310     ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
311 
312     char *str;
313     size_t size;
314     status_t status = writeToByteString(&str, &size);
315     if (status == NO_ERROR) {
316         status = submitBuffer(str, size);
317         free(str);
318     }
319     if (status != NO_ERROR) {
320         ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
321         return false;
322     }
323     return true;
324 }
325 
326 //static
isEnabled()327 bool BaseItem::isEnabled() {
328     // completely skip logging from certain UIDs. We do this here
329     // to avoid the multi-second timeouts while we learn that
330     // sepolicy will not let us find the service.
331     // We do this only for a select set of UIDs
332     // The sepolicy protection is still in place, we just want a faster
333     // response from this specific, small set of uids.
334 
335     // This is checked only once in the lifetime of the process.
336     const uid_t uid = getuid();
337     switch (uid) {
338     case AID_RADIO:     // telephony subsystem, RIL
339         return false;
340     default:
341         // Some isolated processes can access the audio system; see
342         // AudioSystem::setAudioFlingerBinder (currently only the HotwordDetectionService). Instead
343         // of also allowing access to the MediaMetrics service, it's simpler to just disable it for
344         // now.
345         // TODO(b/190151205): Either allow the HotwordDetectionService to access MediaMetrics or
346         // make this disabling specific to that process.
347         uid_t appid = multiuser_get_app_id(uid);
348         if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
349             return false;
350         }
351         break;
352     }
353 
354     int enabled = property_get_int32(Item::EnabledProperty, -1);
355     if (enabled == -1) {
356         enabled = property_get_int32(Item::EnabledPropertyPersist, -1);
357     }
358     if (enabled == -1) {
359         enabled = Item::EnabledProperty_default;
360     }
361     return enabled > 0;
362 }
363 
364 // monitor health of our connection to the metrics service
365 class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
binderDied(const wp<IBinder> &)366         virtual void binderDied(const wp<IBinder> &) {
367             ALOGW("Reacquire service connection on next request");
368             BaseItem::dropInstance();
369         }
370 };
371 
372 static sp<MediaMetricsDeathNotifier> sNotifier;
373 // static
374 sp<media::IMediaMetricsService> BaseItem::sMediaMetricsService;
375 static std::mutex sServiceMutex;
376 static int sRemainingBindAttempts = SVC_TRIES;
377 
378 // static
dropInstance()379 void BaseItem::dropInstance() {
380     std::lock_guard  _l(sServiceMutex);
381     sRemainingBindAttempts = SVC_TRIES;
382     sMediaMetricsService = nullptr;
383 }
384 
385 // static
submitBuffer(const char * buffer,size_t size)386 status_t BaseItem::submitBuffer(const char *buffer, size_t size) {
387     ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
388 
389     // Validate size
390     if (size > std::numeric_limits<int32_t>::max()) return BAD_VALUE;
391 
392     // Do we have the service available?
393     sp<media::IMediaMetricsService> svc = getService();
394     if (svc == nullptr)  return NO_INIT;
395 
396     ::android::status_t status = NO_ERROR;
397     if constexpr (/* DISABLES CODE */ (false)) {
398         // THIS PATH IS FOR REFERENCE ONLY.
399         // It is compiled so that any changes to IMediaMetricsService::submitBuffer()
400         // will lead here.  If this code is changed, the else branch must
401         // be changed as well.
402         //
403         // Use the AIDL calling interface - this is a bit slower as a byte vector must be
404         // constructed. As the call is one-way, the only a transaction error occurs.
405         status = svc->submitBuffer({buffer, buffer + size}).transactionError();
406     } else {
407         // Use the Binder calling interface - this direct implementation avoids
408         // malloc/copy/free for the vector and reduces the overhead for logging.
409         // We based this off of the AIDL generated file:
410         // out/soong/.intermediates/frameworks/av/media/libmediametrics/mediametricsservice-aidl-unstable-cpp-source/gen/android/media/IMediaMetricsService.cpp
411         // TODO: Create an AIDL C++ back end optimized form of vector writing.
412         ::android::Parcel _aidl_data;
413         ::android::Parcel _aidl_reply; // we don't care about this as it is one-way.
414 
415         status = _aidl_data.writeInterfaceToken(svc->getInterfaceDescriptor());
416         if (status != ::android::OK) goto _aidl_error;
417 
418         status = _aidl_data.writeInt32(static_cast<int32_t>(size));
419         if (status != ::android::OK) goto _aidl_error;
420 
421         status = _aidl_data.write(buffer, static_cast<int32_t>(size));
422         if (status != ::android::OK) goto _aidl_error;
423 
424         status = ::android::IInterface::asBinder(svc)->transact(
425                 ::android::media::BnMediaMetricsService::TRANSACTION_submitBuffer,
426                 _aidl_data, &_aidl_reply, ::android::IBinder::FLAG_ONEWAY);
427 
428         // AIDL permits setting a default implementation for additional functionality.
429         // See go/aog/713984. This is not used here.
430         // if (status == ::android::UNKNOWN_TRANSACTION
431         //         && ::android::media::IMediaMetricsService::getDefaultImpl()) {
432         //     status = ::android::media::IMediaMetricsService::getDefaultImpl()
433         //             ->submitBuffer(immutableByteVectorFromBuffer(buffer, size))
434         //             .transactionError();
435         // }
436     }
437 
438     if (status == NO_ERROR) return NO_ERROR;
439 
440     _aidl_error:
441     ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
442     return status;
443 }
444 
445 //static
getService()446 sp<media::IMediaMetricsService> BaseItem::getService() {
447     static const char *servicename = "media.metrics";
448     static const bool enabled = isEnabled(); // singleton initialized
449 
450     if (enabled == false) {
451         ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
452         return nullptr;
453     }
454     std::lock_guard _l(sServiceMutex);
455     // think of remainingBindAttempts as telling us whether service == nullptr because
456     // (1) we haven't tried to initialize it yet
457     // (2) we've tried to initialize it, but failed.
458     if (sMediaMetricsService == nullptr && sRemainingBindAttempts > 0) {
459         const char *badness = "";
460         sp<IServiceManager> sm = defaultServiceManager();
461         if (sm != nullptr) {
462             sp<IBinder> binder = sm->getService(String16(servicename));
463             if (binder != nullptr) {
464                 sMediaMetricsService = interface_cast<media::IMediaMetricsService>(binder);
465                 sNotifier = new MediaMetricsDeathNotifier();
466                 binder->linkToDeath(sNotifier);
467             } else {
468                 badness = "did not find service";
469             }
470         } else {
471             badness = "No Service Manager access";
472         }
473         if (sMediaMetricsService == nullptr) {
474             if (sRemainingBindAttempts > 0) {
475                 sRemainingBindAttempts--;
476             }
477             ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
478                     __func__, servicename, badness);
479         }
480     }
481     return sMediaMetricsService;
482 }
483 
484 
writeToByteString(char ** pbuffer,size_t * plength) const485 status_t mediametrics::Item::writeToByteString(char **pbuffer, size_t *plength) const
486 {
487     if (pbuffer == nullptr || plength == nullptr)
488         return BAD_VALUE;
489 
490     // get size
491     const size_t keySizeZeroTerminated = strlen(mKey.c_str()) + 1;
492     if (keySizeZeroTerminated > UINT16_MAX) {
493         ALOGW("%s: key size %zu too large", __func__, keySizeZeroTerminated);
494         return INVALID_OPERATION;
495     }
496     const uint16_t version = 0;
497     const uint32_t header_size =
498         sizeof(uint32_t)      // total size
499         + sizeof(header_size) // header size
500         + sizeof(version)     // encoding version
501         + sizeof(uint16_t)    // key size
502         + keySizeZeroTerminated // key, zero terminated
503         + sizeof(int32_t)     // pid
504         + sizeof(int32_t)     // uid
505         + sizeof(int64_t)     // timestamp
506         ;
507 
508     uint32_t size = header_size
509         + sizeof(uint32_t) // # properties
510         ;
511     for (auto &prop : *this) {
512         const size_t propSize = prop.getByteStringSize();
513         if (propSize > UINT16_MAX) {
514             ALOGW("%s: prop %s size %zu too large", __func__, prop.getName(), propSize);
515             return INVALID_OPERATION;
516         }
517         if (__builtin_add_overflow(size, propSize, &size)) {
518             ALOGW("%s: item size overflow at property %s", __func__, prop.getName());
519             return INVALID_OPERATION;
520         }
521     }
522 
523     // since we fill every byte in the buffer (there is no padding),
524     // malloc is used here instead of calloc.
525     char * const build = (char *)malloc(size);
526     if (build == nullptr) return NO_MEMORY;
527 
528     char *filling = build;
529     char *buildmax = build + size;
530     if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
531             || insert(header_size, &filling, buildmax) != NO_ERROR
532             || insert(version, &filling, buildmax) != NO_ERROR
533             || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
534             || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
535             || insert((int32_t)mPid, &filling, buildmax) != NO_ERROR
536             || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
537             || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
538             || insert((uint32_t)mProps.size(), &filling, buildmax) != NO_ERROR) {
539         ALOGE("%s:could not write header", __func__);  // shouldn't happen
540         free(build);
541         return INVALID_OPERATION;
542     }
543     for (auto &prop : *this) {
544         if (prop.writeToByteString(&filling, buildmax) != NO_ERROR) {
545             free(build);
546             // shouldn't happen
547             ALOGE("%s:could not write prop %s", __func__, prop.getName());
548             return INVALID_OPERATION;
549         }
550     }
551 
552     if (filling != buildmax) {
553         ALOGE("%s: problems populating; wrote=%d planned=%d",
554                 __func__, (int)(filling - build), (int)size);
555         free(build);
556         return INVALID_OPERATION;
557     }
558     *pbuffer = build;
559     *plength = size;
560     return NO_ERROR;
561 }
562 
readFromByteString(const char * bufferptr,size_t length)563 status_t mediametrics::Item::readFromByteString(const char *bufferptr, size_t length)
564 {
565     if (bufferptr == nullptr) return BAD_VALUE;
566 
567     const char *read = bufferptr;
568     const char *readend = bufferptr + length;
569 
570     uint32_t size;
571     uint32_t header_size;
572     uint16_t version;
573     uint16_t key_size;
574     std::string key;
575     int32_t pid;
576     int32_t uid;
577     int64_t timestamp;
578     uint32_t propCount;
579     if (extract(&size, &read, readend) != NO_ERROR
580             || extract(&header_size, &read, readend) != NO_ERROR
581             || extract(&version, &read, readend) != NO_ERROR
582             || extract(&key_size, &read, readend) != NO_ERROR
583             || extract(&key, &read, readend) != NO_ERROR
584             || extract(&pid, &read, readend) != NO_ERROR
585             || extract(&uid, &read, readend) != NO_ERROR
586             || extract(&timestamp, &read, readend) != NO_ERROR
587             || size > length
588             || key.size() + 1 != key_size
589             || header_size > size) {
590         ALOGW("%s: invalid header", __func__);
591         return INVALID_OPERATION;
592     }
593     mKey = std::move(key);
594     const size_t pos = read - bufferptr;
595     if (pos > header_size) {
596         ALOGW("%s: invalid header pos:%zu > header_size:%u",
597                 __func__, pos, header_size);
598         return INVALID_OPERATION;
599     } else if (pos < header_size) {
600         ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
601                 __func__, pos, header_size);
602         read += (header_size - pos);
603     }
604     if (extract(&propCount, &read, readend) != NO_ERROR) {
605         ALOGD("%s: cannot read prop count", __func__);
606         return INVALID_OPERATION;
607     }
608     mPid = pid;
609     mUid = uid;
610     mTimestamp = timestamp;
611     for (size_t i = 0; i < propCount; ++i) {
612         Prop prop;
613         if (prop.readFromByteString(&read, readend) != NO_ERROR) {
614             ALOGW("%s: cannot read prop %zu", __func__, i);
615             return INVALID_OPERATION;
616         }
617         mProps[prop.getName()] = std::move(prop);
618     }
619     return NO_ERROR;
620 }
621 
readFromParcel(const Parcel & data)622 status_t mediametrics::Item::Prop::readFromParcel(const Parcel& data)
623 {
624     const char *key = data.readCString();
625     if (key == nullptr) return BAD_VALUE;
626     int32_t type;
627     status_t status = data.readInt32(&type);
628     if (status != NO_ERROR) return status;
629     switch (type) {
630     case mediametrics::kTypeInt32: {
631         int32_t value;
632         status = data.readInt32(&value);
633         if (status != NO_ERROR) return status;
634         mElem = value;
635     } break;
636     case mediametrics::kTypeInt64: {
637         int64_t value;
638         status = data.readInt64(&value);
639         if (status != NO_ERROR) return status;
640         mElem = value;
641     } break;
642     case mediametrics::kTypeDouble: {
643         double value;
644         status = data.readDouble(&value);
645         if (status != NO_ERROR) return status;
646         mElem = value;
647     } break;
648     case mediametrics::kTypeCString: {
649         const char *s = data.readCString();
650         if (s == nullptr) return BAD_VALUE;
651         mElem = s;
652     } break;
653     case mediametrics::kTypeRate: {
654         std::pair<int64_t, int64_t> rate;
655         status = data.readInt64(&rate.first)
656                 ?: data.readInt64(&rate.second);
657         if (status != NO_ERROR) return status;
658         mElem = rate;
659     } break;
660     case mediametrics::kTypeNone: {
661         mElem = std::monostate{};
662     } break;
663     default:
664         ALOGE("%s: reading bad item type: %d", __func__, type);
665         return BAD_VALUE;
666     }
667     setName(key);
668     return NO_ERROR;
669 }
670 
readFromByteString(const char ** bufferpptr,const char * bufferptrmax)671 status_t mediametrics::Item::Prop::readFromByteString(
672         const char **bufferpptr, const char *bufferptrmax)
673 {
674     uint16_t len;
675     std::string name;
676     uint8_t type;
677     status_t status = extract(&len, bufferpptr, bufferptrmax)
678             ?: extract(&type, bufferpptr, bufferptrmax)
679             ?: extract(&name, bufferpptr, bufferptrmax);
680     if (status != NO_ERROR) return status;
681     switch (type) {
682     case mediametrics::kTypeInt32: {
683         int32_t value;
684         status = extract(&value, bufferpptr, bufferptrmax);
685         if (status != NO_ERROR) return status;
686         mElem = value;
687     } break;
688     case mediametrics::kTypeInt64: {
689         int64_t value;
690         status = extract(&value, bufferpptr, bufferptrmax);
691         if (status != NO_ERROR) return status;
692         mElem = value;
693     } break;
694     case mediametrics::kTypeDouble: {
695         double value;
696         status = extract(&value, bufferpptr, bufferptrmax);
697         if (status != NO_ERROR) return status;
698         mElem = value;
699     } break;
700     case mediametrics::kTypeRate: {
701         std::pair<int64_t, int64_t> value;
702         status = extract(&value.first, bufferpptr, bufferptrmax)
703                 ?: extract(&value.second, bufferpptr, bufferptrmax);
704         if (status != NO_ERROR) return status;
705         mElem = value;
706     } break;
707     case mediametrics::kTypeCString: {
708         std::string value;
709         status = extract(&value, bufferpptr, bufferptrmax);
710         if (status != NO_ERROR) return status;
711         mElem = std::move(value);
712     } break;
713     case mediametrics::kTypeNone: {
714         mElem = std::monostate{};
715     } break;
716     default:
717         ALOGE("%s: found bad prop type: %d, name %s",
718                 __func__, (int)type, mName.c_str());  // no payload sent
719         return BAD_VALUE;
720     }
721     mName = name;
722     return NO_ERROR;
723 }
724 
725 } // namespace android::mediametrics
726