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