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