Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b6928036 authored by Ji Soo Shin's avatar Ji Soo Shin Committed by Android (Google) Code Review
Browse files

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

parents d431c503 9035e7c6
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
//
// 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.can@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",
    ],
}
+28 −0
Original line number Diff line number Diff line
/*
 * 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
+61 −0
Original line number Diff line number Diff line
/*
 * 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
+62 −0
Original line number Diff line number Diff line
/*
 * 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
+110 −0
Original line number Diff line number Diff line
/*
 * 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
Loading