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 <gtest/gtest.h>
16
17 #include <unistd.h>
18 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
19 #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
20 #include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
21 #include "src/shell/ShellSubscriber.h"
22 #include "tests/metrics/metrics_test_helper.h"
23
24 #include <stdio.h>
25 #include <vector>
26
27 using namespace android::os::statsd;
28 using android::sp;
29 using std::vector;
30 using testing::_;
31 using testing::Invoke;
32 using testing::NaggyMock;
33 using testing::StrictMock;
34
35 #ifdef __ANDROID__
36
37 class MyResultReceiver : public BnResultReceiver {
38 public:
39 Mutex mMutex;
40 Condition mCondition;
41 bool mHaveResult = false;
42 int32_t mResult = 0;
43
send(int32_t resultCode)44 virtual void send(int32_t resultCode) {
45 AutoMutex _l(mMutex);
46 mResult = resultCode;
47 mHaveResult = true;
48 mCondition.signal();
49 }
50
waitForResult()51 int32_t waitForResult() {
52 AutoMutex _l(mMutex);
53 mCondition.waitRelative(mMutex, 1000000000);
54 return mResult;
55 }
56 };
57
runShellTest(ShellSubscription config,sp<MockUidMap> uidMap,sp<MockStatsPullerManager> pullerManager,const vector<std::shared_ptr<LogEvent>> & pushedEvents,const ShellData & expectedData)58 void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
59 sp<MockStatsPullerManager> pullerManager,
60 const vector<std::shared_ptr<LogEvent>>& pushedEvents,
61 const ShellData& expectedData) {
62 // set up 2 pipes for read/write config and data
63 int fds_config[2];
64 ASSERT_EQ(0, pipe(fds_config));
65
66 int fds_data[2];
67 ASSERT_EQ(0, pipe(fds_data));
68
69 size_t bufferSize = config.ByteSize();
70
71 // write the config to pipe, first write size of the config
72 vector<uint8_t> size_buffer(sizeof(bufferSize));
73 std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize));
74 write(fds_config[1], &bufferSize, sizeof(bufferSize));
75 // then write config itself
76 vector<uint8_t> buffer(bufferSize);
77 config.SerializeToArray(&buffer[0], bufferSize);
78 write(fds_config[1], buffer.data(), bufferSize);
79 close(fds_config[1]);
80
81 sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
82 sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
83
84 // mimic a binder thread that a shell subscriber runs on. it would block.
85 std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
86 shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
87 });
88 reader.detach();
89
90 // let the shell subscriber to receive the config from pipe.
91 std::this_thread::sleep_for(100ms);
92
93 if (pushedEvents.size() > 0) {
94 // send a log event that matches the config.
95 std::thread log_reader([&shellClient, &pushedEvents] {
96 for (const auto& event : pushedEvents) {
97 shellClient->onLogEvent(*event);
98 }
99 });
100
101 log_reader.detach();
102
103 if (log_reader.joinable()) {
104 log_reader.join();
105 }
106 }
107
108 // wait for the data to be written.
109 std::this_thread::sleep_for(100ms);
110
111 int expected_data_size = expectedData.ByteSize();
112
113 // now read from the pipe. firstly read the atom size.
114 size_t dataSize = 0;
115 EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize)));
116 EXPECT_EQ(expected_data_size, (int)dataSize);
117
118 // then read that much data which is the atom in proto binary format
119 vector<uint8_t> dataBuffer(dataSize);
120 EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
121
122 // make sure the received bytes can be parsed to an atom
123 ShellData receivedAtom;
124 EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
125
126 // serialze the expected atom to bytes. and compare. to make sure they are the same.
127 vector<uint8_t> atomBuffer(expected_data_size);
128 expectedData.SerializeToArray(&atomBuffer[0], expected_data_size);
129 EXPECT_EQ(atomBuffer, dataBuffer);
130 close(fds_data[0]);
131 }
132
TEST(ShellSubscriberTest,testPushedSubscription)133 TEST(ShellSubscriberTest, testPushedSubscription) {
134 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
135
136 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
137 vector<std::shared_ptr<LogEvent>> pushedList;
138
139 std::shared_ptr<LogEvent> event1 =
140 std::make_shared<LogEvent>(29 /*screen_state_atom_id*/, 1000 /*timestamp*/);
141 event1->write(::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
142 event1->init();
143 pushedList.push_back(event1);
144
145 // create a simple config to get screen events
146 ShellSubscription config;
147 config.add_pushed()->set_atom_id(29);
148
149 // this is the expected screen event atom.
150 ShellData shellData;
151 shellData.add_atom()->mutable_screen_state_changed()->set_state(
152 ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
153
154 runShellTest(config, uidMap, pullerManager, pushedList, shellData);
155 }
156
157 namespace {
158
159 int kUid1 = 1000;
160 int kUid2 = 2000;
161
162 int kCpuTime1 = 100;
163 int kCpuTime2 = 200;
164
getExpectedShellData()165 ShellData getExpectedShellData() {
166 ShellData shellData;
167 auto* atom1 = shellData.add_atom()->mutable_cpu_active_time();
168 atom1->set_uid(kUid1);
169 atom1->set_time_millis(kCpuTime1);
170
171 auto* atom2 = shellData.add_atom()->mutable_cpu_active_time();
172 atom2->set_uid(kUid2);
173 atom2->set_time_millis(kCpuTime2);
174
175 return shellData;
176 }
177
getPulledConfig()178 ShellSubscription getPulledConfig() {
179 ShellSubscription config;
180 auto* pull_config = config.add_pulled();
181 pull_config->mutable_matcher()->set_atom_id(10016);
182 pull_config->set_freq_millis(2000);
183 return config;
184 }
185
186 } // namespace
187
TEST(ShellSubscriberTest,testPulledSubscription)188 TEST(ShellSubscriberTest, testPulledSubscription) {
189 sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
190
191 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
192 EXPECT_CALL(*pullerManager, Pull(10016, _))
193 .WillRepeatedly(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
194 data->clear();
195 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, 1111L);
196 event->write(kUid1);
197 event->write(kCpuTime1);
198 event->init();
199 data->push_back(event);
200 // another event
201 event = make_shared<LogEvent>(tagId, 1111L);
202 event->write(kUid2);
203 event->write(kCpuTime2);
204 event->init();
205 data->push_back(event);
206 return true;
207 }));
208
209 runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
210 getExpectedShellData());
211 }
212
213 #else
214 GTEST_LOG_(INFO) << "This test does nothing.\n";
215 #endif
216