/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include // max_align_t #include #include // placement new #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using nanoapp_testing::AbortBlame; using nanoapp_testing::MessageType; using nanoapp_testing::sendFatalFailureToHost; using nanoapp_testing::sendInternalFailureToHost; namespace general_test { // The size of this array is checked at compile time by the static_assert // in getNew(). alignas(alignof(max_align_t)) static uint8_t gGetNewBackingMemory[128]; template static N *getNew() { // We intentionally avoid using chreHeapAlloc() to reduce dependencies // for our tests, especially things like HelloWorld. This obviously // cannot be called more than once, but our usage doesn't require it. static_assert(sizeof(gGetNewBackingMemory) >= sizeof(N), "getNew() backing memory is undersized"); return new(gGetNewBackingMemory) N(); } // TODO(b/32114261): Remove this variable. bool gUseNycMessageHack = true; class App { public: App() : mConstructionCookie(kConstructed), mCurrentTest(nullptr) {} ~App() { // Yes, it's very odd to actively set a value in our destructor. // However, since we're making a static instance of this class, // the space for this class will stick around (unlike heap memory // which might get reused), so we can still use this to perform // some testing. mConstructionCookie = kDestructed; } bool wasConstructed() const { return mConstructionCookie == kConstructed; } bool wasDestructed() const { return mConstructionCookie == kDestructed; } void handleEvent(uint32_t senderInstanceId, uint16_t eventType, const void *eventData); void createTest(const void *eventData); void freeTest(); private: uint32_t mConstructionCookie; Test *mCurrentTest; static constexpr uint32_t kConstructed = UINT32_C(0x51501984); static constexpr uint32_t kDestructed = UINT32_C(0x19845150); // TODO(b/32114261): Remove this method. chreMessageFromHostData adjustHostMessageForNYC(const chreMessageFromHostData *data); }; // In the NYC version of the CHRE, the "reservedMessageType" isn't // assured to be sent correctly from the host. But we want our // tests to be written using this field (it's cleaner). So in NYC // the host prefixes this value in the first four bytes of 'message', // and here we reconstruct the message to be correct. // TODO(b/32114261): Remove this method. chreMessageFromHostData App::adjustHostMessageForNYC(const chreMessageFromHostData *data) { if (!gUseNycMessageHack) { return *data; } chreMessageFromHostData ret; if (data->messageSize < sizeof(uint32_t)) { sendFatalFailureToHost("Undersized message in adjustHostMessageForNYC"); } const uint8_t *messageBytes = static_cast(data->message); nanoapp_testing::memcpy(&(ret.reservedMessageType), messageBytes, sizeof(ret.reservedMessageType)); ret.reservedMessageType = nanoapp_testing::littleEndianToHost(ret.reservedMessageType); ret.messageSize = data->messageSize - sizeof(ret.reservedMessageType); ret.message = messageBytes + sizeof(ret.reservedMessageType); return ret; } void App::handleEvent(uint32_t senderInstanceId, uint16_t eventType, const void* eventData) { // TODO: When we get an API that fixes the reservedMessageType, // then we should only use our adjustedData hack on APIs // prior to it being fixed. Eventually, we should remove // this altogether. chreMessageFromHostData adjustedData; if (eventType == CHRE_EVENT_MESSAGE_FROM_HOST) { auto data = static_cast(eventData); adjustedData = adjustHostMessageForNYC(data); eventData = &adjustedData; } if (mCurrentTest != nullptr) { // Our test is in progress, so let it take control. mCurrentTest->testHandleEvent(senderInstanceId, eventType, eventData); return; } // No test in progress, so we expect this message to be the host telling // us which test to run. We fail if it's anything else. if (eventType != CHRE_EVENT_MESSAGE_FROM_HOST) { uint32_t localEventType = eventType; sendFatalFailureToHost( "Unexpected event type with no established test:", &localEventType); } if (senderInstanceId != CHRE_INSTANCE_ID) { sendFatalFailureToHost( "Got MESSAGE_FROM_HOST not from CHRE_INSTANCE_ID:", &senderInstanceId); } createTest(eventData); } void App::createTest(const void *eventData) { if (mCurrentTest != nullptr) { sendInternalFailureToHost( "Got to createTest() with non-null mCurrentTest"); } auto data = static_cast(eventData); switch (static_cast(data->reservedMessageType)) { using namespace general_test; #define CASE(testName, className) \ case TestNames::testName: \ mCurrentTest = getNew(); \ break; CASE(kHelloWorld, HelloWorldTest); CASE(kSimpleHeapAlloc, SimpleHeapAllocTest); CASE(kHeapAllocStress, HeapAllocStressTest); CASE(kGetTime, GetTimeTest); CASE(kEventBetweenApps0, EventBetweenApps0); CASE(kEventBetweenApps1, EventBetweenApps1); CASE(kSendEvent, SendEventTest); CASE(kBasicAccelerometer, BasicAccelerometerTest); CASE(kBasicInstantMotionDetect, BasicInstantMotionDetectTest); CASE(kBasicStationaryDetect, BasicStationaryDetectTest); CASE(kBasicGyroscope, BasicGyroscopeTest); CASE(kBasicMagnetometer, BasicMagnetometerTest); CASE(kBasicBarometer, BasicBarometerTest); CASE(kBasicLightSensor, BasicLightSensorTest); CASE(kBasicProximity, BasicProximityTest); CASE(kVersionSanity, VersionSanityTest); CASE(kLoggingSanity, LoggingSanityTest); CASE(kSendMessageToHost, SendMessageToHostTest); CASE(kTimerSet, TimerSetTest); CASE(kTimerCancel, TimerCancelTest); CASE(kTimerStress, TimerStressTest); CASE(kSendEventStress, SendEventStressTest); CASE(kHeapExhaustionStability, HeapExhaustionStabilityTest); CASE(kGnssCapabilities, GnssCapabilitiesTest); CASE(kWifiCapabilities, WifiCapabilitiesTest); CASE(kWwanCapabilities, WwanCapabilitiesTest); CASE(kSensorInfo, SensorInfoTest); CASE(kWwanCellInfoTest, WwanCellInfoTest); CASE(kEstimatedHostTime, EstimatedHostTimeTest); CASE(kNanoappInfoByAppId, NanoappInfoByAppIdTest); CASE(kNanoappInfoByInstanceId, NanoappInfoByInstanceIdTest); CASE(kNanoAppInfoEventsPerformer, NanoAppInfoEventsTestPerformer); CASE(kNanoAppInfoEventsObserver, NanoAppInfoEventsTestObserver); CASE(kBasicAudioTest, BasicAudioTest); CASE(kHostAwakeSuspend, HostAwakeSuspendTest); CASE(kBasicGnssTest, BasicGnssTest); CASE(kBasicWifiTest, BasicWifiTest); #undef CASE default: sendFatalFailureToHost("Unexpected message type:", &(data->reservedMessageType)); } if (mCurrentTest != nullptr) { mCurrentTest->testSetUp(data->messageSize, data->message); } else { sendInternalFailureToHost("createTest() ended with null mCurrentTest"); } } void App::freeTest() { if (mCurrentTest == nullptr) { sendInternalFailureToHost("Nanoapp unloading without running any test"); } mCurrentTest->~Test(); } } // namespace general_test static general_test::App gApp; extern "C" void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType, const void* eventData) { gApp.handleEvent(senderInstanceId, eventType, eventData); } static uint32_t zeroedBytes[13]; extern "C" bool nanoappStart(void) { // zeroedBytes is in the BSS and needs to be zero'd out. for (size_t i = 0; i < sizeof(zeroedBytes)/sizeof(zeroedBytes[0]); i++) { if (zeroedBytes[i] != 0) { return false; } } // A CHRE is required to call the constructor of our class prior to // reaching this point. return gApp.wasConstructed(); } extern "C" void nanoappEnd(void) { if (gApp.wasDestructed()) { // It's not legal for us to send a message from here. The best // we can do is abort, although there's no means for the end user // to see such a failure. // TODO: Figure out how to have this failure noticed. nanoapp_testing::abort(AbortBlame::kChreInNanoappEnd); } gApp.freeTest(); // TODO: Unclear how we can test the global destructor being called, // but that would be good to test. Since it's supposed to happen // after this call completes, it's difficult to test. }