Merge "Move libprotocan to hardware/interfaces/automotive domain"

This commit is contained in:
TreeHugger Robot
2022-01-04 21:15:22 +00:00
committed by Android (Google) Code Review
13 changed files with 933 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
//
// Copyright (C) 2020 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.
//
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_library_static {
name: "libprotocan",
defaults: ["android.hardware.automotive.vehicle@2.0-protocan-defaults"],
vendor: true,
srcs: [
"Checksum.cpp",
"MessageCounter.cpp",
"MessageDef.cpp",
"MessageInjector.cpp",
"Signal.cpp",
],
export_include_dirs: ["include"],
shared_libs: [
"android.hardware.automotive.can@1.0",
],
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 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 <libprotocan/Checksum.h>
namespace android::hardware::automotive::protocan {
Checksum::Checksum(Signal signal, formula f) : mSignal(signal), mFormula(f) {}
void Checksum::update(can::V1_0::CanMessage& msg) const {
mSignal.set(msg, 0);
mSignal.set(msg, mFormula(msg) % (mSignal.maxValue + 1));
}
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2020 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 <libprotocan/MessageCounter.h>
#include <android-base/logging.h>
namespace android::hardware::automotive::protocan {
/** Whether to log counter state messages. */
static constexpr bool kSuperVerbose = false;
MessageCounter::MessageCounter(Signal signal) : upperBound(signal.maxValue + 1), mSignal(signal) {}
Signal::value MessageCounter::next() const {
CHECK(mCurrent.has_value()) << "Counter not initialized. Did you call isReady?";
return (*mCurrent + 1) % upperBound;
}
void MessageCounter::read(const can::V1_0::CanMessage& msg) {
auto val = mSignal.get(msg);
if (!mCurrent.has_value()) {
LOG(VERBOSE) << "Got first counter val of " << val;
mCurrent = val;
return;
}
auto nextVal = next();
if (nextVal == val) {
if constexpr (kSuperVerbose) {
LOG(VERBOSE) << "Got next counter val of " << nextVal;
}
mCurrent = nextVal;
} else {
LOG(DEBUG) << "Ignoring next counter val of " << val << ", waiting for " << nextVal;
}
}
bool MessageCounter::isReady() const { return mCurrent.has_value(); }
void MessageCounter::increment(can::V1_0::CanMessage& msg) {
auto newVal = next();
mCurrent = newVal;
mSignal.set(msg, newVal);
}
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2020 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 <libprotocan/MessageDef.h>
#include <android-base/logging.h>
namespace android::hardware::automotive::protocan {
using can::V1_0::CanMessage;
using can::V1_0::CanMessageId;
MessageDef::MessageDef(CanMessageId id, uint16_t len, std::map<std::string, Signal> signals,
std::optional<Signal> counter, std::optional<Checksum> checksum)
: id(id), kLen(len), kSignals(std::move(signals)), kCounter(counter), kChecksum(checksum) {}
const Signal& MessageDef::operator[](const std::string& signalName) const {
auto it = kSignals.find(signalName);
CHECK(it != kSignals.end()) << "Signal " << signalName << " doesn't exist";
return it->second;
}
CanMessage MessageDef::makeDefault() const {
CanMessage msg = {};
msg.id = id;
msg.payload.resize(kLen);
for (auto const& [name, signal] : kSignals) {
signal.setDefault(msg);
}
return msg;
}
MessageCounter MessageDef::makeCounter() const {
CHECK(kCounter.has_value()) << "Can't build a counter for message without such signal";
return MessageCounter(*kCounter);
}
void MessageDef::updateChecksum(can::V1_0::CanMessage& msg) const {
if (!kChecksum.has_value()) return;
kChecksum->update(msg);
}
bool MessageDef::validate(const can::V1_0::CanMessage& msg) const {
return msg.payload.size() >= kLen;
}
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2020 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 <libprotocan/MessageInjector.h>
#include <android-base/logging.h>
#include <thread>
namespace android::hardware::automotive::protocan {
/** Whether to log injected messages. */
static constexpr bool kSuperVerbose = true;
using namespace std::literals::chrono_literals;
using can::V1_0::CanMessage;
using can::V1_0::CanMessageId;
using can::V1_0::ICanBus;
using can::V1_0::Result;
MessageInjector::MessageInjector(MessageDef msgDef,
std::optional<std::chrono::milliseconds> interMessageDelay)
: kMsgDef(std::move(msgDef)),
kInterMessageDelay(interMessageDelay),
mCounter(msgDef.makeCounter()) {}
void MessageInjector::inject(const CanMessage& msg) { inject({msg}); }
void MessageInjector::inject(const std::initializer_list<can::V1_0::CanMessage> msgs) {
std::lock_guard<std::mutex> lock(mMessagesGuard);
for (const auto& msg : msgs) {
if constexpr (kSuperVerbose) {
LOG(VERBOSE) << "Message scheduled for injection: " << toString(msg);
}
mMessages.push(msg);
}
}
void MessageInjector::processQueueLocked(can::V1_0::ICanBus& bus) {
if (mMessages.empty() || !mCounter.isReady()) return;
auto paddingMessagesCount = mCounter.upperBound - (mMessages.size() % mCounter.upperBound);
auto padMessage = kMsgDef.makeDefault();
for (unsigned i = 0; i < paddingMessagesCount; i++) {
mMessages.push(padMessage);
}
while (!mMessages.empty()) {
auto&& outMsg = mMessages.front();
mCounter.increment(outMsg);
kMsgDef.updateChecksum(outMsg);
if constexpr (kSuperVerbose) {
LOG(VERBOSE) << "Injecting message: " << toString(outMsg);
}
auto result = bus.send(outMsg);
if (result != Result::OK) {
LOG(ERROR) << "Message injection failed: " << toString(result);
}
mMessages.pop();
// This would block onReceive, but the class is not supposed to be used in production anyway
// (see MessageInjector docstring).
if (kInterMessageDelay.has_value()) {
std::this_thread::sleep_for(*kInterMessageDelay);
}
}
}
void MessageInjector::onReceive(ICanBus& bus, const CanMessage& msg) {
if (!kMsgDef.validate(msg)) return;
std::lock_guard<std::mutex> lock(mMessagesGuard);
mCounter.read(msg);
processQueueLocked(bus);
}
MessageInjectorManager::MessageInjectorManager(
std::initializer_list<std::shared_ptr<MessageInjector>> injectors) {
std::transform(injectors.begin(), injectors.end(), std::inserter(mInjectors, mInjectors.end()),
[](const std::shared_ptr<MessageInjector>& injector) {
return std::make_pair(injector->kMsgDef.id, std::move(injector));
});
}
void MessageInjectorManager::onReceive(sp<ICanBus> bus, const CanMessage& msg) {
auto it = mInjectors.find(msg.id);
if (it == mInjectors.end()) return;
it->second->onReceive(*bus, msg);
}
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2020 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 <libprotocan/Signal.h>
#include <android-base/logging.h>
namespace android::hardware::automotive::protocan {
static uint8_t calculateLastByteMask(uint16_t start, uint8_t length) {
unsigned lastByteBits = (start + length) % 8;
unsigned lastBytePadding = (8 - lastByteBits) % 8;
return 0xFF >> lastBytePadding;
}
static uint8_t calculateFirstByteMask(uint16_t firstByte, uint8_t firstBit, uint16_t lastByte,
uint8_t lastMask) {
uint8_t firstMask = 0xFF << firstBit;
if (firstByte == lastByte) firstMask &= lastMask;
return firstMask;
}
Signal::Signal(uint16_t start, uint8_t length, value defVal)
: maxValue((1u << length) - 1),
kFirstByte(start / 8),
kFirstBit(start % 8),
kFirstByteBits(8 - kFirstBit),
kLastByte((start + length - 1) / 8),
kLastMask(calculateLastByteMask(start, length)),
kFirstMask(calculateFirstByteMask(kFirstByte, kFirstBit, kLastByte, kLastMask)),
kDefVal(defVal) {
CHECK(length > 0) << "Signal length must not be zero";
}
Signal::value Signal::get(const can::V1_0::CanMessage& msg) const {
CHECK(msg.payload.size() > kLastByte)
<< "Message is too short. Did you call MessageDef::validate?";
Signal::value v = 0;
if (kLastByte != kFirstByte) v = kLastMask & msg.payload[kLastByte];
for (int i = kLastByte - 1; i > kFirstByte; i--) {
v = (v << 8) | msg.payload[i];
}
return (v << kFirstByteBits) | ((msg.payload[kFirstByte] & kFirstMask) >> kFirstBit);
}
void Signal::set(can::V1_0::CanMessage& msg, Signal::value val) const {
CHECK(msg.payload.size() > kLastByte)
<< "Signal requires message of length " << (kLastByte + 1)
<< " which is beyond message length of " << msg.payload.size();
uint8_t firstByte = val << kFirstBit;
val >>= kFirstByteBits;
msg.payload[kFirstByte] = (msg.payload[kFirstByte] & ~kFirstMask) | (firstByte & kFirstMask);
for (int i = kFirstByte + 1; i < kLastByte; i++) {
msg.payload[i] = val & 0xFF;
val >>= 8;
}
if (kLastByte != kFirstByte) {
msg.payload[kLastByte] = (msg.payload[kLastByte] & ~kLastMask) | (val & kLastMask);
}
}
void Signal::setDefault(can::V1_0::CanMessage& msg) const { set(msg, kDefVal); }
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <android/hardware/automotive/can/1.0/types.h>
#include <libprotocan/Signal.h>
namespace android::hardware::automotive::protocan {
class Checksum {
public:
using formula = std::function<Signal::value(const can::V1_0::CanMessage&)>;
Checksum(Signal signal, formula f);
void update(can::V1_0::CanMessage& msg) const;
private:
const Signal mSignal;
const formula mFormula;
};
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <android/hardware/automotive/can/1.0/types.h>
#include <libprotocan/Signal.h>
namespace android::hardware::automotive::protocan {
class MessageCounter {
public:
const Signal::value upperBound;
MessageCounter(Signal signal);
/**
* Parse CAN message sent by external ECU to determine current counter value.
*/
void read(const can::V1_0::CanMessage& msg);
/**
* States whether current counter value is determined.
*/
bool isReady() const;
/**
* Increment current counter value and set it in a new message.
*
* Caller must check isReady() at least once before calling this method.
*/
void increment(can::V1_0::CanMessage& msg);
private:
const Signal mSignal;
std::optional<Signal::value> mCurrent = std::nullopt;
Signal::value next() const;
};
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <android/hardware/automotive/can/1.0/types.h>
#include <libprotocan/Checksum.h>
#include <libprotocan/MessageCounter.h>
#include <libprotocan/Signal.h>
namespace android::hardware::automotive::protocan {
/**
* CAN message definition (not the actual message data).
*
* Describes static message properties (message ID, signals etc).
*/
class MessageDef {
public:
const can::V1_0::CanMessageId id;
/**
* Create message definition.
*
* Currently only constant length messages are supported.
*
* \param id CAN message ID
* \param len CAN message length
* \param signals CAN signal definitions
* \param counter Designated CAN signal definition for message counter, if the message has one
* \param checksum Designated CAN signal definition for payload checksum, if the message has one
*/
MessageDef(can::V1_0::CanMessageId id, uint16_t len, std::map<std::string, Signal> signals,
std::optional<Signal> counter = std::nullopt,
std::optional<Checksum> checksum = std::nullopt);
const Signal& operator[](const std::string& signalName) const;
can::V1_0::CanMessage makeDefault() const;
MessageCounter makeCounter() const;
void updateChecksum(can::V1_0::CanMessage& msg) const;
/**
* Validate the message payload is large enough to hold all the signals.
*/
bool validate(const can::V1_0::CanMessage& msg) const;
private:
const uint16_t kLen;
const std::map<std::string, Signal> kSignals;
const std::optional<Signal> kCounter;
const std::optional<Checksum> kChecksum;
};
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <android-base/macros.h>
#include <android/hardware/automotive/can/1.0/ICanBus.h>
#include <libprotocan/MessageCounter.h>
#include <libprotocan/MessageDef.h>
#include <utils/Mutex.h>
#include <queue>
namespace android::hardware::automotive::protocan {
class MessageInjectorManager;
/**
* Injects CAN messages with a counter to an existing system.
*
* This class is NOT meant to use in production - there should be no need to inject counted CAN
* messages where the other sender is also broadcasting them. If this is the case, it may be a sign
* your CAN network needs a redesign. This tool is intended for use for testing and demo purposes.
*/
class MessageInjector {
public:
MessageInjector(MessageDef msgDef, std::optional<std::chrono::milliseconds> interMessageDelay);
void inject(const can::V1_0::CanMessage& msg);
void inject(const std::initializer_list<can::V1_0::CanMessage> msgs);
private:
const MessageDef kMsgDef;
const std::optional<std::chrono::milliseconds> kInterMessageDelay;
MessageCounter mCounter;
mutable std::mutex mMessagesGuard;
std::queue<can::V1_0::CanMessage> mMessages GUARDED_BY(mMessagesGuard);
void onReceive(can::V1_0::ICanBus& bus, const can::V1_0::CanMessage& msg);
void processQueueLocked(can::V1_0::ICanBus& bus);
friend class MessageInjectorManager;
DISALLOW_COPY_AND_ASSIGN(MessageInjector);
};
/**
* Routes intercepted messages to MessageInjector instances configured to handle specific CAN
* message (CAN message ID). Intercepted messages from other nodes in CAN network are used to read
* current counter value in order to spoof the next packet.
*/
class MessageInjectorManager {
public:
MessageInjectorManager(std::initializer_list<std::shared_ptr<MessageInjector>> injectors);
void onReceive(sp<can::V1_0::ICanBus> bus, const can::V1_0::CanMessage& msg);
private:
std::map<can::V1_0::CanMessageId, std::shared_ptr<MessageInjector>> mInjectors;
DISALLOW_COPY_AND_ASSIGN(MessageInjectorManager);
};
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <android-base/macros.h>
#include <android/hardware/automotive/can/1.0/types.h>
namespace android::hardware::automotive::protocan {
/**
* TODO(twasilczyk): right now, only Little Endian signals are supported.
*/
class Signal {
public:
using value = uint64_t;
const value maxValue;
Signal(uint16_t start, uint8_t length, value defVal = 0);
value get(const can::V1_0::CanMessage& msg) const;
void set(can::V1_0::CanMessage& msg, value val) const;
void setDefault(can::V1_0::CanMessage& msg) const;
private:
const uint16_t kFirstByte; ///< Index of first byte that holds the signal
const uint8_t kFirstBit; ///< Index of first bit within first byte
const uint8_t kFirstByteBits; ///< How many bits of the first byte belong to the signal
const uint16_t kLastByte; ///< Index of last byte that holds the signal
const uint8_t kLastMask; ///< Bits of the last byte that belong to the signal
const uint8_t kFirstMask; ///< Bits of the first byte that belong to the signal
const value kDefVal;
};
} // namespace android::hardware::automotive::protocan

View File

@@ -0,0 +1,39 @@
//
// Copyright (C) 2020 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.
//
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_test {
name: "libprotocan_signal_test",
defaults: ["android.hardware.automotive.can@defaults"],
vendor: true,
gtest: true,
srcs: ["libprotocan_signal_test.cpp"],
static_libs: [
"libprotocan",
],
shared_libs: [
"android.hardware.automotive.can@1.0",
"libhidlbase",
],
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright (C) 2020 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 <libprotocan/Signal.h>
#include <gtest/gtest.h>
namespace android::hardware::automotive::protocan::unittest {
TEST(SignalTest, TestGetSingleBytes) {
can::V1_0::CanMessage msg = {};
msg.payload = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (unsigned i = 0; i < msg.payload.size(); i++) {
Signal signal(8 * i, 8);
ASSERT_EQ(i, signal.get(msg));
}
}
TEST(SignalTest, TestSetSingleBytes) {
std::vector<can::V1_0::CanMessage> msgs = {{}, {}, {}};
msgs[0].payload = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
msgs[1].payload = {0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB};
msgs[2].payload = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
for (unsigned i = 0; i < msgs[0].payload.size(); i++) {
Signal signal(8 * i, 8);
for (auto&& msgOriginal : msgs) {
auto msgModified = msgOriginal;
signal.set(msgModified, 0xBA);
auto msgExpected = msgOriginal;
msgExpected.payload[i] = 0xBA;
ASSERT_EQ(msgExpected, msgModified) << "i=" << i;
}
}
}
TEST(SignalTest, TestGetStart4) {
/* Data generated with Python3:
*
* from cantools.database.can import *
* hex(Message(1, 'm', 4, [Signal('s', 4, 16, byte_order='little_endian')]).
* decode(b'\xde\xad\xbe\xef')['s'])
*/
can::V1_0::CanMessage msg = {};
msg.payload = {0xDE, 0xAD, 0xBE, 0xEF};
can::V1_0::CanMessage msg2 = {};
msg2.payload = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD};
Signal s0_4(0, 4);
Signal s4_4(4, 4);
Signal s4_8(4, 8);
Signal s4_16(4, 16);
Signal s4_28(4, 28);
Signal s12_8(12, 8);
Signal s12_12(12, 12);
Signal s12_16(12, 16);
Signal s12_20(12, 20);
Signal s12_32(12, 32);
ASSERT_EQ(0xEu, s0_4.get(msg));
ASSERT_EQ(0xDu, s4_4.get(msg));
ASSERT_EQ(0xDDu, s4_8.get(msg));
ASSERT_EQ(0xEADDu, s4_16.get(msg));
ASSERT_EQ(0xEFBEADDu, s4_28.get(msg));
ASSERT_EQ(0xEAu, s12_8.get(msg));
ASSERT_EQ(0xBEAu, s12_12.get(msg));
ASSERT_EQ(0xFBEAu, s12_16.get(msg));
ASSERT_EQ(0xEFBEAu, s12_20.get(msg));
ASSERT_EQ(0xDDEEFBEAu, s12_32.get(msg2));
}
TEST(SignalTest, TestGet64) {
/* Data generated with Python3:
*
* from cantools.database.can import *
* hex(Message(1, 'm', 9, [Signal('s', 4, 64, byte_order='little_endian')]).
* decode(b'\xde\xad\xbe\xef\xab\xbc\xcd\xde\xef')['s'])
*/
can::V1_0::CanMessage msg = {};
msg.payload = {0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xBC, 0xCD, 0xDE, 0xEF};
Signal s0_64(0, 64);
Signal s8_64(8, 64);
Signal s4_64(4, 64);
Signal s1_64(1, 64);
ASSERT_EQ(0xDECDBCABEFBEADDEu, s0_64.get(msg));
ASSERT_EQ(0xEFDECDBCABEFBEADu, s8_64.get(msg));
ASSERT_EQ(0xFDECDBCABEFBEADDu, s4_64.get(msg));
ASSERT_EQ(0xEF66DE55F7DF56EFu, s1_64.get(msg));
}
TEST(SignalTest, TestGetAllStarts) {
/* Data generated with Python3:
*
* from cantools.database.can import *
* hex(Message(1, 'm', 6, [Signal('s', 0, 20, byte_order='little_endian')]).
* decode(b'\xde\xad\xbe\xef\xde\xad')['s'])
*/
std::map<int, Signal::value> shifts = {
{0, 0xEADDEu}, {1, 0xF56EFu}, {2, 0xFAB77u}, {3, 0x7D5BBu}, {4, 0xBEADDu}, {5, 0xDF56Eu},
{6, 0xEFAB7u}, {7, 0xF7D5Bu}, {8, 0xFBEADu}, {9, 0x7DF56u}, {10, 0xBEFABu}, {11, 0xDF7D5u},
};
can::V1_0::CanMessage msg = {};
msg.payload = {0xDE, 0xAD, 0xBE, 0xEF, 0xCC, 0xCC};
for (auto const& [start, expected] : shifts) {
Signal s(start, 20);
ASSERT_EQ(expected, s.get(msg)) << "shift of " << start << " failed";
}
}
TEST(SignalTest, TestSetStart4) {
/* Data generated with Python3:
*
* from cantools.database.can import *
* so=4 ; sl=8
* md = Message(1, 'm', 4, [Signal('a1', 0, so), Signal('a2', so+sl, 32-so-sl),
* Signal('s', so, sl, byte_order='little_endian')])
* m = md.decode(b'\xcc\xcc\xcc\xcc')
* m['s'] = 0xDE
* binascii.hexlify(md.encode(m)).upper()
*/
typedef struct {
int start;
int length;
Signal::value setValue;
hidl_vec<uint8_t> payload;
} case_t;
std::vector<case_t> cases = {
{0, 4, 0xDu, {0xCD, 0xCC, 0xCC, 0xCC}}, {4, 4, 0xDu, {0xDC, 0xCC, 0xCC, 0xCC}},
{4, 8, 0xDEu, {0xEC, 0xCD, 0xCC, 0xCC}}, {4, 16, 0xDEADu, {0xDC, 0xEA, 0xCD, 0xCC}},
{4, 24, 0xDEADBEu, {0xEC, 0xDB, 0xEA, 0xCD}}, {4, 28, 0xDEADBEEu, {0xEC, 0xBE, 0xAD, 0xDE}},
{12, 8, 0xDEu, {0xCC, 0xEC, 0xCD, 0xCC}}, {12, 12, 0xDEAu, {0xCC, 0xAC, 0xDE, 0xCC}},
{12, 16, 0xDEADu, {0xCC, 0xDC, 0xEA, 0xCD}}, {12, 20, 0xDEADBu, {0xCC, 0xBC, 0xAD, 0xDE}},
};
can::V1_0::CanMessage msg = {};
msg.payload = {0xCC, 0xCC, 0xCC, 0xCC};
for (auto const& tcase : cases) {
Signal s(tcase.start, tcase.length);
can::V1_0::CanMessage expectedMsg = {};
expectedMsg.payload = tcase.payload;
can::V1_0::CanMessage editedMsg = msg;
s.set(editedMsg, tcase.setValue);
ASSERT_EQ(expectedMsg, editedMsg) << " set(" << tcase.start << ", " << tcase.length << ")";
}
}
TEST(SignalTest, TestSetAllStarts) {
/* Data generated with Python3:
* from cantools.database.can import *
* import binascii
* import textwrap
*
* length = 20
* for start in range(0, 32 - length):
* signals = [Signal('s', start, length, byte_order='little_endian')]
* if start > 0: signals.append(Signal('a', 0, start, byte_order='little_endian'))
* signals.append(Signal('b', start + length, 32 - start - length,
* byte_order='little_endian'))
*
* md = Message(1, 'm', 4, signals)
* m = md.decode(b'\xcc\xcc\xcc\xcc')
* m['s'] = 0xDEADB
* out = binascii.hexlify(md.encode(m)).decode('ascii').upper()
* out = ', '.join(['0x{}'.format(v) for v in textwrap.wrap(out, 2)])
* print('{{ {:d}, {{ {:s} }}}},'.format(start, out))
*/
std::map<int, hidl_vec<uint8_t>> shifts = {
{0, {0xDB, 0xEA, 0xCD, 0xCC}}, {1, {0xB6, 0xD5, 0xDB, 0xCC}}, {2, {0x6C, 0xAB, 0xF7, 0xCC}},
{3, {0xDC, 0x56, 0xEF, 0xCC}}, {4, {0xBC, 0xAD, 0xDE, 0xCC}}, {5, {0x6C, 0x5B, 0xBD, 0xCD}},
{6, {0xCC, 0xB6, 0x7A, 0xCF}}, {7, {0xCC, 0x6D, 0xF5, 0xCE}}, {8, {0xCC, 0xDB, 0xEA, 0xCD}},
{9, {0xCC, 0xB6, 0xD5, 0xDB}}, {10, {0xCC, 0x6C, 0xAB, 0xF7}}, {11, {0xCC, 0xDC, 0x56, 0xEF}},
};
can::V1_0::CanMessage msg = {};
msg.payload = {0xCC, 0xCC, 0xCC, 0xCC};
for (auto const& [start, expectedPayload] : shifts) {
Signal s(start, 20);
can::V1_0::CanMessage expectedMsg = {};
expectedMsg.payload = expectedPayload;
can::V1_0::CanMessage editedMsg = msg;
s.set(editedMsg, 0xDEADB);
ASSERT_EQ(expectedMsg, editedMsg) << "shift of " << start << " failed";
}
}
} // namespace android::hardware::automotive::protocan::unittest