1/*
2 * Copyright (C) 2017 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_NDEBUG 0
18#define LOG_TAG "PluginMetricsReporting"
19#include <utils/Log.h>
20
21#include <media/PluginMetricsReporting.h>
22
23#include <media/MediaAnalyticsItem.h>
24
25#include "protos/plugin_metrics.pb.h"
26
27namespace android {
28
29namespace {
30
31using android::drm_metrics::MetricsGroup;
32using android::drm_metrics::MetricsGroup_Metric;
33using android::drm_metrics::MetricsGroup_Metric_MetricValue;
34
35const char* const kParentAttribute = "/parent/external";
36
37status_t reportMetricsGroup(const MetricsGroup& metricsGroup,
38                            const String8& batchName,
39                            const int64_t* parentId) {
40    MediaAnalyticsItem analyticsItem(batchName.c_str());
41    analyticsItem.generateSessionID();
42    int64_t sessionId = analyticsItem.getSessionID();
43    if (parentId != NULL) {
44        analyticsItem.setInt64(kParentAttribute, *parentId);
45    }
46
47    // Report the package name.
48    if (metricsGroup.has_app_package_name()) {
49      AString app_package_name(metricsGroup.app_package_name().c_str(),
50                               metricsGroup.app_package_name().size());
51      analyticsItem.setPkgName(app_package_name);
52    }
53
54    for (int i = 0; i < metricsGroup.metric_size(); ++i) {
55        const MetricsGroup_Metric& metric = metricsGroup.metric(i);
56        if (!metric.has_name()) {
57            ALOGE("Metric with no name.");
58            return BAD_VALUE;
59        }
60
61        if (!metric.has_value()) {
62            ALOGE("Metric with no value.");
63            return BAD_VALUE;
64        }
65
66        const MetricsGroup_Metric_MetricValue& value = metric.value();
67        if (value.has_int_value()) {
68            analyticsItem.setInt64(metric.name().c_str(),
69                                   value.int_value());
70        } else if (value.has_double_value()) {
71            analyticsItem.setDouble(metric.name().c_str(),
72                                    value.double_value());
73        } else if (value.has_string_value()) {
74            analyticsItem.setCString(metric.name().c_str(),
75                                     value.string_value().c_str());
76        } else {
77            ALOGE("Metric Value with no actual value.");
78            return BAD_VALUE;
79        }
80    }
81
82    analyticsItem.setFinalized(true);
83    if (!analyticsItem.selfrecord()) {
84      // Note the cast to int is because we build on 32 and 64 bit.
85      // The cast prevents a peculiar printf problem where one format cannot
86      // satisfy both.
87      ALOGE("selfrecord() returned false. sessioId %d", (int) sessionId);
88    }
89
90    for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
91        const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
92        status_t res = reportMetricsGroup(subGroup, batchName, &sessionId);
93        if (res != OK) {
94            return res;
95        }
96    }
97
98    return OK;
99}
100
101String8 sanitize(const String8& input) {
102    // Filters the input string down to just alphanumeric characters.
103    String8 output;
104    for (size_t i = 0; i < input.size(); ++i) {
105        char candidate = input[i];
106        if ((candidate >= 'a' && candidate <= 'z') ||
107                (candidate >= 'A' && candidate <= 'Z') ||
108                (candidate >= '0' && candidate <= '9')) {
109            output.append(&candidate, 1);
110        }
111    }
112    return output;
113}
114
115}  // namespace
116
117status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
118                                const String8& vendor,
119                                const String8& description) {
120    MetricsGroup root_metrics_group;
121    if (!root_metrics_group.ParseFromArray(serializedMetrics.array(),
122                                           serializedMetrics.size())) {
123        ALOGE("Failure to parse.");
124        return BAD_VALUE;
125    }
126
127    String8 name = String8::format("drm.vendor.%s.%s",
128                                   sanitize(vendor).c_str(),
129                                   sanitize(description).c_str());
130
131    return reportMetricsGroup(root_metrics_group, name, NULL);
132}
133
134}  // namespace android
135