1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/shell/ShellSubscriber.h"
16
17 #include <aidl/android/os/StatsSubscriptionCallbackReason.h>
18 #include <gtest/gtest.h>
19 #include <stdio.h>
20 #include <unistd.h>
21
22 #include <optional>
23 #include <vector>
24
25 #include "frameworks/proto_logging/stats/atoms.pb.h"
26 #include "gtest_matchers.h"
27 #include "src/shell/shell_config.pb.h"
28 #include "src/shell/shell_data.pb.h"
29 #include "stats_event.h"
30 #include "statslog_statsdtest.h"
31 #include "tests/metrics/metrics_test_helper.h"
32 #include "tests/statsd_test_util.h"
33
34 using ::aidl::android::os::StatsSubscriptionCallbackReason;
35 using android::sp;
36 using android::os::statsd::TestAtomReported;
37 using android::os::statsd::TrainExperimentIds;
38 using android::os::statsd::util::BytesField;
39 using android::os::statsd::util::CPU_ACTIVE_TIME;
40 using android::os::statsd::util::PHONE_SIGNAL_STRENGTH_CHANGED;
41 using android::os::statsd::util::PLUGGED_STATE_CHANGED;
42 using android::os::statsd::util::SCREEN_STATE_CHANGED;
43 using android::os::statsd::util::TEST_ATOM_REPORTED;
44 using std::vector;
45 using testing::_;
46 using testing::A;
47 using testing::ByMove;
48 using testing::DoAll;
49 using testing::Invoke;
50 using testing::NaggyMock;
51 using testing::Return;
52 using testing::SaveArg;
53 using testing::SetArgPointee;
54 using testing::StrictMock;
55
56 namespace android {
57 namespace os {
58 namespace statsd {
59
60 #ifdef __ANDROID__
61
62 namespace {
63
64 int kUid1 = 1000;
65 int kUid2 = 2000;
66
67 int kCpuTime1 = 100;
68 int kCpuTime2 = 200;
69
70 int64_t kCpuActiveTimeEventTimestampNs = 1111L;
71
72 // Number of clients running simultaneously
73
74 // Just a single client
75 const int kSingleClient = 1;
76 // One more client than allowed binder threads
77 const int kNumClients = 11;
78
79 // Utility to make an expected pulled atom shell data
getExpectedPulledData()80 ShellData getExpectedPulledData() {
81 ShellData shellData;
82 auto* atom1 = shellData.add_atom()->mutable_cpu_active_time();
83 atom1->set_uid(kUid1);
84 atom1->set_time_millis(kCpuTime1);
85 shellData.add_elapsed_timestamp_nanos(kCpuActiveTimeEventTimestampNs);
86
87 auto* atom2 = shellData.add_atom()->mutable_cpu_active_time();
88 atom2->set_uid(kUid2);
89 atom2->set_time_millis(kCpuTime2);
90 shellData.add_elapsed_timestamp_nanos(kCpuActiveTimeEventTimestampNs);
91
92 return shellData;
93 }
94
95 // Utility to make a pulled atom Shell Config
getPulledConfig()96 ShellSubscription getPulledConfig() {
97 ShellSubscription config;
98 auto* pull_config = config.add_pulled();
99 pull_config->mutable_matcher()->set_atom_id(CPU_ACTIVE_TIME);
100 pull_config->set_freq_millis(2000);
101 return config;
102 }
103
104 // Utility to adjust CPU time for pulled events
makeCpuActiveTimeAtom(int32_t uid,int64_t timeMillis)105 shared_ptr<LogEvent> makeCpuActiveTimeAtom(int32_t uid, int64_t timeMillis) {
106 AStatsEvent* statsEvent = AStatsEvent_obtain();
107 AStatsEvent_setAtomId(statsEvent, CPU_ACTIVE_TIME);
108 AStatsEvent_overwriteTimestamp(statsEvent, kCpuActiveTimeEventTimestampNs);
109 AStatsEvent_writeInt32(statsEvent, uid);
110 AStatsEvent_writeInt64(statsEvent, timeMillis);
111
112 std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
113 parseStatsEventToLogEvent(statsEvent, logEvent.get());
114 return logEvent;
115 }
116
117 // Utility to create pushed atom LogEvents
getPushedEvents()118 vector<std::shared_ptr<LogEvent>> getPushedEvents() {
119 vector<std::shared_ptr<LogEvent>> pushedList;
120 // Create the LogEvent from an AStatsEvent
121 std::unique_ptr<LogEvent> logEvent1 = CreateScreenStateChangedEvent(
122 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
123 std::unique_ptr<LogEvent> logEvent2 = CreateScreenStateChangedEvent(
124 2000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
125 std::unique_ptr<LogEvent> logEvent3 = CreateBatteryStateChangedEvent(
126 3000 /*timestamp*/, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
127 std::unique_ptr<LogEvent> logEvent4 = CreateBatteryStateChangedEvent(
128 4000 /*timestamp*/, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
129 pushedList.push_back(std::move(logEvent1));
130 pushedList.push_back(std::move(logEvent2));
131 pushedList.push_back(std::move(logEvent3));
132 pushedList.push_back(std::move(logEvent4));
133 return pushedList;
134 }
135
136 // Utility to read & return ShellData proto, skipping heartbeats.
readData(int fd)137 static ShellData readData(int fd) {
138 ssize_t dataSize = 0;
139 while (dataSize == 0) {
140 read(fd, &dataSize, sizeof(dataSize));
141 }
142 // Read that much data in proto binary format.
143 vector<uint8_t> dataBuffer(dataSize);
144 EXPECT_EQ((int)dataSize, read(fd, dataBuffer.data(), dataSize));
145
146 // Make sure the received bytes can be parsed to an atom.
147 ShellData receivedAtom;
148 EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
149 return receivedAtom;
150 }
151
runShellTest(ShellSubscription config,sp<MockUidMap> uidMap,sp<MockStatsPullerManager> pullerManager,const vector<std::shared_ptr<LogEvent>> & pushedEvents,const vector<ShellData> & expectedData,int numClients)152 void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
153 sp<MockStatsPullerManager> pullerManager,
154 const vector<std::shared_ptr<LogEvent>>& pushedEvents,
155 const vector<ShellData>& expectedData, int numClients) {
156 sp<ShellSubscriber> shellManager =
157 new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
158
159 size_t bufferSize = config.ByteSize();
160 vector<uint8_t> buffer(bufferSize);
161 config.SerializeToArray(&buffer[0], bufferSize);
162
163 int fds_configs[numClients][2];
164 int fds_datas[numClients][2];
165 for (int i = 0; i < numClients; i++) {
166 // set up 2 pipes for read/write config and data
167 ASSERT_EQ(0, pipe2(fds_configs[i], O_CLOEXEC));
168 ASSERT_EQ(0, pipe2(fds_datas[i], O_CLOEXEC));
169
170 // write the config to pipe, first write size of the config
171 write(fds_configs[i][1], &bufferSize, sizeof(bufferSize));
172 // then write config itself
173 write(fds_configs[i][1], buffer.data(), bufferSize);
174 close(fds_configs[i][1]);
175
176 shellManager->startNewSubscription(fds_configs[i][0], fds_datas[i][1], /*timeoutSec=*/-1);
177 close(fds_configs[i][0]);
178 close(fds_datas[i][1]);
179 }
180
181 // send a log event that matches the config.
182 for (const auto& event : pushedEvents) {
183 shellManager->onLogEvent(*event);
184 }
185
186 for (int i = 0; i < numClients; i++) {
187 vector<ShellData> actualData;
188 for (int j = 1; j <= expectedData.size(); j++) {
189 actualData.push_back(readData(fds_datas[i][0]));
190 }
191
192 EXPECT_THAT(expectedData, UnorderedPointwise(EqShellData(), actualData));
193 }
194
195 // Not closing fds_datas[i][0] because this causes writes within ShellSubscriberClient to hang
196 }
197
createTestAtomReportedEvent(const uint64_t timestampNs,const int32_t intFieldValue,const vector<int64_t> & expIds)198 unique_ptr<LogEvent> createTestAtomReportedEvent(const uint64_t timestampNs,
199 const int32_t intFieldValue,
200 const vector<int64_t>& expIds) {
201 TrainExperimentIds trainExpIds;
202 *trainExpIds.mutable_experiment_id() = {expIds.begin(), expIds.end()};
203 const vector<uint8_t> trainExpIdsBytes = protoToBytes(trainExpIds);
204 return CreateTestAtomReportedEvent(
205 timestampNs, /* attributionUids */ {1001},
206 /* attributionTags */ {"app1"}, intFieldValue, /*longField */ 0LL,
207 /* floatField */ 0.0f,
208 /* stringField */ "abc", /* boolField */ false, TestAtomReported::OFF, trainExpIdsBytes,
209 /* repeatedIntField */ {}, /* repeatedLongField */ {}, /* repeatedFloatField */ {},
210 /* repeatedStringField */ {}, /* repeatedBoolField */ {},
211 /* repeatedBoolFieldLength */ 0, /* repeatedEnumField */ {});
212 }
213
createTestAtomReportedProto(const int32_t intFieldValue,const vector<int64_t> & expIds)214 TestAtomReported createTestAtomReportedProto(const int32_t intFieldValue,
215 const vector<int64_t>& expIds) {
216 TestAtomReported t;
217 auto* attributionNode = t.add_attribution_node();
218 attributionNode->set_uid(1001);
219 attributionNode->set_tag("app1");
220 t.set_int_field(intFieldValue);
221 t.set_long_field(0);
222 t.set_float_field(0.0f);
223 t.set_string_field("abc");
224 t.set_boolean_field(false);
225 t.set_state(TestAtomReported_State_OFF);
226 *t.mutable_bytes_field()->mutable_experiment_id() = {expIds.begin(), expIds.end()};
227 return t;
228 }
229
230 class ShellSubscriberCallbackTest : public ::testing::Test {
231 protected:
ShellSubscriberCallbackTest()232 ShellSubscriberCallbackTest()
233 : uidMap(new NaggyMock<MockUidMap>()),
234 pullerManager(new StrictMock<MockStatsPullerManager>()),
235 mockLogEventFilter(std::make_shared<MockLogEventFilter>()),
236 shellSubscriber(uidMap, pullerManager, mockLogEventFilter),
237 callback(SharedRefBase::make<StrictMock<MockStatsSubscriptionCallback>>()),
238 reason(nullopt) {
239 }
240
SetUp()241 void SetUp() override {
242 // Save callback arguments when it is invoked.
243 ON_CALL(*callback, onSubscriptionData(_, _))
244 .WillByDefault(DoAll(SaveArg<0>(&reason), SaveArg<1>(&payload),
245 Return(ByMove(Status::ok()))));
246
247 ShellSubscription config;
248 config.add_pushed()->set_atom_id(TEST_ATOM_REPORTED);
249 config.add_pushed()->set_atom_id(SCREEN_STATE_CHANGED);
250 config.add_pushed()->set_atom_id(PHONE_SIGNAL_STRENGTH_CHANGED);
251 configBytes = protoToBytes(config);
252 }
253
TearDown()254 void TearDown() override {
255 // Expect empty call from the shellSubscriber destructor
256 LogEventFilter::AtomIdSet tagIds;
257 EXPECT_CALL(*mockLogEventFilter, setAtomIds(tagIds, &shellSubscriber)).Times(1);
258 }
259
260 sp<MockUidMap> uidMap;
261 sp<MockStatsPullerManager> pullerManager;
262 std::shared_ptr<MockLogEventFilter> mockLogEventFilter;
263 ShellSubscriber shellSubscriber;
264 std::shared_ptr<MockStatsSubscriptionCallback> callback;
265 vector<uint8_t> configBytes;
266
267 // Capture callback arguments.
268 std::optional<StatsSubscriptionCallbackReason> reason;
269 vector<uint8_t> payload;
270 };
271
272 class ShellSubscriberCallbackPulledTest : public ShellSubscriberCallbackTest {
273 protected:
SetUp()274 void SetUp() override {
275 ShellSubscriberCallbackTest::SetUp();
276
277 const vector<int32_t> uids{AID_SYSTEM};
278 const vector<std::shared_ptr<LogEvent>> pulledData{
279 makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1),
280 makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2)};
281 ON_CALL(*pullerManager, Pull(CPU_ACTIVE_TIME, uids, _, _))
282 .WillByDefault(DoAll(SetArgPointee<3>(pulledData), Return(true)));
283
284 configBytes = protoToBytes(getPulledConfig());
285
286 // Used to call pullAndSendHeartbeatsIfNeeded directly without depending on sleep.
287 shellSubscriberClient = std::move(ShellSubscriberClient::create(
288 configBytes, callback, /* startTimeSec= */ 0, uidMap, pullerManager));
289 }
290
291 unique_ptr<ShellSubscriberClient> shellSubscriberClient;
292 };
293
CreateAtomIdSetFromShellSubscriptionBytes(const vector<uint8_t> & bytes)294 LogEventFilter::AtomIdSet CreateAtomIdSetFromShellSubscriptionBytes(const vector<uint8_t>& bytes) {
295 LogEventFilter::AtomIdSet result;
296
297 ShellSubscription config;
298 config.ParseFromArray(bytes.data(), bytes.size());
299
300 for (int i = 0; i < config.pushed_size(); i++) {
301 const auto& pushed = config.pushed(i);
302 EXPECT_TRUE(pushed.has_atom_id());
303 result.insert(pushed.atom_id());
304 }
305
306 return result;
307 }
308
309 } // namespace
310
TEST_F(ShellSubscriberCallbackTest,testAddSubscription)311 TEST_F(ShellSubscriberCallbackTest, testAddSubscription) {
312 EXPECT_CALL(
313 *mockLogEventFilter,
314 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes), &shellSubscriber))
315 .Times(1);
316 EXPECT_TRUE(shellSubscriber.startNewSubscription(configBytes, callback));
317 }
318
TEST_F(ShellSubscriberCallbackTest,testAddSubscriptionExceedMax)319 TEST_F(ShellSubscriberCallbackTest, testAddSubscriptionExceedMax) {
320 const size_t maxSubs = ShellSubscriber::getMaxSubscriptions();
321 EXPECT_CALL(
322 *mockLogEventFilter,
323 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes), &shellSubscriber))
324 .Times(maxSubs);
325 vector<bool> results(maxSubs, false);
326 for (int i = 0; i < maxSubs; i++) {
327 results[i] = shellSubscriber.startNewSubscription(configBytes, callback);
328 }
329
330 // First maxSubs subscriptions should succeed.
331 EXPECT_THAT(results, Each(IsTrue()));
332
333 // Subsequent startNewSubscription should fail.
334 EXPECT_FALSE(shellSubscriber.startNewSubscription(configBytes, callback));
335 }
336
TEST_F(ShellSubscriberCallbackTest,testPushedEventsAreCached)337 TEST_F(ShellSubscriberCallbackTest, testPushedEventsAreCached) {
338 // Expect callback to not be invoked
339 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(0));
340 EXPECT_CALL(
341 *mockLogEventFilter,
342 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes), &shellSubscriber))
343 .Times(1);
344 shellSubscriber.startNewSubscription(configBytes, callback);
345
346 // Log an event that does NOT invoke the callack.
347 shellSubscriber.onLogEvent(*CreateScreenStateChangedEvent(
348 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON));
349 }
350
TEST_F(ShellSubscriberCallbackTest,testOverflowCacheIsFlushed)351 TEST_F(ShellSubscriberCallbackTest, testOverflowCacheIsFlushed) {
352 // Expect callback to be invoked once.
353 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
354 EXPECT_CALL(
355 *mockLogEventFilter,
356 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes), &shellSubscriber))
357 .Times(1);
358 shellSubscriber.startNewSubscription(configBytes, callback);
359
360 shellSubscriber.onLogEvent(*CreateScreenStateChangedEvent(
361 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON));
362
363 // Inflate size of TestAtomReported through the MODE_BYTES field.
364 const vector<int64_t> expIds = vector<int64_t>(200, INT64_MAX);
365
366 // This event should trigger cache overflow flush.
367 shellSubscriber.onLogEvent(*createTestAtomReportedEvent(/*timestampNs=*/1100,
368 /*intFieldValue=*/1, expIds));
369
370 EXPECT_THAT(reason, Eq(StatsSubscriptionCallbackReason::STATSD_INITIATED));
371
372 // Get ShellData proto from the bytes payload of the callback.
373 ShellData actualShellData;
374 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
375
376 ShellData expectedShellData;
377 expectedShellData.add_atom()->mutable_screen_state_changed()->set_state(
378 ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
379 *expectedShellData.add_atom()->mutable_test_atom_reported() =
380 createTestAtomReportedProto(/* intFieldValue=*/1, expIds);
381 expectedShellData.add_elapsed_timestamp_nanos(1000);
382 expectedShellData.add_elapsed_timestamp_nanos(1100);
383
384 EXPECT_THAT(actualShellData, EqShellData(expectedShellData));
385 }
386
TEST_F(ShellSubscriberCallbackTest,testFlushTrigger)387 TEST_F(ShellSubscriberCallbackTest, testFlushTrigger) {
388 // Expect callback to be invoked once.
389 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
390 EXPECT_CALL(
391 *mockLogEventFilter,
392 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes), &shellSubscriber))
393 .Times(1);
394 shellSubscriber.startNewSubscription(configBytes, callback);
395
396 shellSubscriber.onLogEvent(*CreateScreenStateChangedEvent(
397 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON));
398
399 shellSubscriber.flushSubscription(callback);
400
401 EXPECT_THAT(reason, Eq(StatsSubscriptionCallbackReason::FLUSH_REQUESTED));
402
403 // Get ShellData proto from the bytes payload of the callback.
404 ShellData actualShellData;
405 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
406
407 ShellData expectedShellData;
408 expectedShellData.add_atom()->mutable_screen_state_changed()->set_state(
409 ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
410 expectedShellData.add_elapsed_timestamp_nanos(1000);
411
412 EXPECT_THAT(actualShellData, EqShellData(expectedShellData));
413 }
414
TEST_F(ShellSubscriberCallbackTest,testFlushTriggerEmptyCache)415 TEST_F(ShellSubscriberCallbackTest, testFlushTriggerEmptyCache) {
416 // Expect callback to be invoked once.
417 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
418 EXPECT_CALL(
419 *mockLogEventFilter,
420 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes), &shellSubscriber))
421 .Times(1);
422 shellSubscriber.startNewSubscription(configBytes, callback);
423
424 shellSubscriber.flushSubscription(callback);
425
426 EXPECT_THAT(reason, Eq(StatsSubscriptionCallbackReason::FLUSH_REQUESTED));
427
428 // Get ShellData proto from the bytes payload of the callback.
429 ShellData actualShellData;
430 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
431
432 ShellData expectedShellData;
433
434 EXPECT_THAT(actualShellData, EqShellData(expectedShellData));
435 }
436
TEST_F(ShellSubscriberCallbackTest,testUnsubscribe)437 TEST_F(ShellSubscriberCallbackTest, testUnsubscribe) {
438 // Expect callback to be invoked once.
439 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
440 Expectation newSubcriptionEvent =
441 EXPECT_CALL(*mockLogEventFilter,
442 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes),
443 &shellSubscriber))
444 .Times(1);
445 LogEventFilter::AtomIdSet idSetEmpty;
446 EXPECT_CALL(*mockLogEventFilter, setAtomIds(idSetEmpty, &shellSubscriber))
447 .Times(1)
448 .After(newSubcriptionEvent);
449
450 shellSubscriber.startNewSubscription(configBytes, callback);
451
452 shellSubscriber.onLogEvent(*CreateScreenStateChangedEvent(
453 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON));
454
455 shellSubscriber.unsubscribe(callback);
456
457 EXPECT_THAT(reason, Eq(StatsSubscriptionCallbackReason::SUBSCRIPTION_ENDED));
458
459 // Get ShellData proto from the bytes payload of the callback.
460 ShellData actualShellData;
461 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
462
463 ShellData expectedShellData;
464 expectedShellData.add_atom()->mutable_screen_state_changed()->set_state(
465 ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
466 expectedShellData.add_elapsed_timestamp_nanos(1000);
467
468 EXPECT_THAT(actualShellData, EqShellData(expectedShellData));
469
470 // This event is ignored as the subscription has ended.
471 shellSubscriber.onLogEvent(*CreateScreenStateChangedEvent(
472 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON));
473
474 // This should be a no-op as we've already unsubscribed.
475 shellSubscriber.unsubscribe(callback);
476 }
477
TEST_F(ShellSubscriberCallbackTest,testUnsubscribeEmptyCache)478 TEST_F(ShellSubscriberCallbackTest, testUnsubscribeEmptyCache) {
479 // Expect callback to be invoked once.
480 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
481 Expectation newSubcriptionEvent =
482 EXPECT_CALL(*mockLogEventFilter,
483 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes),
484 &shellSubscriber))
485 .Times(1);
486 LogEventFilter::AtomIdSet idSetEmpty;
487 EXPECT_CALL(*mockLogEventFilter, setAtomIds(idSetEmpty, &shellSubscriber))
488 .Times(1)
489 .After(newSubcriptionEvent);
490
491 shellSubscriber.startNewSubscription(configBytes, callback);
492
493 shellSubscriber.unsubscribe(callback);
494
495 EXPECT_THAT(reason, Eq(StatsSubscriptionCallbackReason::SUBSCRIPTION_ENDED));
496
497 // Get ShellData proto from the bytes payload of the callback.
498 ShellData actualShellData;
499 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
500
501 ShellData expectedShellData;
502
503 EXPECT_THAT(actualShellData, EqShellData(expectedShellData));
504 }
505
TEST_F(ShellSubscriberCallbackTest,testTruncateTimestampAtom)506 TEST_F(ShellSubscriberCallbackTest, testTruncateTimestampAtom) {
507 // Expect callback to be invoked once.
508 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
509 EXPECT_CALL(
510 *mockLogEventFilter,
511 setAtomIds(CreateAtomIdSetFromShellSubscriptionBytes(configBytes), &shellSubscriber))
512 .Times(1);
513 shellSubscriber.startNewSubscription(configBytes, callback);
514
515 shellSubscriber.onLogEvent(*CreatePhoneSignalStrengthChangedEvent(
516 NS_PER_SEC * 5 * 60 + 1000 /*timestamp*/,
517 ::android::telephony::SignalStrengthEnum::SIGNAL_STRENGTH_GOOD));
518
519 shellSubscriber.flushSubscription(callback);
520
521 // Get ShellData proto from the bytes payload of the callback.
522 ShellData actualShellData;
523 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
524
525 ShellData expectedShellData;
526 expectedShellData.add_atom()->mutable_phone_signal_strength_changed()->set_signal_strength(
527 ::android::telephony::SignalStrengthEnum::SIGNAL_STRENGTH_GOOD);
528 expectedShellData.add_elapsed_timestamp_nanos(NS_PER_SEC * 5 * 60);
529
530 EXPECT_THAT(actualShellData, EqShellData(expectedShellData));
531 }
532
TEST_F(ShellSubscriberCallbackPulledTest,testPullIfNeededBeforeInterval)533 TEST_F(ShellSubscriberCallbackPulledTest, testPullIfNeededBeforeInterval) {
534 // Pull should not happen
535 EXPECT_CALL(*pullerManager, Pull(_, A<const vector<int32_t>&>(), _, _)).Times(Exactly(0));
536
537 // Expect callback to not be invoked.
538 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(0));
539
540 shellSubscriberClient->pullAndSendHeartbeatsIfNeeded(/* nowSecs= */ 0, /* nowMillis= */ 0,
541 /* nowNanos= */ 0);
542 }
543
TEST_F(ShellSubscriberCallbackPulledTest,testPullAtInterval)544 TEST_F(ShellSubscriberCallbackPulledTest, testPullAtInterval) {
545 // Pull should happen once. The data is cached.
546 EXPECT_CALL(*pullerManager, Pull(_, A<const vector<int32_t>&>(), _, _)).Times(Exactly(1));
547
548 // Expect callback to not be invoked.
549 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(0));
550
551 // This pull should NOT trigger a cache flush.
552 shellSubscriberClient->pullAndSendHeartbeatsIfNeeded(/* nowSecs= */ 61, /* nowMillis= */ 61'000,
553 /* nowNanos= */ 61'000'000'000);
554 }
555
TEST_F(ShellSubscriberCallbackPulledTest,testCachedPullIsFlushed)556 TEST_F(ShellSubscriberCallbackPulledTest, testCachedPullIsFlushed) {
557 // Pull should happen once. The data is cached.
558 EXPECT_CALL(*pullerManager, Pull(_, A<const vector<int32_t>&>(), _, _)).Times(Exactly(1));
559
560 // This pull should NOT trigger a cache flush.
561 shellSubscriberClient->pullAndSendHeartbeatsIfNeeded(/* nowSecs= */ 61, /* nowMillis= */ 61'000,
562 /* nowNanos= */ 61'000'000'000);
563
564 // Expect callback to be invoked once flush is requested.
565 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
566
567 // This should flush out data cached from the pull.
568 shellSubscriberClient->flush();
569
570 EXPECT_THAT(reason, Eq(StatsSubscriptionCallbackReason::FLUSH_REQUESTED));
571
572 // Get ShellData proto from the bytes payload of the callback.
573 ShellData actualShellData;
574 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
575
576 EXPECT_THAT(actualShellData, EqShellData(getExpectedPulledData()));
577 }
578
TEST_F(ShellSubscriberCallbackPulledTest,testPullAtCacheTimeout)579 TEST_F(ShellSubscriberCallbackPulledTest, testPullAtCacheTimeout) {
580 // Pull should happen once. The data is flushed.
581 EXPECT_CALL(*pullerManager, Pull(_, A<const vector<int32_t>&>(), _, _)).Times(Exactly(1));
582
583 // Expect callback to be invoked.
584 EXPECT_CALL(*callback, onSubscriptionData(_, _)).Times(Exactly(1));
585
586 // This pull should trigger a cache flush.
587 shellSubscriberClient->pullAndSendHeartbeatsIfNeeded(/* nowSecs= */ 70, /* nowMillis= */ 70'000,
588 /* nowNanos= */ 70'000'000'000);
589
590 EXPECT_THAT(reason, Eq(StatsSubscriptionCallbackReason::STATSD_INITIATED));
591
592 // Get ShellData proto from the bytes payload of the callback.
593 ShellData actualShellData;
594 ASSERT_TRUE(actualShellData.ParseFromArray(payload.data(), payload.size()));
595
596 EXPECT_THAT(actualShellData, EqShellData(getExpectedPulledData()));
597 }
598
TEST_F(ShellSubscriberCallbackPulledTest,testPullFrequencyTooShort)599 TEST_F(ShellSubscriberCallbackPulledTest, testPullFrequencyTooShort) {
600 // Pull should NOT happen.
601 EXPECT_CALL(*pullerManager, Pull(_, A<const vector<int32_t>&>(), _, _)).Times(Exactly(0));
602
603 // This should not trigger a pull even though the timestamp passed in matches the pull interval
604 // specified in the config.
605 const int64_t sleepTimeMs =
606 shellSubscriberClient->pullAndSendHeartbeatsIfNeeded(2, 2000, 2'000'000'000);
607 }
608
TEST_F(ShellSubscriberCallbackPulledTest,testMinSleep)609 TEST_F(ShellSubscriberCallbackPulledTest, testMinSleep) {
610 // Pull should NOT happen.
611 EXPECT_CALL(*pullerManager, Pull(_, A<const vector<int32_t>&>(), _, _)).Times(Exactly(0));
612
613 const int64_t sleepTimeMs =
614 shellSubscriberClient->pullAndSendHeartbeatsIfNeeded(59, 59'000, 59'000'000'000);
615
616 // Even though there is only 1000 ms left until the next pull, the sleep time returned is
617 // kMinCallbackSleepIntervalMs.
618 EXPECT_THAT(sleepTimeMs, Eq(ShellSubscriberClient::kMinCallbackSleepIntervalMs));
619 }
620
TEST(ShellSubscriberTest,testPushedSubscription)621 TEST(ShellSubscriberTest, testPushedSubscription) {
622 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
623 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
624
625 vector<std::shared_ptr<LogEvent>> pushedList = getPushedEvents();
626
627 // create a simple config to get screen events
628 ShellSubscription config;
629 config.add_pushed()->set_atom_id(SCREEN_STATE_CHANGED);
630
631 // this is the expected screen event atom.
632 vector<ShellData> expectedData;
633 ShellData shellData1;
634 shellData1.add_atom()->mutable_screen_state_changed()->set_state(
635 ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
636 shellData1.add_elapsed_timestamp_nanos(pushedList[0]->GetElapsedTimestampNs());
637 ShellData shellData2;
638 shellData2.add_atom()->mutable_screen_state_changed()->set_state(
639 ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
640 shellData2.add_elapsed_timestamp_nanos(pushedList[1]->GetElapsedTimestampNs());
641 expectedData.push_back(shellData1);
642 expectedData.push_back(shellData2);
643
644 // Test with single client
645 TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData,
646 kSingleClient);
647
648 // Test with multiple client
649 TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData, kNumClients);
650 }
651
TEST(ShellSubscriberTest,testPulledSubscription)652 TEST(ShellSubscriberTest, testPulledSubscription) {
653 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
654 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
655
656 const vector<int32_t> uids = {AID_SYSTEM};
657 EXPECT_CALL(*pullerManager, Pull(CPU_ACTIVE_TIME, uids, _, _))
658 .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
659 vector<std::shared_ptr<LogEvent>>* data) {
660 data->clear();
661 data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
662 data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
663 return true;
664 }));
665
666 // Test with single client
667 TRACE_CALL(runShellTest, getPulledConfig(), uidMap, pullerManager, /*pushedEvents=*/{},
668 {getExpectedPulledData()}, kSingleClient);
669
670 // Test with multiple clients.
671 TRACE_CALL(runShellTest, getPulledConfig(), uidMap, pullerManager, {},
672 {getExpectedPulledData()}, kNumClients);
673 }
674
TEST(ShellSubscriberTest,testBothSubscriptions)675 TEST(ShellSubscriberTest, testBothSubscriptions) {
676 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
677 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
678
679 const vector<int32_t> uids = {AID_SYSTEM};
680 EXPECT_CALL(*pullerManager, Pull(CPU_ACTIVE_TIME, uids, _, _))
681 .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
682 vector<std::shared_ptr<LogEvent>>* data) {
683 data->clear();
684 data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
685 data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
686 return true;
687 }));
688
689 vector<std::shared_ptr<LogEvent>> pushedList = getPushedEvents();
690
691 ShellSubscription config = getPulledConfig();
692 config.add_pushed()->set_atom_id(SCREEN_STATE_CHANGED);
693
694 vector<ShellData> expectedData;
695 ShellData shellData1;
696 shellData1.add_atom()->mutable_screen_state_changed()->set_state(
697 ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
698 shellData1.add_elapsed_timestamp_nanos(pushedList[0]->GetElapsedTimestampNs());
699 ShellData shellData2;
700 shellData2.add_atom()->mutable_screen_state_changed()->set_state(
701 ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
702 shellData2.add_elapsed_timestamp_nanos(pushedList[1]->GetElapsedTimestampNs());
703 expectedData.push_back(getExpectedPulledData());
704 expectedData.push_back(shellData1);
705 expectedData.push_back(shellData2);
706
707 // Test with single client
708 TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData,
709 kSingleClient);
710
711 // Test with multiple client
712 TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData, kNumClients);
713 }
714
TEST(ShellSubscriberTest,testMaxSizeGuard)715 TEST(ShellSubscriberTest, testMaxSizeGuard) {
716 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
717 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
718 sp<ShellSubscriber> shellManager =
719 new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
720
721 // set up 2 pipes for read/write config and data
722 int fds_config[2];
723 ASSERT_EQ(0, pipe2(fds_config, O_CLOEXEC));
724
725 int fds_data[2];
726 ASSERT_EQ(0, pipe2(fds_data, O_CLOEXEC));
727
728 // write invalid size of the config
729 size_t invalidBufferSize = (shellManager->getMaxSizeKb() * 1024) + 1;
730 write(fds_config[1], &invalidBufferSize, sizeof(invalidBufferSize));
731 close(fds_config[1]);
732 close(fds_data[0]);
733
734 EXPECT_FALSE(shellManager->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1));
735 close(fds_config[0]);
736 close(fds_data[1]);
737 }
738
TEST(ShellSubscriberTest,testMaxSubscriptionsGuard)739 TEST(ShellSubscriberTest, testMaxSubscriptionsGuard) {
740 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
741 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
742 sp<ShellSubscriber> shellManager =
743 new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
744
745 // create a simple config to get screen events
746 ShellSubscription config;
747 config.add_pushed()->set_atom_id(SCREEN_STATE_CHANGED);
748
749 size_t bufferSize = config.ByteSize();
750 vector<uint8_t> buffer(bufferSize);
751 config.SerializeToArray(&buffer[0], bufferSize);
752
753 size_t maxSubs = shellManager->getMaxSubscriptions();
754 int fds_configs[maxSubs + 1][2];
755 int fds_datas[maxSubs + 1][2];
756 for (int i = 0; i < maxSubs; i++) {
757 // set up 2 pipes for read/write config and data
758 ASSERT_EQ(0, pipe2(fds_configs[i], O_CLOEXEC));
759 ASSERT_EQ(0, pipe2(fds_datas[i], O_CLOEXEC));
760
761 // write the config to pipe, first write size of the config
762 write(fds_configs[i][1], &bufferSize, sizeof(bufferSize));
763 // then write config itself
764 write(fds_configs[i][1], buffer.data(), bufferSize);
765 close(fds_configs[i][1]);
766
767 EXPECT_TRUE(shellManager->startNewSubscription(fds_configs[i][0], fds_datas[i][1],
768 /*timeoutSec=*/-1));
769 close(fds_configs[i][0]);
770 close(fds_datas[i][1]);
771 }
772 ASSERT_EQ(0, pipe2(fds_configs[maxSubs], O_CLOEXEC));
773 ASSERT_EQ(0, pipe2(fds_datas[maxSubs], O_CLOEXEC));
774
775 // write the config to pipe, first write size of the config
776 write(fds_configs[maxSubs][1], &bufferSize, sizeof(bufferSize));
777 // then write config itself
778 write(fds_configs[maxSubs][1], buffer.data(), bufferSize);
779 close(fds_configs[maxSubs][1]);
780
781 EXPECT_FALSE(shellManager->startNewSubscription(fds_configs[maxSubs][0], fds_datas[maxSubs][1],
782 /*timeoutSec=*/-1));
783 close(fds_configs[maxSubs][0]);
784 close(fds_datas[maxSubs][1]);
785
786 // Not closing fds_datas[i][0] because this causes writes within ShellSubscriberClient to hang
787 }
788
TEST(ShellSubscriberTest,testDifferentConfigs)789 TEST(ShellSubscriberTest, testDifferentConfigs) {
790 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
791 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
792 sp<ShellSubscriber> shellManager =
793 new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
794
795 // number of different configs
796 int numConfigs = 2;
797
798 // create a simple config to get screen events
799 ShellSubscription configs[numConfigs];
800 configs[0].add_pushed()->set_atom_id(SCREEN_STATE_CHANGED);
801 configs[1].add_pushed()->set_atom_id(PLUGGED_STATE_CHANGED);
802
803 vector<vector<uint8_t>> configBuffers;
804 for (int i = 0; i < numConfigs; i++) {
805 size_t bufferSize = configs[i].ByteSize();
806 vector<uint8_t> buffer(bufferSize);
807 configs[i].SerializeToArray(&buffer[0], bufferSize);
808 configBuffers.push_back(buffer);
809 }
810
811 int fds_configs[numConfigs][2];
812 int fds_datas[numConfigs][2];
813 for (int i = 0; i < numConfigs; i++) {
814 // set up 2 pipes for read/write config and data
815 ASSERT_EQ(0, pipe2(fds_configs[i], O_CLOEXEC));
816 ASSERT_EQ(0, pipe2(fds_datas[i], O_CLOEXEC));
817
818 size_t configSize = configBuffers[i].size();
819 // write the config to pipe, first write size of the config
820 write(fds_configs[i][1], &configSize, sizeof(configSize));
821 // then write config itself
822 write(fds_configs[i][1], configBuffers[i].data(), configSize);
823 close(fds_configs[i][1]);
824
825 EXPECT_TRUE(shellManager->startNewSubscription(fds_configs[i][0], fds_datas[i][1],
826 /*timeoutSec=*/-1));
827 close(fds_configs[i][0]);
828 close(fds_datas[i][1]);
829 }
830
831 // send a log event that matches the config.
832 vector<std::shared_ptr<LogEvent>> pushedList = getPushedEvents();
833 for (const auto& event : pushedList) {
834 shellManager->onLogEvent(*event);
835 }
836
837 // Validate Config 1
838 ShellData actual1 = readData(fds_datas[0][0]);
839 ShellData expected1;
840 expected1.add_atom()->mutable_screen_state_changed()->set_state(
841 ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
842 expected1.add_elapsed_timestamp_nanos(pushedList[0]->GetElapsedTimestampNs());
843 EXPECT_THAT(expected1, EqShellData(actual1));
844
845 ShellData actual2 = readData(fds_datas[0][0]);
846 ShellData expected2;
847 expected2.add_atom()->mutable_screen_state_changed()->set_state(
848 ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
849 expected2.add_elapsed_timestamp_nanos(pushedList[1]->GetElapsedTimestampNs());
850 EXPECT_THAT(expected2, EqShellData(actual2));
851
852 // Validate Config 2, repeating the process
853 ShellData actual3 = readData(fds_datas[1][0]);
854 ShellData expected3;
855 expected3.add_atom()->mutable_plugged_state_changed()->set_state(
856 BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
857 expected3.add_elapsed_timestamp_nanos(pushedList[2]->GetElapsedTimestampNs());
858 EXPECT_THAT(expected3, EqShellData(actual3));
859
860 ShellData actual4 = readData(fds_datas[1][0]);
861 ShellData expected4;
862 expected4.add_atom()->mutable_plugged_state_changed()->set_state(
863 BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
864 expected4.add_elapsed_timestamp_nanos(pushedList[3]->GetElapsedTimestampNs());
865 EXPECT_THAT(expected4, EqShellData(actual4));
866
867 // Not closing fds_datas[i][0] because this causes writes within ShellSubscriberClient to hang
868 }
869
TEST(ShellSubscriberTest,testPushedSubscriptionRestrictedEvent)870 TEST(ShellSubscriberTest, testPushedSubscriptionRestrictedEvent) {
871 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
872 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
873
874 std::vector<shared_ptr<LogEvent>> pushedList;
875 pushedList.push_back(CreateRestrictedLogEvent(/*atomTag=*/10, /*timestamp=*/1000));
876
877 // create a simple config to get screen events
878 ShellSubscription config;
879 config.add_pushed()->set_atom_id(10);
880
881 // expect empty data
882 vector<ShellData> expectedData;
883
884 // Test with single client
885 TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData,
886 kSingleClient);
887
888 // Test with multiple client
889 TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData, kNumClients);
890 }
891
892 #else
893 GTEST_LOG_(INFO) << "This test does nothing.\n";
894 #endif
895
896 } // namespace statsd
897 } // namespace os
898 } // namespace android
899