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 <binder/IServiceManager.h>
35 #include <media/IMediaMetricsService.h>
36 #include <media/MediaMetricsItem.h>
37 #include <private/android_filesystem_config.h>
38 
39 // Max per-property string size before truncation in toString().
40 // Do not make too large, as this is used for dumpsys purposes.
41 static constexpr size_t kMaxPropertyStringSize = 4096;
42 
43 namespace android::mediametrics {
44 
45 #define DEBUG_SERVICEACCESS     0
46 #define DEBUG_API               0
47 #define DEBUG_ALLOCATIONS       0
48 
49 // after this many failed attempts, we stop trying [from this process] and just say that
50 // the service is off.
51 #define SVC_TRIES               2
52 
convert(mediametrics_handle_t handle)53 mediametrics::Item* mediametrics::Item::convert(mediametrics_handle_t handle) {
54     mediametrics::Item *item = (android::mediametrics::Item *) handle;
55     return item;
56 }
57 
convert(mediametrics::Item * item)58 mediametrics_handle_t mediametrics::Item::convert(mediametrics::Item *item ) {
59     mediametrics_handle_t handle = (mediametrics_handle_t) item;
60     return handle;
61 }
62 
~Item()63 mediametrics::Item::~Item() {
64     if (DEBUG_ALLOCATIONS) {
65         ALOGD("Destroy  mediametrics::Item @ %p", this);
66     }
67 }
68 
setTimestamp(nsecs_t ts)69 mediametrics::Item &mediametrics::Item::setTimestamp(nsecs_t ts) {
70     mTimestamp = ts;
71     return *this;
72 }
73 
getTimestamp() const74 nsecs_t mediametrics::Item::getTimestamp() const {
75     return mTimestamp;
76 }
77 
setPid(pid_t pid)78 mediametrics::Item &mediametrics::Item::setPid(pid_t pid) {
79     mPid = pid;
80     return *this;
81 }
82 
getPid() const83 pid_t mediametrics::Item::getPid() const {
84     return mPid;
85 }
86 
setUid(uid_t uid)87 mediametrics::Item &mediametrics::Item::setUid(uid_t uid) {
88     mUid = uid;
89     return *this;
90 }
91 
getUid() const92 uid_t mediametrics::Item::getUid() const {
93     return mUid;
94 }
95 
setPkgName(const std::string & pkgName)96 mediametrics::Item &mediametrics::Item::setPkgName(const std::string &pkgName) {
97     mPkgName = pkgName;
98     return *this;
99 }
100 
setPkgVersionCode(int64_t pkgVersionCode)101 mediametrics::Item &mediametrics::Item::setPkgVersionCode(int64_t pkgVersionCode) {
102     mPkgVersionCode = pkgVersionCode;
103     return *this;
104 }
105 
getPkgVersionCode() const106 int64_t mediametrics::Item::getPkgVersionCode() const {
107     return mPkgVersionCode;
108 }
109 
110 // remove indicated keys and their values
111 // return value is # keys removed
filter(size_t n,const char * attrs[])112 size_t mediametrics::Item::filter(size_t n, const char *attrs[]) {
113     size_t zapped = 0;
114     for (size_t i = 0; i < n; ++i) {
115         zapped += mProps.erase(attrs[i]);
116     }
117     return zapped;
118 }
119 
120 // remove any keys NOT in the provided list
121 // return value is # keys removed
filterNot(size_t n,const char * attrs[])122 size_t mediametrics::Item::filterNot(size_t n, const char *attrs[]) {
123     std::set<std::string> check(attrs, attrs + n);
124     size_t zapped = 0;
125     for (auto it = mProps.begin(); it != mProps.end();) {
126         if (check.find(it->first) != check.end()) {
127             ++it;
128         } else {
129            it = mProps.erase(it);
130            ++zapped;
131         }
132     }
133     return zapped;
134 }
135 
136 // Parcel / serialize things for binder calls
137 //
138 
readFromParcel(const Parcel & data)139 status_t mediametrics::Item::readFromParcel(const Parcel& data) {
140     int32_t version;
141     status_t status = data.readInt32(&version);
142     if (status != NO_ERROR) return status;
143 
144     switch (version) {
145     case 0:
146       return readFromParcel0(data);
147     default:
148       ALOGE("%s: unsupported parcel version: %d", __func__, version);
149       return INVALID_OPERATION;
150     }
151 }
152 
readFromParcel0(const Parcel & data)153 status_t mediametrics::Item::readFromParcel0(const Parcel& data) {
154     const char *s = data.readCString();
155     mKey = s == nullptr ? "" : s;
156     int32_t pid, uid;
157     status_t status = data.readInt32(&pid) ?: data.readInt32(&uid);
158     if (status != NO_ERROR) return status;
159     mPid = (pid_t)pid;
160     mUid = (uid_t)uid;
161     s = data.readCString();
162     mPkgName = s == nullptr ? "" : s;
163     int32_t count;
164     int64_t version, timestamp;
165     status = data.readInt64(&version) ?: data.readInt64(&timestamp) ?: data.readInt32(&count);
166     if (status != NO_ERROR) return status;
167     if (count < 0) return BAD_VALUE;
168     mPkgVersionCode = version;
169     mTimestamp = timestamp;
170     for (int i = 0; i < count; i++) {
171         Prop prop;
172         status_t status = prop.readFromParcel(data);
173         if (status != NO_ERROR) return status;
174         mProps[prop.getName()] = std::move(prop);
175     }
176     return NO_ERROR;
177 }
178 
writeToParcel(Parcel * data) const179 status_t mediametrics::Item::writeToParcel(Parcel *data) const {
180     if (data == nullptr) return BAD_VALUE;
181 
182     const int32_t version = 0;
183     status_t status = data->writeInt32(version);
184     if (status != NO_ERROR) return status;
185 
186     switch (version) {
187     case 0:
188       return writeToParcel0(data);
189     default:
190       ALOGE("%s: unsupported parcel version: %d", __func__, version);
191       return INVALID_OPERATION;
192     }
193 }
194 
writeToParcel0(Parcel * data) const195 status_t mediametrics::Item::writeToParcel0(Parcel *data) const {
196     status_t status =
197         data->writeCString(mKey.c_str())
198         ?: data->writeInt32(mPid)
199         ?: data->writeInt32(mUid)
200         ?: data->writeCString(mPkgName.c_str())
201         ?: data->writeInt64(mPkgVersionCode)
202         ?: data->writeInt64(mTimestamp);
203     if (status != NO_ERROR) return status;
204 
205     data->writeInt32((int32_t)mProps.size());
206     for (auto &prop : *this) {
207         status = prop.writeToParcel(data);
208         if (status != NO_ERROR) return status;
209     }
210     return NO_ERROR;
211 }
212 
toCString()213 const char *mediametrics::Item::toCString() {
214     std::string val = toString();
215     return strdup(val.c_str());
216 }
217 
218 /*
219  * Similar to audio_utils/clock.h but customized for displaying mediametrics time.
220  */
221 
nsToString(int64_t ns,char * buffer,size_t bufferSize,PrintFormat format)222 void nsToString(int64_t ns, char *buffer, size_t bufferSize, PrintFormat format)
223 {
224     if (bufferSize == 0) return;
225 
226     const int one_second = 1000000000;
227     const time_t sec = ns / one_second;
228     struct tm tm;
229 
230     // Supported on bionic, glibc, and macOS, but not mingw.
231     if (localtime_r(&sec, &tm) == NULL) {
232         buffer[0] = '\0';
233         return;
234     }
235 
236     switch (format) {
237     default:
238     case kPrintFormatLong:
239         if (snprintf(buffer, bufferSize, "%02d-%02d %02d:%02d:%02d.%03d",
240             tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
241             tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
242             (int)(ns % one_second / 1000000)) < 0) {
243             buffer[0] = '\0'; // null terminate on format error, which should not happen
244         }
245         break;
246     case kPrintFormatShort:
247         if (snprintf(buffer, bufferSize, "%02d:%02d:%02d.%03d",
248             tm.tm_hour, tm.tm_min, tm.tm_sec,
249             (int)(ns % one_second / 1000000)) < 0) {
250             buffer[0] = '\0'; // null terminate on format error, which should not happen
251         }
252         break;
253     }
254 }
255 
toString() const256 std::string mediametrics::Item::toString() const {
257     std::string result;
258     char buffer[kMaxPropertyStringSize];
259 
260     snprintf(buffer, sizeof(buffer), "{%s, (%s), (%s, %d, %d)",
261             mKey.c_str(),
262             timeStringFromNs(mTimestamp, kPrintFormatLong).time,
263             mPkgName.c_str(), mPid, mUid
264            );
265     result.append(buffer);
266     bool first = true;
267     for (auto &prop : *this) {
268         prop.toStringBuffer(buffer, sizeof(buffer));
269         result += first ? ", (" : ", ";
270         result += buffer;
271         first = false;
272     }
273     result.append(")}");
274     return result;
275 }
276 
277 // for the lazy, we offer methods that finds the service and
278 // calls the appropriate daemon
selfrecord()279 bool mediametrics::Item::selfrecord() {
280     ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
281     sp<IMediaMetricsService> svc = getService();
282     if (svc != NULL) {
283         status_t status = svc->submit(this);
284         if (status != NO_ERROR) {
285             ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
286             return false;
287         }
288         return true;
289     } else {
290         return false;
291     }
292 }
293 
294 //static
isEnabled()295 bool BaseItem::isEnabled() {
296     // completely skip logging from certain UIDs. We do this here
297     // to avoid the multi-second timeouts while we learn that
298     // sepolicy will not let us find the service.
299     // We do this only for a select set of UIDs
300     // The sepolicy protection is still in place, we just want a faster
301     // response from this specific, small set of uids.
302 
303     // This is checked only once in the lifetime of the process.
304     const uid_t uid = getuid();
305     switch (uid) {
306     case AID_RADIO:     // telephony subsystem, RIL
307         return false;
308     }
309 
310     int enabled = property_get_int32(Item::EnabledProperty, -1);
311     if (enabled == -1) {
312         enabled = property_get_int32(Item::EnabledPropertyPersist, -1);
313     }
314     if (enabled == -1) {
315         enabled = Item::EnabledProperty_default;
316     }
317     return enabled > 0;
318 }
319 
320 // monitor health of our connection to the metrics service
321 class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
binderDied(const wp<IBinder> &)322         virtual void binderDied(const wp<IBinder> &) {
323             ALOGW("Reacquire service connection on next request");
324             BaseItem::dropInstance();
325         }
326 };
327 
328 static sp<MediaMetricsDeathNotifier> sNotifier;
329 // static
330 sp<IMediaMetricsService> BaseItem::sMediaMetricsService;
331 static std::mutex sServiceMutex;
332 static int sRemainingBindAttempts = SVC_TRIES;
333 
334 // static
dropInstance()335 void BaseItem::dropInstance() {
336     std::lock_guard  _l(sServiceMutex);
337     sRemainingBindAttempts = SVC_TRIES;
338     sMediaMetricsService = nullptr;
339 }
340 
341 // static
submitBuffer(const char * buffer,size_t size)342 bool BaseItem::submitBuffer(const char *buffer, size_t size) {
343 /*
344     mediametrics::Item item;
345     status_t status = item.readFromByteString(buffer, size);
346     ALOGD("%s: status:%d, size:%zu, item:%s", __func__, status, size, item.toString().c_str());
347     return item.selfrecord();
348     */
349 
350     ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
351     sp<IMediaMetricsService> svc = getService();
352     if (svc != nullptr) {
353         const status_t status = svc->submitBuffer(buffer, size);
354         if (status != NO_ERROR) {
355             ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
356             return false;
357         }
358         return true;
359     }
360     return false;
361 }
362 
363 //static
getService()364 sp<IMediaMetricsService> BaseItem::getService() {
365     static const char *servicename = "media.metrics";
366     static const bool enabled = isEnabled(); // singleton initialized
367 
368     if (enabled == false) {
369         ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
370         return nullptr;
371     }
372     std::lock_guard _l(sServiceMutex);
373     // think of remainingBindAttempts as telling us whether service == nullptr because
374     // (1) we haven't tried to initialize it yet
375     // (2) we've tried to initialize it, but failed.
376     if (sMediaMetricsService == nullptr && sRemainingBindAttempts > 0) {
377         const char *badness = "";
378         sp<IServiceManager> sm = defaultServiceManager();
379         if (sm != nullptr) {
380             sp<IBinder> binder = sm->getService(String16(servicename));
381             if (binder != nullptr) {
382                 sMediaMetricsService = interface_cast<IMediaMetricsService>(binder);
383                 sNotifier = new MediaMetricsDeathNotifier();
384                 binder->linkToDeath(sNotifier);
385             } else {
386                 badness = "did not find service";
387             }
388         } else {
389             badness = "No Service Manager access";
390         }
391         if (sMediaMetricsService == nullptr) {
392             if (sRemainingBindAttempts > 0) {
393                 sRemainingBindAttempts--;
394             }
395             ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
396                     __func__, servicename, badness);
397         }
398     }
399     return sMediaMetricsService;
400 }
401 
402 
writeToByteString(char ** pbuffer,size_t * plength) const403 status_t mediametrics::Item::writeToByteString(char **pbuffer, size_t *plength) const
404 {
405     if (pbuffer == nullptr || plength == nullptr)
406         return BAD_VALUE;
407 
408     // get size
409     const size_t keySizeZeroTerminated = strlen(mKey.c_str()) + 1;
410     if (keySizeZeroTerminated > UINT16_MAX) {
411         ALOGW("%s: key size %zu too large", __func__, keySizeZeroTerminated);
412         return INVALID_OPERATION;
413     }
414     const uint16_t version = 0;
415     const uint32_t header_size =
416         sizeof(uint32_t)      // total size
417         + sizeof(header_size) // header size
418         + sizeof(version)     // encoding version
419         + sizeof(uint16_t)    // key size
420         + keySizeZeroTerminated // key, zero terminated
421         + sizeof(int32_t)     // pid
422         + sizeof(int32_t)     // uid
423         + sizeof(int64_t)     // timestamp
424         ;
425 
426     uint32_t size = header_size
427         + sizeof(uint32_t) // # properties
428         ;
429     for (auto &prop : *this) {
430         const size_t propSize = prop.getByteStringSize();
431         if (propSize > UINT16_MAX) {
432             ALOGW("%s: prop %s size %zu too large", __func__, prop.getName(), propSize);
433             return INVALID_OPERATION;
434         }
435         if (__builtin_add_overflow(size, propSize, &size)) {
436             ALOGW("%s: item size overflow at property %s", __func__, prop.getName());
437             return INVALID_OPERATION;
438         }
439     }
440 
441     // since we fill every byte in the buffer (there is no padding),
442     // malloc is used here instead of calloc.
443     char * const build = (char *)malloc(size);
444     if (build == nullptr) return NO_MEMORY;
445 
446     char *filling = build;
447     char *buildmax = build + size;
448     if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
449             || insert(header_size, &filling, buildmax) != NO_ERROR
450             || insert(version, &filling, buildmax) != NO_ERROR
451             || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
452             || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
453             || insert((int32_t)mPid, &filling, buildmax) != NO_ERROR
454             || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
455             || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
456             || insert((uint32_t)mProps.size(), &filling, buildmax) != NO_ERROR) {
457         ALOGE("%s:could not write header", __func__);  // shouldn't happen
458         free(build);
459         return INVALID_OPERATION;
460     }
461     for (auto &prop : *this) {
462         if (prop.writeToByteString(&filling, buildmax) != NO_ERROR) {
463             free(build);
464             // shouldn't happen
465             ALOGE("%s:could not write prop %s", __func__, prop.getName());
466             return INVALID_OPERATION;
467         }
468     }
469 
470     if (filling != buildmax) {
471         ALOGE("%s: problems populating; wrote=%d planned=%d",
472                 __func__, (int)(filling - build), (int)size);
473         free(build);
474         return INVALID_OPERATION;
475     }
476     *pbuffer = build;
477     *plength = size;
478     return NO_ERROR;
479 }
480 
readFromByteString(const char * bufferptr,size_t length)481 status_t mediametrics::Item::readFromByteString(const char *bufferptr, size_t length)
482 {
483     if (bufferptr == nullptr) return BAD_VALUE;
484 
485     const char *read = bufferptr;
486     const char *readend = bufferptr + length;
487 
488     uint32_t size;
489     uint32_t header_size;
490     uint16_t version;
491     uint16_t key_size;
492     std::string key;
493     int32_t pid;
494     int32_t uid;
495     int64_t timestamp;
496     uint32_t propCount;
497     if (extract(&size, &read, readend) != NO_ERROR
498             || extract(&header_size, &read, readend) != NO_ERROR
499             || extract(&version, &read, readend) != NO_ERROR
500             || extract(&key_size, &read, readend) != NO_ERROR
501             || extract(&key, &read, readend) != NO_ERROR
502             || extract(&pid, &read, readend) != NO_ERROR
503             || extract(&uid, &read, readend) != NO_ERROR
504             || extract(&timestamp, &read, readend) != NO_ERROR
505             || size > length
506             || key.size() + 1 != key_size
507             || header_size > size) {
508         ALOGW("%s: invalid header", __func__);
509         return INVALID_OPERATION;
510     }
511     mKey = std::move(key);
512     const size_t pos = read - bufferptr;
513     if (pos > header_size) {
514         ALOGW("%s: invalid header pos:%zu > header_size:%u",
515                 __func__, pos, header_size);
516         return INVALID_OPERATION;
517     } else if (pos < header_size) {
518         ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
519                 __func__, pos, header_size);
520         read += (header_size - pos);
521     }
522     if (extract(&propCount, &read, readend) != NO_ERROR) {
523         ALOGD("%s: cannot read prop count", __func__);
524         return INVALID_OPERATION;
525     }
526     mPid = pid;
527     mUid = uid;
528     mTimestamp = timestamp;
529     for (size_t i = 0; i < propCount; ++i) {
530         Prop prop;
531         if (prop.readFromByteString(&read, readend) != NO_ERROR) {
532             ALOGW("%s: cannot read prop %zu", __func__, i);
533             return INVALID_OPERATION;
534         }
535         mProps[prop.getName()] = std::move(prop);
536     }
537     return NO_ERROR;
538 }
539 
readFromParcel(const Parcel & data)540 status_t mediametrics::Item::Prop::readFromParcel(const Parcel& data)
541 {
542     const char *key = data.readCString();
543     if (key == nullptr) return BAD_VALUE;
544     int32_t type;
545     status_t status = data.readInt32(&type);
546     if (status != NO_ERROR) return status;
547     switch (type) {
548     case mediametrics::kTypeInt32: {
549         int32_t value;
550         status = data.readInt32(&value);
551         if (status != NO_ERROR) return status;
552         mElem = value;
553     } break;
554     case mediametrics::kTypeInt64: {
555         int64_t value;
556         status = data.readInt64(&value);
557         if (status != NO_ERROR) return status;
558         mElem = value;
559     } break;
560     case mediametrics::kTypeDouble: {
561         double value;
562         status = data.readDouble(&value);
563         if (status != NO_ERROR) return status;
564         mElem = value;
565     } break;
566     case mediametrics::kTypeCString: {
567         const char *s = data.readCString();
568         if (s == nullptr) return BAD_VALUE;
569         mElem = s;
570     } break;
571     case mediametrics::kTypeRate: {
572         std::pair<int64_t, int64_t> rate;
573         status = data.readInt64(&rate.first)
574                 ?: data.readInt64(&rate.second);
575         if (status != NO_ERROR) return status;
576         mElem = rate;
577     } break;
578     case mediametrics::kTypeNone: {
579         mElem = std::monostate{};
580     } break;
581     default:
582         ALOGE("%s: reading bad item type: %d", __func__, type);
583         return BAD_VALUE;
584     }
585     setName(key);
586     return NO_ERROR;
587 }
588 
readFromByteString(const char ** bufferpptr,const char * bufferptrmax)589 status_t mediametrics::Item::Prop::readFromByteString(
590         const char **bufferpptr, const char *bufferptrmax)
591 {
592     uint16_t len;
593     std::string name;
594     uint8_t type;
595     status_t status = extract(&len, bufferpptr, bufferptrmax)
596             ?: extract(&type, bufferpptr, bufferptrmax)
597             ?: extract(&name, bufferpptr, bufferptrmax);
598     if (status != NO_ERROR) return status;
599     switch (type) {
600     case mediametrics::kTypeInt32: {
601         int32_t value;
602         status = extract(&value, bufferpptr, bufferptrmax);
603         if (status != NO_ERROR) return status;
604         mElem = value;
605     } break;
606     case mediametrics::kTypeInt64: {
607         int64_t value;
608         status = extract(&value, bufferpptr, bufferptrmax);
609         if (status != NO_ERROR) return status;
610         mElem = value;
611     } break;
612     case mediametrics::kTypeDouble: {
613         double value;
614         status = extract(&value, bufferpptr, bufferptrmax);
615         if (status != NO_ERROR) return status;
616         mElem = value;
617     } break;
618     case mediametrics::kTypeRate: {
619         std::pair<int64_t, int64_t> value;
620         status = extract(&value.first, bufferpptr, bufferptrmax)
621                 ?: extract(&value.second, bufferpptr, bufferptrmax);
622         if (status != NO_ERROR) return status;
623         mElem = value;
624     } break;
625     case mediametrics::kTypeCString: {
626         std::string value;
627         status = extract(&value, bufferpptr, bufferptrmax);
628         if (status != NO_ERROR) return status;
629         mElem = std::move(value);
630     } break;
631     case mediametrics::kTypeNone: {
632         mElem = std::monostate{};
633     } break;
634     default:
635         ALOGE("%s: found bad prop type: %d, name %s",
636                 __func__, (int)type, mName.c_str());  // no payload sent
637         return BAD_VALUE;
638     }
639     mName = name;
640     return NO_ERROR;
641 }
642 
643 } // namespace android::mediametrics
644