1 /*
2  * Copyright (C) 2016 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 <general_test/timer_set_test.h>
18 
19 #include <cinttypes>
20 #include <cstddef>
21 #include <new>
22 
23 #include <shared/send_message.h>
24 #include <shared/time_util.h>
25 
26 #include <chre.h>
27 
28 using nanoapp_testing::kOneMillisecondInNanoseconds;
29 using nanoapp_testing::kOneSecondInNanoseconds;
30 using nanoapp_testing::sendFatalFailureToHost;
31 using nanoapp_testing::sendInternalFailureToHost;
32 using nanoapp_testing::sendSuccessToHost;
33 
34 /*
35  * We have various "stages" for different timer setups we want to test.
36  * To speed up the test, we run all our stages simultaneously.  That
37  * requires having 6 timers available, but with a 32 timer minimum
38  * and a presumption that tests aren't going to be run alongside a lot
39  * of other nanoapps, this should be fine.
40  *
41  * See initStages() for the description of each stage.  Since these all
42  * happen in parallel, we leave it to each stage to mark itself has having
43  * succeeded, and have markSuccess() tell the Host when all stages have
44  * reported in.
45  *
46  * Note that we intentionally place the one-shot timers first, to give
47  * us more time to notice them (incorrectly) firing multiple times.
48  */
49 
50 static uint64_t kShortDuration = 10 * kOneMillisecondInNanoseconds;
51 static uint64_t kLongDuration = kOneSecondInNanoseconds;
52 
53 namespace general_test {
54 
Stage(uint32_t stage,uint64_t duration,const void * cookie,bool oneShot)55 TimerSetTest::Stage::Stage(uint32_t stage, uint64_t duration,
56                            const void *cookie, bool oneShot)
57     : mSetTime(0), mDuration(duration), mStage(stage), mEventCount(0),
58       mCookie(cookie), mOneShot(oneShot) {}
59 
start()60 void TimerSetTest::Stage::start() {
61   mSetTime = chreGetTime();
62   mTimerHandle = chreTimerSet(mDuration, mCookie, mOneShot);
63   if (mTimerHandle == CHRE_TIMER_INVALID) {
64     sendFatalFailureToHost("Unable to set timer ", &mStage);
65   }
66   if (mSetTime == 0) {
67     sendFatalFailureToHost("chreGetTime() gave 0");
68   }
69 }
70 
processEvent(uint64_t timestamp,TimerSetTest * test)71 void TimerSetTest::Stage::processEvent(uint64_t timestamp, TimerSetTest *test) {
72   if (mSetTime == 0) {
73     sendInternalFailureToHost("Didn't initialize mSetTime");
74   }
75   mEventCount++;
76 
77   uint64_t expectedTime = mSetTime + (mEventCount * mDuration);
78   if (timestamp < expectedTime) {
79     sendFatalFailureToHost("Timer triggered too soon ", &mStage);
80   }
81   // TODO(b/32179037): Make this check much stricter.
82   if (timestamp > (expectedTime + kOneSecondInNanoseconds)) {
83     sendFatalFailureToHost("Timer triggered over a second late ", &mStage);
84   }
85 
86   if (mOneShot) {
87     if (mEventCount > 1) {
88       sendFatalFailureToHost("One shot timer called multiple times ",
89                              &mStage);
90     } else {
91       test->markSuccess(mStage);
92     }
93   } else if (mEventCount == 3) {
94     // We mark recurring timers as successful on their third firing, if we
95     // can cancel it.
96     if (chreTimerCancel(mTimerHandle)) {
97       test->markSuccess(mStage);
98     } else {
99       sendFatalFailureToHost("Could not cancel recurring timer", &mStage);
100     }
101   }
102 }
103 
initStages()104 void TimerSetTest::initStages() {
105   // To avoid fragmentation, we do one large allocation, and use
106   // placement new to initialize it.
107   mStages = static_cast<Stage*>(chreHeapAlloc(sizeof(*mStages) *
108                                               kStageCount));
109   if (mStages == nullptr) {
110     sendFatalFailureToHost("Insufficient heap");
111   }
112 
113 #define COOKIE(num) reinterpret_cast<const void*>(num)
114 
115   // Stage 0: Test NULL cookie
116   new(&mStages[0]) Stage(0, kShortDuration, nullptr, true);
117   // Stage 1: Test (void*)-1 cookie
118   new(&mStages[1]) Stage(1, kShortDuration, COOKIE(-1), true);
119   // Stage 2: Test one shot with short duration
120   new(&mStages[2]) Stage(2, kShortDuration, COOKIE(2), true);
121   // Stage 3: Test one shot with long duration
122   new(&mStages[3]) Stage(3, kLongDuration,  COOKIE(3), true);
123   // Stage 4: Test recurring with long duration
124   new(&mStages[4]) Stage(4, kLongDuration,  COOKIE(4), false);
125   // Stage 5: Test recurring with short duration
126   new(&mStages[5]) Stage(5, kShortDuration, COOKIE(5), false);
127   static_assert((5 + 1) == kStageCount, "Missized array");
128 
129 #undef COOKIE
130 }
131 
TimerSetTest()132 TimerSetTest::TimerSetTest()
133   : Test(CHRE_API_VERSION_1_0), mInMethod(false), mFinishedBitmask(0) {
134 }
135 
setUp(uint32_t messageSize,const void *)136 void TimerSetTest::setUp(uint32_t messageSize, const void * /* message */) {
137   mInMethod = true;
138 
139   if (messageSize != 0) {
140     sendFatalFailureToHost(
141         "TimerSet message expects 0 additional bytes, got ",
142         &messageSize);
143   }
144 
145   initStages();
146   for (size_t i = 0; i < kStageCount; i++) {
147     mStages[i].start();
148   }
149 
150   mInMethod = false;
151 }
152 
~TimerSetTest()153 TimerSetTest::~TimerSetTest() {
154   chreHeapFree(mStages);
155 }
156 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)157 void TimerSetTest::handleEvent(uint32_t senderInstanceId,
158                                uint16_t eventType, const void* eventData) {
159   uint64_t timestamp = chreGetTime();
160   if (mInMethod) {
161     sendFatalFailureToHost("handleEvent invoked while another nanoapp "
162                            "method is running");
163   }
164   mInMethod = true;
165   if (senderInstanceId != CHRE_INSTANCE_ID) {
166     sendFatalFailureToHost("handleEvent got event from unexpected sender:",
167                            &senderInstanceId);
168   }
169   if (eventType != CHRE_EVENT_TIMER) {
170     unexpectedEvent(eventType);
171   }
172   Stage *stage = getStageFromCookie(eventData);
173   if (stage == nullptr) {
174     sendFatalFailureToHost("handleEvent got invalid eventData");
175   }
176   stage->processEvent(timestamp, this);
177 
178   mInMethod = false;
179 }
180 
markSuccess(uint32_t stage)181 void TimerSetTest::markSuccess(uint32_t stage) {
182   chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
183   uint32_t finishedBit = (1 << stage);
184   if ((kAllFinished & finishedBit) == 0) {
185     sendFatalFailureToHost("markSuccess bad stage", &stage);
186   }
187   mFinishedBitmask |= finishedBit;
188   if (mFinishedBitmask == kAllFinished) {
189     sendSuccessToHost();
190   }
191 }
192 
getStageFromCookie(const void * cookie)193 TimerSetTest::Stage *TimerSetTest::getStageFromCookie(const void *cookie) {
194   Stage *ret = nullptr;
195   for (size_t i = 0; i < kStageCount; i++) {
196     if (mStages[i].getCookie() == cookie) {
197       if (ret != nullptr) {
198         sendInternalFailureToHost("Multiple stages with the same "
199                                   "cookie");
200       }
201       ret = &mStages[i];
202       // It's cheap enough to go through the whole array, and will
203       // catch if we screw up this test setup by duplicating a cookie.
204     }
205   }
206   return ret;
207 }
208 
209 }  // namespace general_test
210