1 /*
2  * Copyright (C) 2023, 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 #include <binder/ProcessState.h>
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <gtest_matchers.h>
21 #include <stats_subscription.h>
22 #include <stdint.h>
23 #include <utils/Looper.h>
24 
25 #include <chrono>
26 #include <string>
27 #include <thread>
28 #include <vector>
29 
30 #include "packages/modules/StatsD/statsd/src/shell/shell_config.pb.h"
31 #include "packages/modules/StatsD/statsd/src/shell/shell_data.pb.h"
32 #include "statslog_statsdtest.h"
33 
34 #ifdef __ANDROID__
35 
36 using namespace testing;
37 using android::Looper;
38 using android::ProcessState;
39 using android::sp;
40 using android::os::statsd::Atom;
41 using android::os::statsd::ShellData;
42 using android::os::statsd::ShellSubscription;
43 using android::os::statsd::TestAtomReported_State_OFF;
44 using android::os::statsd::TrainExperimentIds;
45 using android::os::statsd::util::BytesField;
46 using android::os::statsd::util::SCREEN_BRIGHTNESS_CHANGED;
47 using android::os::statsd::util::stats_write;
48 using android::os::statsd::util::TEST_ATOM_REPORTED;
49 using android::os::statsd::util::TEST_ATOM_REPORTED__REPEATED_ENUM_FIELD__OFF;
50 using std::string;
51 using std::vector;
52 using std::this_thread::sleep_for;
53 
54 namespace {
55 
56 class SubscriptionTest : public Test {
57 public:
SubscriptionTest()58     SubscriptionTest() : looper(Looper::prepare(/*opts=*/0)) {
59         const TestInfo* const test_info = UnitTest::GetInstance()->current_test_info();
60         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
61 
62         *trainExpIds.mutable_experiment_id() = {expIds.begin(), expIds.end()};
63         trainExpIds.SerializeToString(&trainExpIdsBytes);
64     }
65 
~SubscriptionTest()66     ~SubscriptionTest() {
67         const TestInfo* const test_info = UnitTest::GetInstance()->current_test_info();
68         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
69     }
70 
71 protected:
SetUp()72     void SetUp() override {
73         // Start the Binder thread pool.
74         ProcessState::self()->startThreadPool();
75     }
76 
TearDown()77     void TearDown() {
78         // Clear any dangling subscriptions from statsd.
79         if (__builtin_available(android __STATSD_SUBS_MIN_API__, *)) {
80             AStatsManager_removeSubscription(subId);
81         }
82     }
83 
LogTestAtomReported(int32_t intFieldValue)84     void LogTestAtomReported(int32_t intFieldValue) __INTRODUCED_IN(__ANDROID_API_T__) {
85         const BytesField bytesField(trainExpIdsBytes.data(), trainExpIdsBytes.size());
86         stats_write(TEST_ATOM_REPORTED, uids.data(), uids.size(), tags, intFieldValue,
87                     /*long_field=*/2LL, /*float_field=*/3.0F,
88                     /*string_field=*/string1.c_str(),
89                     /*boolean_field=*/false,
90                     /*state=*/TEST_ATOM_REPORTED__REPEATED_ENUM_FIELD__OFF, bytesField,
91                     repeatedInts, repeatedLongs, repeatedFloats, repeatedStrings,
92                     &(repeatedBool[0]), /*repeatedBoolSize=*/2, repeatedEnums);
93     }
94 
95     int32_t subId;
96 
97     // TestAtomReported fields.
98     const vector<int32_t> uids = {1};
99     const string tag = "test";
100     const vector<char const*> tags = {tag.c_str()};
101 
102     // 100 int64s for the MODE_BYTES field to push atom size to over 1K.
103     const vector<int64_t> expIds = vector<int64_t>(100, INT64_MAX);
104 
105     const vector<int32_t> repeatedInts{1};
106     const vector<int64_t> repeatedLongs{2LL};
107     const vector<float> repeatedFloats{3.0F};
108     const string string1 = "ABC";
109     const vector<char const*> repeatedStrings = {string1.c_str()};
110     const bool repeatedBool[2] = {false, true};
111     const vector<int32_t> repeatedEnums = {TEST_ATOM_REPORTED__REPEATED_ENUM_FIELD__OFF};
112     TrainExperimentIds trainExpIds;
113     string trainExpIdsBytes;
114 
115 private:
116     sp<Looper> looper;
117 };
118 
119 // Stores arguments passed in subscription callback.
120 struct CallbackData {
121     int32_t subId;
122     AStatsManager_SubscriptionCallbackReason reason;
123     vector<uint8_t> payload;
124     int count;  // Stores number of times the callback is invoked.
125 };
126 
callback(int32_t subscription_id,AStatsManager_SubscriptionCallbackReason reason,uint8_t * _Nonnull payload,size_t num_bytes,void * _Nullable cookie)127 static void callback(int32_t subscription_id, AStatsManager_SubscriptionCallbackReason reason,
128                      uint8_t* _Nonnull payload, size_t num_bytes, void* _Nullable cookie) {
129     CallbackData* data = static_cast<CallbackData*>(cookie);
130     data->subId = subscription_id;
131     data->reason = reason;
132     data->payload.assign(payload, payload + num_bytes);
133     data->count++;
134 }
135 
136 constexpr static int WAIT_MS = 500;
137 
TEST_F(SubscriptionTest,TestSubscription)138 TEST_F(SubscriptionTest, TestSubscription) {
139     if (__builtin_available(android __STATSD_SUBS_MIN_API__, *)) {
140         ShellSubscription config;
141         config.add_pushed()->set_atom_id(TEST_ATOM_REPORTED);
142         config.add_pushed()->set_atom_id(SCREEN_BRIGHTNESS_CHANGED);
143 
144         string configBytes;
145         config.SerializeToString(&configBytes);
146 
147         CallbackData callbackData{/*subId=*/0,
148                                   ASTATSMANAGER_SUBSCRIPTION_CALLBACK_REASON_SUBSCRIPTION_ENDED,
149                                   /*payload=*/{},
150                                   /*count=*/0};
151 
152         // Add subscription.
153         subId = AStatsManager_addSubscription(reinterpret_cast<const uint8_t*>(configBytes.data()),
154                                               configBytes.size(), &callback, &callbackData);
155         ASSERT_GT(subId, 0);
156         sleep_for(std::chrono::milliseconds(WAIT_MS));
157 
158         // Log events without exceeding statsd cache.
159         stats_write(SCREEN_BRIGHTNESS_CHANGED, 100);
160         LogTestAtomReported(1);
161         sleep_for(std::chrono::milliseconds(WAIT_MS));
162 
163         // Verify no callback occurred yet.
164         EXPECT_EQ(callbackData.subId, 0);
165         EXPECT_EQ(callbackData.reason,
166                   ASTATSMANAGER_SUBSCRIPTION_CALLBACK_REASON_SUBSCRIPTION_ENDED);
167         EXPECT_EQ(callbackData.count, 0);
168         ASSERT_TRUE(callbackData.payload.empty());
169 
170         // Log another TestAtomReported to overflow cache.
171         LogTestAtomReported(2);
172         sleep_for(std::chrono::milliseconds(WAIT_MS));
173 
174         // Verify callback occurred.
175         EXPECT_EQ(callbackData.subId, subId);
176         EXPECT_EQ(callbackData.reason, ASTATSMANAGER_SUBSCRIPTION_CALLBACK_REASON_STATSD_INITIATED);
177         EXPECT_EQ(callbackData.count, 1);
178         ASSERT_GT(callbackData.payload.size(), 0);
179 
180         ShellData actualShellData;
181         ASSERT_TRUE(actualShellData.ParseFromArray(callbackData.payload.data(),
182                                                    callbackData.payload.size()));
183 
184         ASSERT_GE(actualShellData.elapsed_timestamp_nanos_size(), 3);
185         EXPECT_THAT(actualShellData.elapsed_timestamp_nanos(), Each(Gt(0LL)));
186 
187         ASSERT_GE(actualShellData.atom_size(), 3);
188 
189         // Verify atom 1.
190         Atom expectedAtom;
191         expectedAtom.mutable_screen_brightness_changed()->set_level(100);
192         EXPECT_THAT(actualShellData.atom(0), EqAtom(expectedAtom));
193 
194         // Verify atom 2.
195         expectedAtom.Clear();
196         auto* testAtomReported = expectedAtom.mutable_test_atom_reported();
197         auto* attributionNode = testAtomReported->add_attribution_node();
198         attributionNode->set_uid(uids[0]);
199         attributionNode->set_tag(tag);
200         testAtomReported->set_int_field(1);
201         testAtomReported->set_long_field(2LL);
202         testAtomReported->set_float_field(3.0F);
203         testAtomReported->set_string_field(string1);
204         testAtomReported->set_boolean_field(false);
205         testAtomReported->set_state(TestAtomReported_State_OFF);
206         *testAtomReported->mutable_bytes_field() = trainExpIds;
207         *testAtomReported->mutable_repeated_int_field() = {repeatedInts.begin(),
208                                                            repeatedInts.end()};
209         *testAtomReported->mutable_repeated_long_field() = {repeatedLongs.begin(),
210                                                             repeatedLongs.end()};
211         *testAtomReported->mutable_repeated_float_field() = {repeatedFloats.begin(),
212                                                              repeatedFloats.end()};
213         *testAtomReported->mutable_repeated_string_field() = {repeatedStrings.begin(),
214                                                               repeatedStrings.end()};
215         *testAtomReported->mutable_repeated_boolean_field() = {&repeatedBool[0],
216                                                                &repeatedBool[0] + 2};
217         *testAtomReported->mutable_repeated_enum_field() = {repeatedEnums.begin(),
218                                                             repeatedEnums.end()};
219         EXPECT_THAT(actualShellData.atom(1), EqAtom(expectedAtom));
220 
221         // Verify atom 3.
222         testAtomReported->set_int_field(2);
223         EXPECT_THAT(actualShellData.atom(2), EqAtom(expectedAtom));
224 
225         // Log another ScreenBrightnessChanged atom. No callback should occur.
226         stats_write(SCREEN_BRIGHTNESS_CHANGED, 99);
227         sleep_for(std::chrono::milliseconds(WAIT_MS));
228         EXPECT_EQ(callbackData.count, 1);
229 
230         // Flush subscription. Callback should occur.
231         AStatsManager_flushSubscription(subId);
232         sleep_for(std::chrono::milliseconds(WAIT_MS));
233 
234         EXPECT_EQ(callbackData.subId, subId);
235         EXPECT_EQ(callbackData.reason, ASTATSMANAGER_SUBSCRIPTION_CALLBACK_REASON_FLUSH_REQUESTED);
236         EXPECT_EQ(callbackData.count, 2);
237         ASSERT_GT(callbackData.payload.size(), 0);
238 
239         ASSERT_TRUE(actualShellData.ParseFromArray(callbackData.payload.data(),
240                                                    callbackData.payload.size()));
241 
242         ASSERT_GE(actualShellData.elapsed_timestamp_nanos_size(), 1);
243         EXPECT_THAT(actualShellData.elapsed_timestamp_nanos(), Each(Gt(0LL)));
244 
245         ASSERT_GE(actualShellData.atom_size(), 1);
246 
247         // Verify atom 1.
248         expectedAtom.Clear();
249         expectedAtom.mutable_screen_brightness_changed()->set_level(99);
250         EXPECT_THAT(actualShellData.atom(0), EqAtom(expectedAtom));
251 
252         // Log another ScreenBrightnessChanged atom. No callback should occur.
253         stats_write(SCREEN_BRIGHTNESS_CHANGED, 98);
254         sleep_for(std::chrono::milliseconds(WAIT_MS));
255         EXPECT_EQ(callbackData.count, 2);
256 
257         // Trigger callback through cache timeout.
258         // Two 500 ms sleeps have occurred already so the total sleep is 71000 ms since last
259         // callback invocation.
260         sleep_for(std::chrono::milliseconds(70'000));
261         EXPECT_EQ(callbackData.subId, subId);
262         EXPECT_EQ(callbackData.reason, ASTATSMANAGER_SUBSCRIPTION_CALLBACK_REASON_STATSD_INITIATED);
263         EXPECT_EQ(callbackData.count, 3);
264         ASSERT_GT(callbackData.payload.size(), 0);
265 
266         ASSERT_TRUE(actualShellData.ParseFromArray(callbackData.payload.data(),
267                                                    callbackData.payload.size()));
268 
269         ASSERT_GE(actualShellData.elapsed_timestamp_nanos_size(), 1);
270         EXPECT_THAT(actualShellData.elapsed_timestamp_nanos(), Each(Gt(0LL)));
271 
272         ASSERT_GE(actualShellData.atom_size(), 1);
273 
274         // Verify atom 1.
275         expectedAtom.Clear();
276         expectedAtom.mutable_screen_brightness_changed()->set_level(98);
277         EXPECT_THAT(actualShellData.atom(0), EqAtom(expectedAtom));
278 
279         // Log another ScreenBrightnessChanged atom. No callback should occur.
280         stats_write(SCREEN_BRIGHTNESS_CHANGED, 97);
281         sleep_for(std::chrono::milliseconds(WAIT_MS));
282         EXPECT_EQ(callbackData.count, 3);
283 
284         // End subscription. Final callback should occur.
285         AStatsManager_removeSubscription(subId);
286         sleep_for(std::chrono::milliseconds(WAIT_MS));
287 
288         EXPECT_EQ(callbackData.subId, subId);
289         EXPECT_EQ(callbackData.reason,
290                   ASTATSMANAGER_SUBSCRIPTION_CALLBACK_REASON_SUBSCRIPTION_ENDED);
291         EXPECT_EQ(callbackData.count, 4);
292         ASSERT_GT(callbackData.payload.size(), 0);
293 
294         ASSERT_TRUE(actualShellData.ParseFromArray(callbackData.payload.data(),
295                                                    callbackData.payload.size()));
296 
297         ASSERT_GE(actualShellData.elapsed_timestamp_nanos_size(), 1);
298         EXPECT_THAT(actualShellData.elapsed_timestamp_nanos(), Each(Gt(0LL)));
299 
300         ASSERT_GE(actualShellData.atom_size(), 1);
301 
302         // Verify atom 1.
303         expectedAtom.Clear();
304         expectedAtom.mutable_screen_brightness_changed()->set_level(97);
305         EXPECT_THAT(actualShellData.atom(0), EqAtom(expectedAtom));
306     } else {
307         GTEST_SKIP();
308     }
309 }
310 
311 }  // namespace
312 
313 #else
314 GTEST_LOG_(INFO) << "This test does nothing.\n";
315 #endif
316