1/*
2 * Copyright 2018 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 "DrmMetricsTest"
18#include "mediadrm/DrmMetrics.h"
19
20#include <android/hardware/drm/1.0/types.h>
21#include <android/hardware/drm/1.1/types.h>
22#include <binder/PersistableBundle.h>
23#include <google/protobuf/text_format.h>
24#include <google/protobuf/util/message_differencer.h>
25#include <gtest/gtest.h>
26#include <utils/Log.h>
27
28#include "protos/metrics.pb.h"
29
30using ::android::drm_metrics::DrmFrameworkMetrics;
31using ::android::hardware::hidl_vec;
32using ::android::hardware::drm::V1_0::EventType;
33using ::android::hardware::drm::V1_0::KeyStatusType;
34using ::android::hardware::drm::V1_0::Status;
35using ::android::hardware::drm::V1_1::DrmMetricGroup;
36using ::android::os::PersistableBundle;
37using ::google::protobuf::util::MessageDifferencer;
38using ::google::protobuf::TextFormat;
39
40namespace android {
41
42/**
43 * Unit tests for the MediaDrmMetrics class.
44 */
45class MediaDrmMetricsTest : public ::testing::Test {};
46
47/**
48 * This derived class mocks the clock for testing purposes.
49 */
50class FakeMediaDrmMetrics : public MediaDrmMetrics {
51 public:
52  FakeMediaDrmMetrics() : MediaDrmMetrics(), time_(0) {};
53
54  int64_t GetCurrentTimeMs() { return time_++; }
55  int64_t time_;
56};
57
58TEST_F(MediaDrmMetricsTest, EmptySuccess) {
59  MediaDrmMetrics metrics;
60  PersistableBundle bundle;
61
62  metrics.Export(&bundle);
63  EXPECT_TRUE(bundle.empty());
64}
65
66TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) {
67  MediaDrmMetrics metrics;
68
69  metrics.mOpenSessionCounter.Increment(OK);
70  metrics.mCloseSessionCounter.Increment(OK);
71
72  {
73    EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
74    EventTimer<status_t> provide_key_response_timer(
75        &metrics.mProvideKeyResponseTimeUs);
76    get_key_request_timer.SetAttribute(OK);
77    provide_key_response_timer.SetAttribute(OK);
78  }
79
80  metrics.mGetProvisionRequestCounter.Increment(OK);
81  metrics.mProvideProvisionResponseCounter.Increment(OK);
82  metrics.mGetDeviceUniqueIdCounter.Increment(OK);
83
84  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
85  metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
86
87  PersistableBundle bundle;
88
89  metrics.Export(&bundle);
90  EXPECT_EQ(11U, bundle.size());
91
92  // Verify the list of pairs of int64 metrics.
93  std::vector<std::pair<std::string, int64_t>> expected_values = {
94      { "drm.mediadrm.open_session.ok.count", 1 },
95      { "drm.mediadrm.close_session.ok.count", 1 },
96      { "drm.mediadrm.get_key_request.ok.count", 1 },
97      { "drm.mediadrm.provide_key_response.ok.count", 1 },
98      { "drm.mediadrm.get_provision_request.ok.count", 1 },
99      { "drm.mediadrm.provide_provision_response.ok.count", 1 },
100      { "drm.mediadrm.key_status_change.USABLE.count", 1 },
101      { "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 },
102      { "drm.mediadrm.get_device_unique_id.ok.count", 1 }};
103  for (const auto& expected_pair : expected_values) {
104    String16 key(expected_pair.first.c_str());
105    int64_t value = -1;
106    EXPECT_TRUE(bundle.getLong(key, &value))
107        << "Unexpected error retrieviing key: " << key;
108    EXPECT_EQ(expected_pair.second, value)
109        << "Unexpected value for " << expected_pair.first << ". " << value;
110  }
111
112  // Validate timing values exist.
113  String16 get_key_request_key(
114      "drm.mediadrm.get_key_request.ok.average_time_micros");
115  String16 provide_key_response_key(
116      "drm.mediadrm.provide_key_response.ok.average_time_micros");
117  int64_t value = -1;
118  EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
119  EXPECT_GE(value, 0);
120  value = -1;
121  EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
122  EXPECT_GE(value, 0);
123}
124
125TEST_F(MediaDrmMetricsTest, AllValuesFull) {
126  MediaDrmMetrics metrics;
127
128  metrics.mOpenSessionCounter.Increment(OK);
129  metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
130
131  metrics.mCloseSessionCounter.Increment(OK);
132  metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
133
134  for (status_t s : {OK, UNEXPECTED_NULL}) {
135    {
136      EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
137      EventTimer<status_t> provide_key_response_timer(
138          &metrics.mProvideKeyResponseTimeUs);
139      get_key_request_timer.SetAttribute(s);
140      provide_key_response_timer.SetAttribute(s);
141    }
142  }
143
144  metrics.mGetProvisionRequestCounter.Increment(OK);
145  metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL);
146  metrics.mProvideProvisionResponseCounter.Increment(OK);
147  metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL);
148  metrics.mGetDeviceUniqueIdCounter.Increment(OK);
149  metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL);
150
151  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
152  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED);
153  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED);
154  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING);
155  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR);
156  metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
157  metrics.mEventCounter.Increment(EventType::KEY_NEEDED);
158  metrics.mEventCounter.Increment(EventType::KEY_EXPIRED);
159  metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
160  metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
161
162  android::Vector<uint8_t> sessionId1;
163  sessionId1.push_back(1);
164  sessionId1.push_back(2);
165  android::Vector<uint8_t> sessionId2;
166  sessionId2.push_back(3);
167  sessionId2.push_back(4);
168  String16 hexSessionId1("0102");
169  String16 hexSessionId2("0304");
170
171  metrics.SetSessionStart(sessionId1);
172  metrics.SetSessionStart(sessionId2);
173  metrics.SetSessionEnd(sessionId2);
174  metrics.SetSessionEnd(sessionId1);
175
176  PersistableBundle bundle;
177  metrics.Export(&bundle);
178  EXPECT_EQ(35U, bundle.size());
179
180  // Verify the list of pairs of int64 metrics.
181  std::vector<std::pair<std::string, int64_t>> expected_values = {
182      { "drm.mediadrm.open_session.ok.count", 1 },
183      { "drm.mediadrm.close_session.ok.count", 1 },
184      { "drm.mediadrm.get_key_request.ok.count", 1 },
185      { "drm.mediadrm.provide_key_response.ok.count", 1 },
186      { "drm.mediadrm.get_provision_request.ok.count", 1 },
187      { "drm.mediadrm.provide_provision_response.ok.count", 1 },
188      { "drm.mediadrm.get_device_unique_id.ok.count", 1 },
189      { "drm.mediadrm.open_session.error.count", 1 },
190      { "drm.mediadrm.close_session.error.count", 1 },
191      { "drm.mediadrm.get_key_request.error.count", 1 },
192      { "drm.mediadrm.provide_key_response.error.count", 1 },
193      { "drm.mediadrm.get_provision_request.error.count", 1 },
194      { "drm.mediadrm.provide_provision_response.error.count", 1 },
195      { "drm.mediadrm.get_device_unique_id.error.count", 1 },
196      { "drm.mediadrm.key_status_change.USABLE.count", 1 },
197      { "drm.mediadrm.key_status_change.EXPIRED.count", 1 },
198      { "drm.mediadrm.key_status_change.OUTPUT_NOT_ALLOWED.count", 1 },
199      { "drm.mediadrm.key_status_change.STATUS_PENDING.count", 1 },
200      { "drm.mediadrm.key_status_change.INTERNAL_ERROR.count", 1 },
201      { "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 },
202      { "drm.mediadrm.event.KEY_NEEDED.count", 1 },
203      { "drm.mediadrm.event.KEY_EXPIRED.count", 1 },
204      { "drm.mediadrm.event.VENDOR_DEFINED.count", 1 },
205      { "drm.mediadrm.event.SESSION_RECLAIMED.count", 1 }};
206  for (const auto& expected_pair : expected_values) {
207    String16 key(expected_pair.first.c_str());
208    int64_t value = -1;
209    EXPECT_TRUE(bundle.getLong(key, &value))
210        << "Unexpected error retrieviing key: " << key;
211    EXPECT_EQ(expected_pair.second, value)
212        << "Unexpected value for " << expected_pair.first << ". " << value;
213  }
214
215  // Verify the error lists
216  std::vector<std::pair<std::string, std::vector<int64_t>>> expected_vector_values = {
217      { "drm.mediadrm.close_session.error.list", { UNEXPECTED_NULL } },
218      { "drm.mediadrm.get_device_unique_id.error.list", { UNEXPECTED_NULL } },
219      { "drm.mediadrm.get_key_request.error.list", { UNEXPECTED_NULL } },
220      { "drm.mediadrm.get_provision_request.error.list", { UNEXPECTED_NULL } },
221      { "drm.mediadrm.open_session.error.list", { UNEXPECTED_NULL } },
222      { "drm.mediadrm.provide_key_response.error.list", { UNEXPECTED_NULL } },
223      { "drm.mediadrm.provide_provision_response.error.list", { UNEXPECTED_NULL } }};
224  for (const auto& expected_pair : expected_vector_values) {
225    String16 key(expected_pair.first.c_str());
226    std::vector<int64_t> values;
227    EXPECT_TRUE(bundle.getLongVector(key, &values))
228        << "Unexpected error retrieviing key: " << key;
229    for (auto expected : expected_pair.second) {
230      EXPECT_TRUE(std::find(values.begin(), values.end(), expected) != values.end())
231          << "Could not find " << expected << " for key " << expected_pair.first;
232    }
233  }
234
235  // Verify the lifespans
236  PersistableBundle start_times;
237  PersistableBundle end_times;
238  String16 start_time_key("drm.mediadrm.session_start_times_ms");
239  String16 end_time_key("drm.mediadrm.session_end_times_ms");
240  ASSERT_TRUE(bundle.getPersistableBundle(start_time_key, &start_times));
241  ASSERT_TRUE(bundle.getPersistableBundle(end_time_key, &end_times));
242  EXPECT_EQ(2U, start_times.size());
243  EXPECT_EQ(2U, end_times.size());
244  int64_t start_time, end_time;
245  for (const auto& sid : { hexSessionId1, hexSessionId2 }) {
246    start_time = -1;
247    end_time = -1;
248    EXPECT_TRUE(start_times.getLong(sid, &start_time));
249    EXPECT_TRUE(end_times.getLong(sid, &end_time));
250    EXPECT_GT(start_time, 0);
251    EXPECT_GE(end_time, start_time);
252  }
253
254  // Validate timing values exist.
255  String16 get_key_request_key(
256      "drm.mediadrm.get_key_request.ok.average_time_micros");
257  String16 provide_key_response_key(
258      "drm.mediadrm.provide_key_response.ok.average_time_micros");
259  int64_t value = -1;
260  EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
261  EXPECT_GE(value, 0);
262  value = -1;
263  EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
264  EXPECT_GE(value, 0);
265}
266
267
268TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) {
269  MediaDrmMetrics metrics;
270
271  metrics.mOpenSessionCounter.Increment(OK);
272  metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
273  metrics.mCloseSessionCounter.Increment(OK);
274  metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
275
276  metrics.mGetProvisionRequestCounter.Increment(OK);
277  metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL);
278  metrics.mProvideProvisionResponseCounter.Increment(OK);
279  metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL);
280  metrics.mGetDeviceUniqueIdCounter.Increment(OK);
281  metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL);
282
283  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
284  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED);
285  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED);
286  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING);
287  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR);
288  metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
289  metrics.mEventCounter.Increment(EventType::KEY_NEEDED);
290  metrics.mEventCounter.Increment(EventType::KEY_EXPIRED);
291  metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
292  metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
293
294  std::string serializedMetrics;
295  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
296
297  DrmFrameworkMetrics metricsProto;
298  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
299
300  std::string expectedMetrics =
301      "open_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
302      "open_session_counter { count: 1 attributes { error_code: 0 } } "
303      "close_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
304      "close_session_counter { count: 1 attributes { error_code: 0 } } "
305      "get_provisioning_request_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
306      "get_provisioning_request_counter { count: 1 attributes { error_code: 0 } } "
307      "provide_provisioning_response_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
308      "provide_provisioning_response_counter { count: 1 attributes { error_code: 0 } } "
309      "get_device_unique_id_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
310      "get_device_unique_id_counter { count: 1 attributes { error_code: 0 } } "
311      "key_status_change_counter { count: 1 attributes { key_status_type: 0 } } "
312      "key_status_change_counter { count: 1 attributes { key_status_type: 1 } } "
313      "key_status_change_counter { count: 1 attributes { key_status_type: 2 } } "
314      "key_status_change_counter { count: 1 attributes { key_status_type: 3 } } "
315      "key_status_change_counter { count: 1 attributes { key_status_type: 4 } } "
316      "event_callback_counter { count: 1 attributes { event_type: 0 } } "
317      "event_callback_counter { count: 1 attributes { event_type: 1 } } "
318      "event_callback_counter { count: 1 attributes { event_type: 2 } } "
319      "event_callback_counter { count: 1 attributes { event_type: 3 } } "
320      "event_callback_counter { count: 1 attributes { event_type: 4 } } ";
321
322  DrmFrameworkMetrics expectedMetricsProto;
323  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
324
325  std::string diffString;
326  MessageDifferencer differ;
327  differ.ReportDifferencesToString(&diffString);
328  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
329      << diffString;
330}
331
332TEST_F(MediaDrmMetricsTest, TimeMetricsProtoSerialization) {
333  MediaDrmMetrics metrics;
334
335  for (status_t s : {OK, UNEXPECTED_NULL}) {
336    double time = 0;
337    for (int i = 0; i < 5; i++) {
338      time += 1.0;
339      metrics.mGetKeyRequestTimeUs.Record(time, s);
340      metrics.mProvideKeyResponseTimeUs.Record(time, s);
341    }
342  }
343
344  std::string serializedMetrics;
345  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
346
347  DrmFrameworkMetrics metricsProto;
348  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
349
350  std::string expectedMetrics =
351      "get_key_request_time_us { "
352      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
353      "  attributes { error_code: -0x7FFFFFF8 } "
354      "} "
355      "get_key_request_time_us { "
356      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
357      "  attributes { error_code: 0 } "
358      "} "
359      "provide_key_response_time_us { "
360      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
361      "  attributes { error_code: -0x7FFFFFF8 } "
362      "} "
363      "provide_key_response_time_us { "
364      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
365      "  attributes { error_code: 0 } "
366      "} ";
367
368  DrmFrameworkMetrics expectedMetricsProto;
369  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
370
371  std::string diffString;
372  MessageDifferencer differ;
373  differ.ReportDifferencesToString(&diffString);
374  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
375      << diffString;
376}
377
378TEST_F(MediaDrmMetricsTest, SessionLifetimeProtoSerialization) {
379  // Use the fake so the clock is predictable;
380  FakeMediaDrmMetrics metrics;
381
382  android::Vector<uint8_t> sessionId1;
383  sessionId1.push_back(1);
384  sessionId1.push_back(2);
385  android::Vector<uint8_t> sessionId2;
386  sessionId2.push_back(3);
387  sessionId2.push_back(4);
388
389  metrics.SetSessionStart(sessionId1);
390  metrics.SetSessionStart(sessionId2);
391  metrics.SetSessionEnd(sessionId2);
392  metrics.SetSessionEnd(sessionId1);
393
394  std::string serializedMetrics;
395  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
396
397  DrmFrameworkMetrics metricsProto;
398  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
399
400  std::string expectedMetrics =
401      "session_lifetimes: { "
402      "  key: '0102' "
403      "  value { start_time_ms: 0 end_time_ms: 3 } "
404      "} "
405      "session_lifetimes: { "
406      "  key: '0304' "
407      "  value { start_time_ms: 1 end_time_ms: 2 } "
408      "} ";
409
410  DrmFrameworkMetrics expectedMetricsProto;
411  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
412
413  std::string diffString;
414  MessageDifferencer differ;
415  differ.ReportDifferencesToString(&diffString);
416  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
417      << diffString;
418}
419
420TEST_F(MediaDrmMetricsTest, HidlToBundleMetricsEmpty) {
421  hidl_vec<DrmMetricGroup> hidlMetricGroups;
422  PersistableBundle bundleMetricGroups;
423
424  ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidlMetricGroups, &bundleMetricGroups));
425  ASSERT_EQ(0U, bundleMetricGroups.size());
426}
427
428TEST_F(MediaDrmMetricsTest, HidlToBundleMetricsMultiple) {
429  DrmMetricGroup hidlMetricGroup =
430      { { {
431              "open_session_ok",
432              { { "status", DrmMetricGroup::ValueType::INT64_TYPE,
433                  (int64_t) Status::OK, 0.0, "" } },
434              { { "count", DrmMetricGroup::ValueType::INT64_TYPE, 3, 0.0, "" } }
435          },
436          {
437              "close_session_not_opened",
438              { { "status", DrmMetricGroup::ValueType::INT64_TYPE,
439                  (int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, 0.0, "" } },
440              { { "count", DrmMetricGroup::ValueType::INT64_TYPE, 7, 0.0, "" } }
441          } } };
442
443  PersistableBundle bundleMetricGroups;
444  ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidl_vec<DrmMetricGroup>({hidlMetricGroup}),
445                                                     &bundleMetricGroups));
446  ASSERT_EQ(1U, bundleMetricGroups.size());
447  PersistableBundle bundleMetricGroup;
448  ASSERT_TRUE(bundleMetricGroups.getPersistableBundle(String16("[0]"), &bundleMetricGroup));
449  ASSERT_EQ(2U, bundleMetricGroup.size());
450
451  // Verify each metric.
452  PersistableBundle metric;
453  ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("open_session_ok"), &metric));
454  PersistableBundle metricInstance;
455  ASSERT_TRUE(metric.getPersistableBundle(String16("[0]"), &metricInstance));
456  int64_t value = 0;
457  ASSERT_TRUE(metricInstance.getLong(String16("count"), &value));
458  ASSERT_EQ(3, value);
459  PersistableBundle attributeBundle;
460  ASSERT_TRUE(metricInstance.getPersistableBundle(String16("attributes"), &attributeBundle));
461  ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value));
462  ASSERT_EQ((int64_t) Status::OK, value);
463
464  ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("close_session_not_opened"),
465                                                     &metric));
466  ASSERT_TRUE(metric.getPersistableBundle(String16("[0]"), &metricInstance));
467  ASSERT_TRUE(metricInstance.getLong(String16("count"), &value));
468  ASSERT_EQ(7, value);
469  ASSERT_TRUE(metricInstance.getPersistableBundle(String16("attributes"), &attributeBundle));
470  value = 0;
471  ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value));
472  ASSERT_EQ((int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, value);
473}
474
475}  // namespace android
476