• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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