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