Merge "Camera: Timing requirement between notify and processCaptureResult"
diff --git a/automotive/can/1.0/Android.bp b/automotive/can/1.0/Android.bp
new file mode 100644
index 0000000..fe2a2be
--- /dev/null
+++ b/automotive/can/1.0/Android.bp
@@ -0,0 +1,20 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.automotive.can@1.0",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "ICanBus.hal",
+ "ICanController.hal",
+ "ICanMessageListener.hal",
+ "ICloseHandle.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/automotive/can/1.0/ICanBus.hal b/automotive/can/1.0/ICanBus.hal
new file mode 100644
index 0000000..6ed89f3
--- /dev/null
+++ b/automotive/can/1.0/ICanBus.hal
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.automotive.can@1.0;
+
+import ICanMessageListener;
+import ICloseHandle;
+
+/**
+ * Represents a CAN bus interface that's up and configured.
+ *
+ * Configuration part is done in ICanController.
+ */
+interface ICanBus {
+ /**
+ * Send CAN message.
+ *
+ * @param message CAN message to send out
+ * @return result OK in the case of success
+ * PAYLOAD_TOO_LONG if the payload is too long
+ * INTERFACE_DOWN if the bus is down
+ * TRANSMISSION_FAILURE in case of transmission failure
+ */
+ send(CanMessage message) generates (Result result);
+
+ /**
+ * Requests HAL implementation to listen for specific CAN messages.
+ *
+ * HAL is responsible for maintaining listener set and sending out messages
+ * to each listener that matches given filter against received message.
+ *
+ * Empty filter list means no filtering. If two or more listeners requested
+ * different filters, HAL server must merge these to fulfill the superset of
+ * these filters. HAL must not send out a message to a listener which filter
+ * doesn't match given message id.
+ *
+ * If filtering is not supported at the hardware level (what's strongly
+ * recommended), it must be covered in the HAL.
+ *
+ * @param filter The set of requested filters
+ * @param listener The interface to receive the messages on
+ * @return result OK in the case of success
+ * INTERFACE_DOWN if the bus is down
+ * @return close A handle to call in order to remove the listener
+ */
+ listen(vec<CanMessageFilter> filter, ICanMessageListener listener)
+ generates (Result result, ICloseHandle close);
+};
diff --git a/automotive/can/1.0/ICanController.hal b/automotive/can/1.0/ICanController.hal
new file mode 100644
index 0000000..2c7494a
--- /dev/null
+++ b/automotive/can/1.0/ICanController.hal
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.automotive.can@1.0;
+
+/**
+ * Represents a CAN controller that's capable of configuring CAN bus interfaces.
+ *
+ * The goal of this service is to configure CAN interfaces and bring up HIDL
+ * server instances of ICanBus for each one that's up.
+ *
+ * Providing an ICanController interface to configure CAN buses is optional.
+ * A system can elect to publish only ICanBus if the hardware is hardcoded
+ * for a specific application.
+ */
+interface ICanController {
+ /**
+ * Type of an interface, a mean to express the domain of device address.
+ */
+ enum InterfaceType : uint8_t {
+ /**
+ * Virtual SocketCAN interface.
+ *
+ * The address is an interface name, such as vcan0. If the interface
+ * doesn't exist, HAL server must create it.
+ *
+ * Valid InterfaceIdentifier types:
+ * - address.
+ */
+ VIRTUAL,
+
+ /**
+ * Native SocketCAN interface.
+ *
+ * The address is an interface name, such as can0.
+ *
+ * Valid InterfaceIdentifier types:
+ * - address;
+ * - serialno.
+ */
+ SOCKETCAN,
+
+ /**
+ * Serial-based interface.
+ *
+ * The address is a patch to a device, such as /dev/ttyUSB0.
+ *
+ * Valid InterfaceIdentifier types:
+ * - address;
+ * - serialno.
+ */
+ SLCAN,
+
+ /**
+ * Proprietary interface, specific to the hardware system Android
+ * is running on. Instead of using address field, the interface is
+ * addressed with 0-based index.
+ *
+ * Valid InterfaceIdentifier types:
+ * - index
+ */
+ INDEXED
+ };
+
+ enum Result : uint8_t {
+ OK,
+
+ /**
+ * General error class, if others are not applicable.
+ */
+ UNKNOWN_ERROR,
+
+ /**
+ * Up request was called out of order (i.e. trying to up the
+ * interface twice).
+ */
+ INVALID_STATE,
+
+ /** Interface type is not supported. */
+ NOT_SUPPORTED,
+
+ /**
+ * Provided address (interface name, device path) doesn't exist or there
+ * is no device with a given serial no.
+ */
+ BAD_ADDRESS,
+
+ /** Provided baud rate is not supported by the hardware. */
+ BAD_BAUDRATE,
+ };
+
+ /**
+ * Configuration of the (physical or virtual) CAN bus.
+ *
+ * ISO TP and CAN FD are currently not supported.
+ */
+ struct BusConfiguration {
+ /**
+ * Name under which ICanBus HIDL service should be published.
+ *
+ * It must consist of only alphanumeric characters and underscore
+ * (a-z, A-Z, 0-9, '_'), at least 1 and at most 32 characters long.
+ */
+ string name;
+
+ /**
+ * Type of the hardware (or virtual) CAN interface.
+ */
+ InterfaceType iftype;
+
+ /**
+ * Identification of hardware interface to configure.
+ */
+ safe_union InterfaceIdentifier {
+ /**
+ * Interface name or other mean of identification of the specific
+ * interface port. Syntax depends on {@see iftype}, for details
+ * {@see InterfaceType}.
+ */
+ string address;
+
+ /**
+ * Numerical identifier of interface, used for InterfaceType#INDEXED.
+ */
+ uint8_t index;
+
+ /**
+ * Alternatively to providing {@see address}, one may provide a list
+ * of interface serial number suffixes. If there happens to be
+ * a device (like USB2CAN) with a matching serial number suffix,
+ * it gets selected.
+ *
+ * Client may utilize this in two ways: by matching against the
+ * entire serial number, or the last few characters (usually one).
+ * The former is better for small-scale test deployments (with just
+ * a handful of vehicles), the latter is good for larger scale
+ * (where a small suffix list may support large test fleet).
+ */
+ vec<string> serialno;
+ } interfaceId;
+
+ /**
+ * Baud rate for CAN communication.
+ *
+ * Typical baud rates are: 100000, 125000, 250000, 500000.
+ *
+ * For virtual interfaces this value is ignored.
+ */
+ uint32_t baudrate;
+ };
+
+ /**
+ * Fetches the list of interface types supported by this HAL server.
+ *
+ * @return iftypes The list of supported interface types
+ */
+ getSupportedInterfaceTypes() generates (vec<InterfaceType> iftypes);
+
+ /**
+ * Bring up the CAN interface and publish ICanBus server instance.
+ *
+ * @param config Configuration of the CAN interface
+ * @return result OK if the operation succeeded; error code otherwise.
+ */
+ upInterface(BusConfiguration config) generates (Result result);
+
+ /**
+ * Unpublish ICanBus server instance and bring down the CAN interface.
+ *
+ * In case of failure, at least the ICanBus server instance must be
+ * unpublished and resources freed on best-effort basis.
+ *
+ * @param name Name of the interface (@see BusConfiguration#name} to
+ * bring down
+ * @return success true in case of success, false otherwise
+ */
+ downInterface(string name) generates (bool success);
+};
diff --git a/automotive/can/1.0/ICanMessageListener.hal b/automotive/can/1.0/ICanMessageListener.hal
new file mode 100644
index 0000000..992d1c7
--- /dev/null
+++ b/automotive/can/1.0/ICanMessageListener.hal
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.automotive.can@1.0;
+
+/**
+ * CAN message listener.
+ */
+interface ICanMessageListener {
+ /**
+ * Called on received CAN message.
+ *
+ * The timestamp field of message struct is set to time when the message
+ * was received by the hardware. If it's not possible to fetch exact
+ * hardware time, this field should be set as early as possible to decrease
+ * potential time delta.
+ *
+ * @param message Received CAN message
+ */
+ onReceive(CanMessage message);
+
+ /**
+ * Called on error event.
+ *
+ * @param error Error type
+ */
+ onError(ErrorEvent error);
+};
diff --git a/automotive/can/1.0/ICloseHandle.hal b/automotive/can/1.0/ICloseHandle.hal
new file mode 100644
index 0000000..924c58b
--- /dev/null
+++ b/automotive/can/1.0/ICloseHandle.hal
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.automotive.can@1.0;
+
+/**
+ * Represents a generic close handle to remove a callback that doesn't need
+ * active interface.
+ *
+ * When close() is called OR when the interface is released, the underlying
+ * resources must be freed.
+ */
+interface ICloseHandle {
+ /**
+ * Closes the handle.
+ *
+ * The call must not fail and must be issued by the client at most once.
+ * Otherwise, the server must ignore subsequent calls.
+ */
+ close();
+};
diff --git a/automotive/can/1.0/default/Android.bp b/automotive/can/1.0/default/Android.bp
new file mode 100644
index 0000000..0a4afd6
--- /dev/null
+++ b/automotive/can/1.0/default/Android.bp
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_defaults {
+ name: "android.hardware.automotive.can@defaults",
+ cpp_std: "experimental",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
+ ],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.automotive.can@1.0-service",
+ init_rc: ["android.hardware.automotive.can@1.0-service.rc"],
+ defaults: ["android.hardware.automotive.can@defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "CanBus.cpp",
+ "CanBusNative.cpp",
+ "CanBusVirtual.cpp",
+ "CanController.cpp",
+ "CanSocket.cpp",
+ "CloseHandle.cpp",
+ "service.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.automotive.can@1.0",
+ "libhidlbase",
+ "libhidltransport",
+ ],
+ static_libs: [
+ "android.hardware.automotive.can@libnetdevice",
+ ],
+}
diff --git a/automotive/can/1.0/default/CanBus.cpp b/automotive/can/1.0/default/CanBus.cpp
new file mode 100644
index 0000000..65da291
--- /dev/null
+++ b/automotive/can/1.0/default/CanBus.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2019 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 "CanBus.h"
+
+#include "CloseHandle.h"
+
+#include <android-base/logging.h>
+#include <libnetdevice/can.h>
+#include <libnetdevice/libnetdevice.h>
+#include <linux/can.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Whether to log sent/received packets.
+ */
+static constexpr bool kSuperVerbose = false;
+
+Return<Result> CanBus::send(const CanMessage& message) {
+ std::lock_guard<std::mutex> lck(mIsUpGuard);
+ if (!mIsUp) return Result::INTERFACE_DOWN;
+
+ if (UNLIKELY(kSuperVerbose)) {
+ LOG(VERBOSE) << "Sending " << toString(message);
+ }
+
+ if (message.payload.size() > CAN_MAX_DLEN) return Result::PAYLOAD_TOO_LONG;
+
+ struct canfd_frame frame = {};
+ frame.can_id = message.id;
+ frame.len = message.payload.size();
+ memcpy(frame.data, message.payload.data(), message.payload.size());
+
+ if (!mSocket->send(frame)) return Result::TRANSMISSION_FAILURE;
+
+ return Result::OK;
+}
+
+Return<void> CanBus::listen(const hidl_vec<CanMessageFilter>& filter,
+ const sp<ICanMessageListener>& listenerCb, listen_cb _hidl_cb) {
+ std::lock_guard<std::mutex> lck(mIsUpGuard);
+
+ if (listenerCb == nullptr) {
+ _hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
+ return {};
+ }
+ if (!mIsUp) {
+ _hidl_cb(Result::INTERFACE_DOWN, nullptr);
+ return {};
+ }
+
+ std::lock_guard<std::mutex> lckListeners(mListenersGuard);
+
+ sp<CloseHandle> closeHandle = new CloseHandle([this, listenerCb]() {
+ std::lock_guard<std::mutex> lck(mListenersGuard);
+ std::erase_if(mListeners, [&](const auto& e) { return e.callback == listenerCb; });
+ });
+ mListeners.emplace_back(CanMessageListener{listenerCb, filter, closeHandle});
+ auto& listener = mListeners.back();
+
+ // fix message IDs to have all zeros on bits not covered by mask
+ std::for_each(listener.filter.begin(), listener.filter.end(),
+ [](auto& rule) { rule.id &= rule.mask; });
+
+ _hidl_cb(Result::OK, closeHandle);
+ return {};
+}
+
+CanBus::CanBus(const std::string& ifname) : mIfname(ifname) {}
+
+CanBus::~CanBus() {
+ std::lock_guard<std::mutex> lck(mIsUpGuard);
+ CHECK(!mIsUp) << "Interface is still up while being destroyed";
+
+ std::lock_guard<std::mutex> lckListeners(mListenersGuard);
+ CHECK(mListeners.empty()) << "Listeners list is not empty while interface is being destroyed";
+}
+
+ICanController::Result CanBus::preUp() {
+ return ICanController::Result::OK;
+}
+
+bool CanBus::postDown() {
+ return true;
+}
+
+ICanController::Result CanBus::up() {
+ std::lock_guard<std::mutex> lck(mIsUpGuard);
+
+ if (mIsUp) {
+ LOG(WARNING) << "Interface is already up";
+ return ICanController::Result::INVALID_STATE;
+ }
+
+ const auto preResult = preUp();
+ if (preResult != ICanController::Result::OK) return preResult;
+
+ const auto isUp = netdevice::isUp(mIfname);
+ if (!isUp.has_value()) {
+ // preUp() should prepare the interface (either create or make sure it's there)
+ LOG(ERROR) << "Interface " << mIfname << " didn't get prepared";
+ return ICanController::Result::BAD_ADDRESS;
+ }
+ mWasUpInitially = *isUp;
+
+ if (!mWasUpInitially && !netdevice::up(mIfname)) {
+ LOG(ERROR) << "Can't bring " << mIfname << " up";
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ using namespace std::placeholders;
+ CanSocket::ReadCallback rdcb = std::bind(&CanBus::onRead, this, _1, _2);
+ CanSocket::ErrorCallback errcb = std::bind(&CanBus::onError, this);
+ mSocket = CanSocket::open(mIfname, rdcb, errcb);
+ if (!mSocket) {
+ if (!mWasUpInitially) netdevice::down(mIfname);
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ mIsUp = true;
+ return ICanController::Result::OK;
+}
+
+void CanBus::clearListeners() {
+ std::vector<wp<ICloseHandle>> listenersToClose;
+ {
+ std::lock_guard<std::mutex> lck(mListenersGuard);
+ std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(listenersToClose),
+ [](const auto& e) { return e.closeHandle; });
+ }
+
+ for (auto& weakListener : listenersToClose) {
+ /* Between populating listenersToClose and calling close method here, some listeners might
+ * have been already removed from the original mListeners list (resulting in a dangling weak
+ * pointer here). It's fine - we just want to clean them up. */
+ auto listener = weakListener.promote();
+ if (listener != nullptr) listener->close();
+ }
+
+ std::lock_guard<std::mutex> lck(mListenersGuard);
+ CHECK(mListeners.empty()) << "Listeners list wasn't emptied";
+}
+
+bool CanBus::down() {
+ std::lock_guard<std::mutex> lck(mIsUpGuard);
+
+ if (!mIsUp) {
+ LOG(WARNING) << "Interface is already down";
+ return false;
+ }
+ mIsUp = false;
+
+ clearListeners();
+ mSocket.reset();
+
+ bool success = true;
+
+ if (!mWasUpInitially && !netdevice::down(mIfname)) {
+ LOG(ERROR) << "Can't bring " << mIfname << " down";
+ // don't return yet, let's try to do best-effort cleanup
+ success = false;
+ }
+
+ if (!postDown()) success = false;
+
+ return success;
+}
+
+/**
+ * Match the filter set against message id.
+ *
+ * For details on the filters syntax, please see CanMessageFilter at
+ * the HAL definition (types.hal).
+ *
+ * \param filter Filter to match against
+ * \param id Message id to filter
+ * \return true if the message id matches the filter, false otherwise
+ */
+static bool match(const hidl_vec<CanMessageFilter>& filter, CanMessageId id) {
+ if (filter.size() == 0) return true;
+
+ bool anyNonInvertedPresent = false;
+ bool anyNonInvertedSatisfied = false;
+ for (auto& rule : filter) {
+ const bool satisfied = ((id & rule.mask) == rule.id) == !rule.inverted;
+ if (rule.inverted) {
+ // Any inverted (blacklist) rule not being satisfied invalidates the whole filter set.
+ if (!satisfied) return false;
+ } else {
+ anyNonInvertedPresent = true;
+ if (satisfied) anyNonInvertedSatisfied = true;
+ }
+ }
+ return !anyNonInvertedPresent || anyNonInvertedSatisfied;
+}
+
+void CanBus::onRead(const struct canfd_frame& frame, std::chrono::nanoseconds timestamp) {
+ CanMessage message = {};
+ message.id = frame.can_id;
+ message.payload = hidl_vec<uint8_t>(frame.data, frame.data + frame.len);
+ message.timestamp = timestamp.count();
+
+ if (UNLIKELY(kSuperVerbose)) {
+ LOG(VERBOSE) << "Got message " << toString(message);
+ }
+
+ std::lock_guard<std::mutex> lck(mListenersGuard);
+ for (auto& listener : mListeners) {
+ if (!match(listener.filter, message.id)) continue;
+ if (!listener.callback->onReceive(message).isOk()) {
+ LOG(WARNING) << "Failed to notify listener about message";
+ }
+ }
+}
+
+void CanBus::onError() {
+ std::lock_guard<std::mutex> lck(mListenersGuard);
+ for (auto& listener : mListeners) {
+ if (!listener.callback->onError(ErrorEvent::HARDWARE_ERROR).isOk()) {
+ LOG(WARNING) << "Failed to notify listener about error";
+ }
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanBus.h b/automotive/can/1.0/default/CanBus.h
new file mode 100644
index 0000000..81a83c8
--- /dev/null
+++ b/automotive/can/1.0/default/CanBus.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 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 "CanSocket.h"
+
+#include <android-base/unique_fd.h>
+#include <android/hardware/automotive/can/1.0/ICanBus.h>
+#include <android/hardware/automotive/can/1.0/ICanController.h>
+#include <utils/Mutex.h>
+
+#include <atomic>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+struct CanBus : public ICanBus {
+ virtual ~CanBus();
+
+ Return<Result> send(const CanMessage& message) override;
+ Return<void> listen(const hidl_vec<CanMessageFilter>& filter,
+ const sp<ICanMessageListener>& listener, listen_cb _hidl_cb) override;
+
+ ICanController::Result up();
+ bool down();
+
+ protected:
+ CanBus(const std::string& ifname);
+
+ /**
+ * Prepare the SocketCAN interface.
+ *
+ * After calling this method, mIfname network interface is available and ready to be brought up.
+ */
+ virtual ICanController::Result preUp();
+
+ /**
+ * Cleanup after bringing the interface down.
+ *
+ * This is a counterpart to preUp().
+ */
+ virtual bool postDown();
+
+ /** Network interface name. */
+ const std::string mIfname;
+
+ private:
+ struct CanMessageListener {
+ sp<ICanMessageListener> callback;
+ hidl_vec<CanMessageFilter> filter;
+ wp<ICloseHandle> closeHandle;
+ };
+ void clearListeners();
+
+ void onRead(const struct canfd_frame& frame, std::chrono::nanoseconds timestamp);
+ void onError();
+
+ std::mutex mListenersGuard;
+ std::vector<CanMessageListener> mListeners GUARDED_BY(mListenersGuard);
+
+ std::unique_ptr<CanSocket> mSocket;
+ bool mWasUpInitially;
+
+ /**
+ * Guard for up flag is required to be held for entire time when the interface is being used
+ * (i.e. message being sent), because we don't want the interface to be torn down while
+ * executing that operation.
+ */
+ std::mutex mIsUpGuard;
+ bool mIsUp GUARDED_BY(mIsUpGuard) = false;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanBusNative.cpp b/automotive/can/1.0/default/CanBusNative.cpp
new file mode 100644
index 0000000..365b749
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusNative.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 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 "CanBusNative.h"
+
+#include <android-base/logging.h>
+#include <libnetdevice/can.h>
+#include <libnetdevice/libnetdevice.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+CanBusNative::CanBusNative(const std::string& ifname, uint32_t baudrate)
+ : CanBus(ifname), mBaudrate(baudrate) {}
+
+ICanController::Result CanBusNative::preUp() {
+ if (!netdevice::exists(mIfname)) {
+ LOG(ERROR) << "Interface " << mIfname << " doesn't exist";
+ return ICanController::Result::BAD_ADDRESS;
+ }
+
+ if (!netdevice::down(mIfname)) {
+ LOG(ERROR) << "Can't bring " << mIfname << " down (to configure it)";
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ if (!netdevice::can::setBitrate(mIfname, mBaudrate)) {
+ LOG(ERROR) << "Can't set bitrate " << mBaudrate << " for " << mIfname;
+ return ICanController::Result::BAD_BAUDRATE;
+ }
+
+ return ICanController::Result::OK;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanBusNative.h b/automotive/can/1.0/default/CanBusNative.h
new file mode 100644
index 0000000..126f1cb
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusNative.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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 "CanBus.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+struct CanBusNative : public CanBus {
+ CanBusNative(const std::string& ifname, uint32_t baudrate);
+
+ protected:
+ virtual ICanController::Result preUp() override;
+
+ private:
+ const uint32_t mBaudrate;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanBusVirtual.cpp b/automotive/can/1.0/default/CanBusVirtual.cpp
new file mode 100644
index 0000000..cc59fa9
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusVirtual.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 "CanBusVirtual.h"
+
+#include <android-base/logging.h>
+#include <libnetdevice/libnetdevice.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+CanBusVirtual::CanBusVirtual(const std::string& ifname) : CanBus(ifname) {}
+
+ICanController::Result CanBusVirtual::preUp() {
+ if (netdevice::exists(mIfname)) return ICanController::Result::OK;
+
+ LOG(DEBUG) << "Virtual interface " << mIfname << " doesn't exist, creating...";
+ mWasCreated = true;
+ if (!netdevice::add(mIfname, "vcan")) {
+ LOG(ERROR) << "Can't create vcan interface " << mIfname;
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ return ICanController::Result::OK;
+}
+
+bool CanBusVirtual::postDown() {
+ if (mWasCreated) {
+ mWasCreated = false;
+ if (!netdevice::del(mIfname)) {
+ LOG(ERROR) << "Couldn't remove vcan interface " << mIfname;
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanBusVirtual.h b/automotive/can/1.0/default/CanBusVirtual.h
new file mode 100644
index 0000000..c2d5794
--- /dev/null
+++ b/automotive/can/1.0/default/CanBusVirtual.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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 "CanBus.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+struct CanBusVirtual : public CanBus {
+ CanBusVirtual(const std::string& ifname);
+
+ protected:
+ virtual ICanController::Result preUp() override;
+ virtual bool postDown() override;
+
+ private:
+ bool mWasCreated = false;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanController.cpp b/automotive/can/1.0/default/CanController.cpp
new file mode 100644
index 0000000..20adbe1
--- /dev/null
+++ b/automotive/can/1.0/default/CanController.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 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 "CanController.h"
+
+#include "CanBusNative.h"
+#include "CanBusVirtual.h"
+
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+
+#include <regex>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+using IfaceIdDisc = ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator;
+
+Return<void> CanController::getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) {
+ _hidl_cb({
+ ICanController::InterfaceType::VIRTUAL,
+ ICanController::InterfaceType::SOCKETCAN,
+ });
+ return {};
+}
+
+static bool isValidName(const std::string& name) {
+ static const std::regex nameRE("^[a-zA-Z0-9_]{1,32}$");
+ return std::regex_match(name, nameRE);
+}
+
+Return<ICanController::Result> CanController::upInterface(
+ const ICanController::BusConfiguration& config) {
+ LOG(VERBOSE) << "Attempting to bring interface up: " << toString(config);
+
+ std::lock_guard<std::mutex> lck(mCanBusesGuard);
+
+ if (!isValidName(config.name)) {
+ LOG(ERROR) << "Bus name " << config.name << " is invalid";
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ if (mCanBuses.find(config.name) != mCanBuses.end()) {
+ LOG(ERROR) << "Bus " << config.name << " is already up";
+ return ICanController::Result::INVALID_STATE;
+ }
+
+ sp<CanBus> busService;
+
+ if (config.iftype == ICanController::InterfaceType::SOCKETCAN) {
+ // TODO(b/135918744): support serialno
+ if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
+ busService = new CanBusNative(config.interfaceId.address(), config.baudrate);
+ } else {
+ return ICanController::Result::BAD_ADDRESS;
+ }
+ } else if (config.iftype == ICanController::InterfaceType::VIRTUAL) {
+ if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
+ busService = new CanBusVirtual(config.interfaceId.address());
+ } else {
+ return ICanController::Result::BAD_ADDRESS;
+ }
+ } else {
+ return ICanController::Result::NOT_SUPPORTED;
+ }
+
+ const auto result = busService->up();
+ if (result != ICanController::Result::OK) return result;
+
+ if (busService->registerAsService(config.name) != OK) {
+ LOG(ERROR) << "Failed to register ICanBus/" << config.name;
+ if (!busService->down()) {
+ LOG(WARNING) << "Failed to bring down CAN bus that failed to register";
+ }
+ return ICanController::Result::UNKNOWN_ERROR;
+ }
+
+ mCanBuses[config.name] = busService;
+
+ return ICanController::Result::OK;
+}
+
+Return<bool> CanController::downInterface(const hidl_string& name) {
+ LOG(VERBOSE) << "Attempting to bring interface down: " << name;
+
+ std::lock_guard<std::mutex> lck(mCanBusesGuard);
+
+ auto busEntry = mCanBuses.extract(name);
+ if (!busEntry) {
+ LOG(WARNING) << "Interface " << name << " is not up";
+ return false;
+ }
+
+ auto success = true;
+
+ auto manager = hidl::manager::V1_2::IServiceManager::getService();
+ if (!manager || !manager->tryUnregister(ICanBus::descriptor, name, busEntry.mapped())) {
+ LOG(ERROR) << "Couldn't unregister " << name;
+ // don't return yet, let's try to do best-effort cleanup
+ success = false;
+ }
+
+ if (!busEntry.mapped()->down()) {
+ LOG(ERROR) << "Couldn't bring " << name << " down";
+ success = false;
+ }
+
+ return success;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanController.h b/automotive/can/1.0/default/CanController.h
new file mode 100644
index 0000000..0674d0e
--- /dev/null
+++ b/automotive/can/1.0/default/CanController.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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 "CanBus.h"
+
+#include <android/hardware/automotive/can/1.0/ICanController.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+struct CanController : public ICanController {
+ Return<void> getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) override;
+
+ Return<ICanController::Result> upInterface(
+ const ICanController::BusConfiguration& config) override;
+ Return<bool> downInterface(const hidl_string& name) override;
+
+ private:
+ std::mutex mCanBusesGuard;
+ std::map<std::string, sp<CanBus>> mCanBuses GUARDED_BY(mCanBusesGuard);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanSocket.cpp b/automotive/can/1.0/default/CanSocket.cpp
new file mode 100644
index 0000000..4d86de6
--- /dev/null
+++ b/automotive/can/1.0/default/CanSocket.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 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 "CanSocket.h"
+
+#include <android-base/logging.h>
+#include <libnetdevice/can.h>
+#include <libnetdevice/libnetdevice.h>
+#include <linux/can.h>
+#include <utils/SystemClock.h>
+
+#include <chrono>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+using namespace std::chrono_literals;
+
+/**
+ * How frequently the read thread checks whether the interface was asked to be down.
+ *
+ * Note: This does *not* affect read timing or bandwidth, just CPU load vs time to
+ * down the interface.
+ */
+static constexpr auto kReadPooling = 100ms;
+
+std::unique_ptr<CanSocket> CanSocket::open(const std::string& ifname, ReadCallback rdcb,
+ ErrorCallback errcb) {
+ auto sock = netdevice::can::socket(ifname);
+ if (!sock.ok()) {
+ LOG(ERROR) << "Can't open CAN socket on " << ifname;
+ return nullptr;
+ }
+
+ // Can't use std::make_unique due to private CanSocket constructor.
+ return std::unique_ptr<CanSocket>(new CanSocket(std::move(sock), rdcb, errcb));
+}
+
+CanSocket::CanSocket(base::unique_fd socket, ReadCallback rdcb, ErrorCallback errcb)
+ : mReadCallback(rdcb),
+ mErrorCallback(errcb),
+ mSocket(std::move(socket)),
+ mReaderThread(&CanSocket::readerThread, this) {}
+
+CanSocket::~CanSocket() {
+ mStopReaderThread = true;
+ mReaderThread.join();
+}
+
+bool CanSocket::send(const struct canfd_frame& frame) {
+ const auto res = write(mSocket.get(), &frame, CAN_MTU);
+ if (res < 0) {
+ LOG(DEBUG) << "CanSocket send failed: " << errno;
+ return false;
+ }
+ if (res != CAN_MTU) {
+ LOG(DEBUG) << "CanSocket sent wrong number of bytes: " << res;
+ return false;
+ }
+ return true;
+}
+
+static struct timeval toTimeval(std::chrono::microseconds t) {
+ struct timeval tv;
+ tv.tv_sec = t / 1s;
+ tv.tv_usec = (t % 1s) / 1us;
+ return tv;
+}
+
+static int selectRead(const base::unique_fd& fd, std::chrono::microseconds timeout) {
+ auto timeouttv = toTimeval(timeout);
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(fd.get(), &readfds);
+ return select(fd.get() + 1, &readfds, nullptr, nullptr, &timeouttv);
+}
+
+void CanSocket::readerThread() {
+ LOG(VERBOSE) << "Reader thread started";
+
+ while (!mStopReaderThread) {
+ /* The ideal would be to have a blocking read(3) call and interrupt it with shutdown(3).
+ * This is unfortunately not supported for SocketCAN, so we need to rely on select(3).
+ */
+ const auto sel = selectRead(mSocket, kReadPooling);
+ if (sel == 0) continue; // timeout
+ if (sel == -1) {
+ LOG(ERROR) << "Select failed: " << errno;
+ break;
+ }
+
+ struct canfd_frame frame;
+ const auto nbytes = read(mSocket.get(), &frame, CAN_MTU);
+
+ /* We could use SIOCGSTAMP to get a precise UNIX timestamp for a given packet, but what
+ * we really need is a time since boot. There is no direct way to convert between these
+ * clocks. We could implement a class to calculate the difference between the clocks
+ * (querying both several times and picking the smallest difference); apply the difference
+ * to a SIOCGSTAMP returned value; re-synchronize if the elapsed time is too much in the
+ * past (indicating the UNIX timestamp might have been adjusted).
+ *
+ * Apart from the added complexity, it's possible the added calculations and system calls
+ * would add so much time to the processing pipeline so the precision of the reported time
+ * was buried under the subsystem latency. Let's just use a local time since boot here and
+ * leave precise hardware timestamps for custom proprietary implementations (if needed).
+ */
+ const std::chrono::nanoseconds ts(elapsedRealtimeNano());
+
+ if (nbytes != CAN_MTU) {
+ if (nbytes >= 0) {
+ LOG(ERROR) << "Failed to read CAN packet, got " << nbytes << " bytes";
+ break;
+ }
+ if (errno == EAGAIN) continue;
+
+ LOG(ERROR) << "Failed to read CAN packet: " << errno;
+ break;
+ }
+
+ mReadCallback(frame, ts);
+ }
+
+ if (!mStopReaderThread) mErrorCallback();
+
+ LOG(VERBOSE) << "Reader thread stopped";
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CanSocket.h b/automotive/can/1.0/default/CanSocket.h
new file mode 100644
index 0000000..cd7162d
--- /dev/null
+++ b/automotive/can/1.0/default/CanSocket.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 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-base/unique_fd.h>
+#include <linux/can.h>
+
+#include <atomic>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Wrapper around SocketCAN socket.
+ */
+struct CanSocket {
+ using ReadCallback = std::function<void(const struct canfd_frame&, std::chrono::nanoseconds)>;
+ using ErrorCallback = std::function<void()>;
+
+ /**
+ * Open and bind SocketCAN socket.
+ *
+ * \param ifname SocketCAN network interface name (such as can0)
+ * \param rdcb Callback on received messages
+ * \param errcb Callback on socket failure
+ * \return Socket instance, or nullptr if it wasn't possible to open one
+ */
+ static std::unique_ptr<CanSocket> open(const std::string& ifname, ReadCallback rdcb,
+ ErrorCallback errcb);
+ virtual ~CanSocket();
+
+ /**
+ * Send CAN frame.
+ *
+ * \param frame Frame to send
+ * \return true in case of success, false otherwise
+ */
+ bool send(const struct canfd_frame& frame);
+
+ private:
+ CanSocket(base::unique_fd socket, ReadCallback rdcb, ErrorCallback errcb);
+ void readerThread();
+
+ ReadCallback mReadCallback;
+ ErrorCallback mErrorCallback;
+
+ const base::unique_fd mSocket;
+ std::thread mReaderThread;
+ std::atomic<bool> mStopReaderThread = false;
+
+ DISALLOW_COPY_AND_ASSIGN(CanSocket);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CloseHandle.cpp b/automotive/can/1.0/default/CloseHandle.cpp
new file mode 100644
index 0000000..13693d2
--- /dev/null
+++ b/automotive/can/1.0/default/CloseHandle.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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 "CloseHandle.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+CloseHandle::CloseHandle(Callback callback) : mCallback(callback) {}
+
+CloseHandle::~CloseHandle() {
+ close();
+}
+
+Return<void> CloseHandle::close() {
+ const auto wasClosed = mIsClosed.exchange(true);
+ if (wasClosed) return {};
+
+ mCallback();
+ return {};
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/CloseHandle.h b/automotive/can/1.0/default/CloseHandle.h
new file mode 100644
index 0000000..94972e0
--- /dev/null
+++ b/automotive/can/1.0/default/CloseHandle.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 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/ICloseHandle.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Generic ICloseHandle implementation ignoring double-close events.
+ */
+struct CloseHandle : public ICloseHandle {
+ using Callback = std::function<void()>;
+
+ /**
+ * Create a handle with a callback.
+ *
+ * The callback is guaranteed to be called exactly once.
+ *
+ * \param callback Called on the first close() call, or on destruction of the handle
+ */
+ CloseHandle(Callback callback);
+ virtual ~CloseHandle();
+
+ Return<void> close() override;
+
+ private:
+ Callback mCallback;
+ std::atomic<bool> mIsClosed = false;
+
+ DISALLOW_COPY_AND_ASSIGN(CloseHandle);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/default/android.hardware.automotive.can@1.0-service.rc b/automotive/can/1.0/default/android.hardware.automotive.can@1.0-service.rc
new file mode 100644
index 0000000..a629bda
--- /dev/null
+++ b/automotive/can/1.0/default/android.hardware.automotive.can@1.0-service.rc
@@ -0,0 +1,5 @@
+service can-hal-1.0-service /vendor/bin/hw/android.hardware.automotive.can@1.0-service
+ class hal
+ capabilities NET_ADMIN
+ user vehicle_network
+ group system inet
diff --git a/automotive/can/1.0/default/libnetdevice/Android.bp b/automotive/can/1.0/default/libnetdevice/Android.bp
new file mode 100644
index 0000000..31e5ad0
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_library_static {
+ name: "android.hardware.automotive.can@libnetdevice",
+ defaults: ["android.hardware.automotive.can@defaults"],
+ vendor_available: true,
+ relative_install_path: "hw",
+ srcs: [
+ "NetlinkRequest.cpp",
+ "NetlinkSocket.cpp",
+ "can.cpp",
+ "common.cpp",
+ "libnetdevice.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkRequest.cpp b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.cpp
new file mode 100644
index 0000000..9845bc7
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 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 "NetlinkRequest.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace netdevice {
+namespace impl {
+
+static struct rtattr* nlmsg_tail(struct nlmsghdr* n) {
+ return reinterpret_cast<struct rtattr*>( //
+ reinterpret_cast<uintptr_t>(n) + NLMSG_ALIGN(n->nlmsg_len));
+}
+
+struct rtattr* addattr_l(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type, const void* data,
+ size_t dataLen) {
+ size_t newLen = NLMSG_ALIGN(n->nlmsg_len) + RTA_SPACE(dataLen);
+ if (newLen > maxLen) {
+ LOG(ERROR) << "addattr_l failed - exceeded maxLen: " << newLen << " > " << maxLen;
+ return nullptr;
+ }
+
+ auto attr = nlmsg_tail(n);
+ attr->rta_len = RTA_SPACE(dataLen);
+ attr->rta_type = type;
+ if (dataLen > 0) memcpy(RTA_DATA(attr), data, dataLen);
+
+ n->nlmsg_len = newLen;
+ return attr;
+}
+
+struct rtattr* addattr_nest(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type) {
+ return addattr_l(n, maxLen, type, nullptr, 0);
+}
+
+void addattr_nest_end(struct nlmsghdr* n, struct rtattr* nest) {
+ size_t nestLen = reinterpret_cast<uintptr_t>(nlmsg_tail(n)) - reinterpret_cast<uintptr_t>(nest);
+ nest->rta_len = nestLen;
+}
+
+} // namespace impl
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkRequest.h b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.h
new file mode 100644
index 0000000..21202e3
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 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 <linux/rtnetlink.h>
+
+#include <string>
+
+namespace android {
+namespace netdevice {
+
+typedef unsigned short rtattrtype_t; // as in rtnetlink.h
+typedef __u16 nlmsgtype_t; // as in netlink.h
+
+/**
+ * Implementation details, do not use outside NetlinkRequest template.
+ */
+namespace impl {
+
+struct rtattr* addattr_l(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type, const void* data,
+ size_t dataLen);
+struct rtattr* addattr_nest(struct nlmsghdr* n, size_t maxLen, rtattrtype_t type);
+void addattr_nest_end(struct nlmsghdr* n, struct rtattr* nest);
+
+} // namespace impl
+
+/**
+ * Wrapper around NETLINK_ROUTE messages, to build them in C++ style.
+ *
+ * \param T specific message header (such as struct ifinfomsg)
+ * \param BUFSIZE how much space to reserve for payload (not counting the header size)
+ */
+template <class T, unsigned int BUFSIZE = 128>
+struct NetlinkRequest {
+ /**
+ * Create empty message.
+ *
+ * \param type Message type (such as RTM_NEWLINK)
+ * \param flags Message flags (such as NLM_F_REQUEST)
+ */
+ NetlinkRequest(nlmsgtype_t type, uint16_t flags) {
+ mRequest.nlmsg.nlmsg_len = NLMSG_LENGTH(sizeof(mRequest.data));
+ mRequest.nlmsg.nlmsg_type = type;
+ mRequest.nlmsg.nlmsg_flags = flags;
+ }
+
+ /** Returns pointer to raw netlink message header. */
+ struct nlmsghdr* header() {
+ return &mRequest.nlmsg;
+ }
+ /** Reference to message-specific header. */
+ T& data() { return mRequest.data; }
+
+ /**
+ * Adds an attribute of a simple type.
+ *
+ * If this method fails (i.e. due to insufficient space), the message will be marked
+ * as bad (\see isGood).
+ *
+ * \param type attribute type (such as IFLA_IFNAME)
+ * \param attr attribute data
+ */
+ template <class A>
+ void addattr(rtattrtype_t type, const A& attr) {
+ if (!mIsGood) return;
+ auto ap = impl::addattr_l(&mRequest.nlmsg, sizeof(mRequest), type, &attr, sizeof(attr));
+ if (ap == nullptr) mIsGood = false;
+ }
+
+ template <>
+ void addattr(rtattrtype_t type, const std::string& s) {
+ if (!mIsGood) return;
+ auto ap = impl::addattr_l(&mRequest.nlmsg, sizeof(mRequest), type, s.c_str(), s.size() + 1);
+ if (ap == nullptr) mIsGood = false;
+ }
+
+ /**
+ * Guard class to frame nested attributes. See nest(int).
+ */
+ struct Nest {
+ Nest(NetlinkRequest& req, rtattrtype_t type) : mReq(req), mAttr(req.nestStart(type)) {}
+ ~Nest() { mReq.nestEnd(mAttr); }
+
+ private:
+ NetlinkRequest& mReq;
+ struct rtattr* mAttr;
+
+ DISALLOW_COPY_AND_ASSIGN(Nest);
+ };
+
+ /**
+ * Add nested attribute.
+ *
+ * The returned object is a guard for auto-nesting children inside the argument attribute.
+ * When the Nest object goes out of scope, the nesting attribute is closed.
+ *
+ * Example usage nesting IFLA_CAN_BITTIMING inside IFLA_INFO_DATA, which is nested
+ * inside IFLA_LINKINFO:
+ * NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
+ * {
+ * auto linkinfo = req.nest(IFLA_LINKINFO);
+ * req.addattr(IFLA_INFO_KIND, "can");
+ * {
+ * auto infodata = req.nest(IFLA_INFO_DATA);
+ * req.addattr(IFLA_CAN_BITTIMING, bitTimingStruct);
+ * }
+ * }
+ * // use req
+ *
+ * \param type attribute type (such as IFLA_LINKINFO)
+ */
+ Nest nest(int type) { return Nest(*this, type); }
+
+ /**
+ * Indicates, whether the message is in a good state.
+ *
+ * The bad state is usually a result of payload buffer being too small.
+ * You can modify BUFSIZE template parameter to fix this.
+ */
+ bool isGood() const { return mIsGood; }
+
+ private:
+ bool mIsGood = true;
+
+ struct {
+ struct nlmsghdr nlmsg;
+ T data;
+ char buf[BUFSIZE];
+ } mRequest = {};
+
+ struct rtattr* nestStart(rtattrtype_t type) {
+ if (!mIsGood) return nullptr;
+ auto attr = impl::addattr_nest(&mRequest.nlmsg, sizeof(mRequest), type);
+ if (attr == nullptr) mIsGood = false;
+ return attr;
+ }
+
+ void nestEnd(struct rtattr* nest) {
+ if (mIsGood && nest != nullptr) impl::addattr_nest_end(&mRequest.nlmsg, nest);
+ }
+};
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp
new file mode 100644
index 0000000..0514764
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 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 "NetlinkSocket.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace netdevice {
+
+NetlinkSocket::NetlinkSocket(int protocol) {
+ mFd.reset(socket(AF_NETLINK, SOCK_RAW, protocol));
+ if (!mFd.ok()) {
+ LOG(ERROR) << "Can't open Netlink socket: " << errno;
+ mFailed = true;
+ return;
+ }
+
+ struct sockaddr_nl sa = {};
+ sa.nl_family = AF_NETLINK;
+
+ if (bind(mFd.get(), reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa)) < 0) {
+ LOG(ERROR) << "Can't bind Netlink socket: " << errno;
+ mFd.reset();
+ mFailed = true;
+ }
+}
+
+bool NetlinkSocket::send(struct nlmsghdr* nlmsg) {
+ if (mFailed) return false;
+
+ nlmsg->nlmsg_pid = 0; // kernel
+ nlmsg->nlmsg_seq = mSeq++;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ struct iovec iov = {nlmsg, nlmsg->nlmsg_len};
+
+ struct sockaddr_nl sa = {};
+ sa.nl_family = AF_NETLINK;
+
+ struct msghdr msg = {};
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (sendmsg(mFd.get(), &msg, 0) < 0) {
+ LOG(ERROR) << "Can't send Netlink message: " << errno;
+ return false;
+ }
+ return true;
+}
+
+bool NetlinkSocket::receiveAck() {
+ if (mFailed) return false;
+
+ char buf[8192];
+
+ struct sockaddr_nl sa;
+ struct iovec iov = {buf, sizeof(buf)};
+
+ struct msghdr msg = {};
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ const ssize_t status = recvmsg(mFd.get(), &msg, 0);
+ if (status < 0) {
+ LOG(ERROR) << "Failed to receive Netlink message: " << errno;
+ return false;
+ }
+ size_t remainingLen = status;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ LOG(ERROR) << "Failed to receive Netlink message: truncated";
+ return false;
+ }
+
+ for (auto nlmsg = reinterpret_cast<struct nlmsghdr*>(buf); NLMSG_OK(nlmsg, remainingLen);
+ nlmsg = NLMSG_NEXT(nlmsg, remainingLen)) {
+ // We're looking for error/ack message only, ignoring others.
+ if (nlmsg->nlmsg_type != NLMSG_ERROR) {
+ LOG(WARNING) << "Received unexpected Netlink message (ignored): " << nlmsg->nlmsg_type;
+ continue;
+ }
+
+ // Found error/ack message, return status.
+ auto nlerr = reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(nlmsg));
+ if (nlerr->error != 0) {
+ LOG(ERROR) << "Received Netlink error message: " << nlerr->error;
+ return false;
+ }
+ return true;
+ }
+ // Couldn't find any error/ack messages.
+ return false;
+}
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkSocket.h b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.h
new file mode 100644
index 0000000..81d6224
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 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 "NetlinkRequest.h"
+
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+#include <linux/netlink.h>
+
+namespace android {
+namespace netdevice {
+
+/**
+ * A wrapper around AF_NETLINK sockets.
+ *
+ * This class is not thread safe to use a single instance between multiple threads, but it's fine to
+ * use multiple instances over multiple threads.
+ */
+struct NetlinkSocket {
+ NetlinkSocket(int protocol);
+
+ /**
+ * Send Netlink message to Kernel.
+ *
+ * @param msg Message to send, nlmsg_seq will be set to next sequence number
+ * @return true, if succeeded
+ */
+ template <class T, unsigned int BUFSIZE>
+ bool send(NetlinkRequest<T, BUFSIZE>& req) {
+ if (!req.isGood()) return false;
+ return send(req.header());
+ }
+
+ /**
+ * Receive Netlink ACK message from Kernel.
+ *
+ * @return true if received ACK message, false in case of error
+ */
+ bool receiveAck();
+
+ private:
+ uint32_t mSeq = 0;
+ base::unique_fd mFd;
+ bool mFailed = false;
+
+ bool send(struct nlmsghdr* msg);
+
+ DISALLOW_COPY_AND_ASSIGN(NetlinkSocket);
+};
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/can.cpp b/automotive/can/1.0/default/libnetdevice/can.cpp
new file mode 100644
index 0000000..87617dd
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/can.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 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 <libnetdevice/libnetdevice.h>
+
+#include "NetlinkRequest.h"
+#include "NetlinkSocket.h"
+#include "common.h"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <linux/can.h>
+#include <linux/can/netlink.h>
+
+namespace android {
+namespace netdevice {
+namespace can {
+
+base::unique_fd socket(const std::string& ifname) {
+ struct sockaddr_can addr = {};
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = nametoindex(ifname);
+ if (addr.can_ifindex == 0) {
+ LOG(ERROR) << "Interface " << ifname << " doesn't exists";
+ return {};
+ }
+
+ base::unique_fd sock(::socket(PF_CAN, SOCK_RAW, CAN_RAW));
+ if (!sock.ok()) {
+ LOG(ERROR) << "Failed to create CAN socket";
+ return {};
+ }
+
+ if (0 != fcntl(sock.get(), F_SETFL, O_RDWR | O_NONBLOCK)) {
+ LOG(ERROR) << "Couldn't put CAN socket in non-blocking mode";
+ return {};
+ }
+
+ if (0 != bind(sock.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))) {
+ LOG(ERROR) << "Can't bind to CAN interface " << ifname;
+ return {};
+ }
+
+ return sock;
+}
+
+bool setBitrate(std::string ifname, uint32_t bitrate) {
+ struct can_bittiming bt = {};
+ bt.bitrate = bitrate;
+
+ NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
+
+ const auto ifidx = nametoindex(ifname);
+ if (ifidx == 0) {
+ LOG(ERROR) << "Can't find interface " << ifname;
+ return false;
+ }
+ req.data().ifi_index = ifidx;
+
+ {
+ auto linkinfo = req.nest(IFLA_LINKINFO);
+ req.addattr(IFLA_INFO_KIND, "can");
+ {
+ auto infodata = req.nest(IFLA_INFO_DATA);
+ /* For CAN FD, it would require to add IFLA_CAN_DATA_BITTIMING
+ * and IFLA_CAN_CTRLMODE as well. */
+ req.addattr(IFLA_CAN_BITTIMING, bt);
+ }
+ }
+
+ NetlinkSocket sock(NETLINK_ROUTE);
+ return sock.send(req) && sock.receiveAck();
+}
+
+} // namespace can
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/common.cpp b/automotive/can/1.0/default/libnetdevice/common.cpp
new file mode 100644
index 0000000..3deac3e
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/common.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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 "common.h"
+
+#include <android-base/logging.h>
+
+#include <net/if.h>
+
+namespace android {
+namespace netdevice {
+
+unsigned int nametoindex(const std::string& ifname) {
+ const auto ifidx = if_nametoindex(ifname.c_str());
+ if (ifidx != 0) return ifidx;
+
+ const auto err = errno;
+ if (err != ENODEV) {
+ LOG(ERROR) << "if_nametoindex(" << ifname << ") failed: " << err;
+ }
+ return 0;
+}
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/common.h b/automotive/can/1.0/default/libnetdevice/common.h
new file mode 100644
index 0000000..9bdff4d
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/common.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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 <string>
+
+namespace android {
+namespace netdevice {
+
+/**
+ * Returns the index of a given network interface.
+ *
+ * If the syscall to check the index fails with other error than ENODEV, it gets logged and the
+ * return value indicates the interface doesn't exists.
+ *
+ * \param ifname Interface to check
+ * \return Interface index, or 0 if the interface doesn't exist
+ */
+unsigned int nametoindex(const std::string& ifname);
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/can.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/can.h
new file mode 100644
index 0000000..ec3f962
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/can.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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/unique_fd.h>
+
+#include <string>
+
+namespace android {
+namespace netdevice {
+namespace can {
+
+/**
+ * Opens and binds SocketCAN socket.
+ *
+ * @param ifname Interface to open a socket against
+ * @return Socket's FD or -1 in case of failure
+ */
+base::unique_fd socket(const std::string& ifname);
+
+/**
+ * Sets CAN interface bitrate.
+ */
+bool setBitrate(std::string ifname, uint32_t bitrate);
+
+} // namespace can
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
new file mode 100644
index 0000000..33d5de5
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 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 <optional>
+#include <string>
+
+namespace android {
+namespace netdevice {
+
+/**
+ * Checks, if the network interface exists.
+ *
+ * @param ifname Interface to check
+ * @return true if it exists, false otherwise
+ */
+bool exists(std::string ifname);
+
+/**
+ * Checks if network interface is up.
+ *
+ * @param ifname Interface to check
+ * @return true/false if the check succeeded, nullopt otherwise
+ */
+std::optional<bool> isUp(std::string ifname);
+
+/**
+ * Brings network interface up.
+ *
+ * @param ifname Interface to bring up
+ * @return true in case of success, false otherwise
+ */
+bool up(std::string ifname);
+
+/**
+ * Brings network interface down.
+ *
+ * @param ifname Interface to bring down
+ * @return true in case of success, false otherwise
+ */
+bool down(std::string ifname);
+
+/**
+ * Adds virtual link.
+ *
+ * @param dev the name of the new virtual device
+ * @param type the type of the new device
+ * @return true in case of success, false otherwise
+ */
+bool add(std::string dev, std::string type);
+
+/**
+ * Deletes virtual link.
+ *
+ * @param dev the name of the device to remove
+ * @return true in case of success, false otherwise
+ */
+bool del(std::string dev);
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
new file mode 100644
index 0000000..fc2b193
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 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 <libnetdevice/libnetdevice.h>
+
+#include "NetlinkRequest.h"
+#include "NetlinkSocket.h"
+#include "common.h"
+
+#include <android-base/logging.h>
+
+#include <linux/can.h>
+#include <net/if.h>
+
+namespace android {
+namespace netdevice {
+
+bool exists(std::string ifname) {
+ return nametoindex(ifname) != 0;
+}
+
+static bool sendIfreq(unsigned long request, struct ifreq& ifr) {
+ /* For general interfaces it would be socket(AF_INET, SOCK_DGRAM, 0),
+ * but SEPolicy forces us to limit our flexibility here. */
+ base::unique_fd sock(socket(PF_CAN, SOCK_RAW, CAN_RAW));
+ if (!sock.ok()) {
+ LOG(ERROR) << "Can't create socket";
+ return false;
+ }
+
+ if (ioctl(sock.get(), request, &ifr) < 0) {
+ LOG(ERROR) << "ioctl(" << std::hex << request << std::dec << ") failed: " << errno;
+ return false;
+ }
+
+ return true;
+}
+
+static struct ifreq ifreqFromName(const std::string& ifname) {
+ struct ifreq ifr = {};
+ strlcpy(ifr.ifr_name, ifname.c_str(), IF_NAMESIZE);
+ return ifr;
+}
+
+std::optional<bool> isUp(std::string ifname) {
+ struct ifreq ifr = ifreqFromName(ifname);
+ if (!sendIfreq(SIOCGIFFLAGS, ifr)) return std::nullopt;
+ return ifr.ifr_flags & IFF_UP;
+}
+
+bool up(std::string ifname) {
+ struct ifreq ifr = ifreqFromName(ifname);
+ if (!sendIfreq(SIOCGIFFLAGS, ifr)) return false;
+ ifr.ifr_flags |= IFF_UP;
+ return sendIfreq(SIOCSIFFLAGS, ifr);
+}
+
+bool down(std::string ifname) {
+ struct ifreq ifr = ifreqFromName(ifname);
+ if (!sendIfreq(SIOCGIFFLAGS, ifr)) return false;
+ ifr.ifr_flags &= ~IFF_UP;
+ return sendIfreq(SIOCSIFFLAGS, ifr);
+}
+
+bool add(std::string dev, std::string type) {
+ NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+ req.addattr(IFLA_IFNAME, dev);
+
+ {
+ auto linkinfo = req.nest(IFLA_LINKINFO);
+ req.addattr(IFLA_INFO_KIND, type);
+ }
+
+ NetlinkSocket sock(NETLINK_ROUTE);
+ return sock.send(req) && sock.receiveAck();
+}
+
+bool del(std::string dev) {
+ NetlinkRequest<struct ifinfomsg> req(RTM_DELLINK, NLM_F_REQUEST);
+ req.addattr(IFLA_IFNAME, dev);
+
+ NetlinkSocket sock(NETLINK_ROUTE);
+ return sock.send(req) && sock.receiveAck();
+}
+
+} // namespace netdevice
+} // namespace android
diff --git a/automotive/can/1.0/default/service.cpp b/automotive/can/1.0/default/service.cpp
new file mode 100644
index 0000000..7ef44fc
--- /dev/null
+++ b/automotive/can/1.0/default/service.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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 "CanController.h"
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace implementation {
+
+static void canControllerService() {
+ base::SetDefaultTag("CanController");
+ base::SetMinimumLogSeverity(android::base::VERBOSE);
+ configureRpcThreadpool(16, true);
+ LOG(DEBUG) << "CAN controller service starting...";
+
+ CanController canController;
+ if (canController.registerAsService("socketcan") != OK) {
+ LOG(FATAL) << "Failed to register CAN controller";
+ return;
+ }
+
+ LOG(INFO) << "CAN controller service ready";
+ joinRpcThreadpool();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+int main() {
+ ::android::hardware::automotive::can::V1_0::implementation::canControllerService();
+ return 1; // canBusService (joinRpcThreadpool) shouldn't exit
+}
diff --git a/automotive/can/1.0/hidl-utils/Android.bp b/automotive/can/1.0/hidl-utils/Android.bp
new file mode 100644
index 0000000..bc8ff13
--- /dev/null
+++ b/automotive/can/1.0/hidl-utils/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_library_headers {
+ name: "android.hardware.automotive.can@hidl-utils-lib",
+ export_include_dirs: ["include"],
+}
diff --git a/automotive/can/1.0/hidl-utils/include/hidl-utils/hidl-utils.h b/automotive/can/1.0/hidl-utils/include/hidl-utils/hidl-utils.h
new file mode 100644
index 0000000..039f971
--- /dev/null
+++ b/automotive/can/1.0/hidl-utils/include/hidl-utils/hidl-utils.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 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
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace hidl_utils {
+
+/**
+ * Helper functor to fetch results from multi-return HIDL calls.
+ * It's meant to be used in place of _hidl_cb callbacks.
+ *
+ * Please note extracting these return variables outside of the callback scope requires making
+ * a copy of each return variable. This may be costly for frequently called HIDL methods with
+ * non-negligible return object size. Please be cautious about performance when using this.
+ *
+ * Example usage:
+ * Result result;
+ * sp<ISomeInterface> iface;
+ * hidlObject->someMethod(arg1, arg2, hidl_utils::fill(&result, &iface)).assertOk();
+ * // use result and iface
+ */
+template <typename... T>
+struct fill : public std::function<void(const T&...)> {
+ /**
+ * Create _hidl_cb functor that copies the call arguments to specified pointers.
+ *
+ * \param args... Targets to copy the call arguments to
+ */
+ fill(T*... args) : mTargets(args...) {}
+
+ void operator()(const T&... args) { copy<0, T...>(args...); }
+
+ private:
+ std::tuple<T*...> mTargets;
+
+ template <int Pos, typename First>
+ inline void copy(const First& first) {
+ *std::get<Pos>(mTargets) = first;
+ }
+
+ template <int Pos, typename First, typename... Rest>
+ inline void copy(const First& first, const Rest&... rest) {
+ *std::get<Pos>(mTargets) = first;
+ copy<Pos + 1, Rest...>(rest...);
+ }
+};
+
+} // namespace hidl_utils
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/tools/Android.bp b/automotive/can/1.0/tools/Android.bp
new file mode 100644
index 0000000..8c26985
--- /dev/null
+++ b/automotive/can/1.0/tools/Android.bp
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_binary {
+ name: "canhalctrl",
+ defaults: ["android.hardware.automotive.can@defaults"],
+ srcs: [
+ "canhalctrl.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.automotive.can@1.0",
+ "libhidlbase",
+ "libhidltransport",
+ ],
+ header_libs: [
+ "android.hardware.automotive.can@hidl-utils-lib",
+ ],
+}
+
+cc_binary {
+ name: "canhaldump",
+ defaults: ["android.hardware.automotive.can@defaults"],
+ srcs: [
+ "canhaldump.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.automotive.can@1.0",
+ "libhidlbase",
+ "libhidltransport",
+ ],
+ header_libs: [
+ "android.hardware.automotive.can@hidl-utils-lib",
+ ],
+}
+
+cc_binary {
+ name: "canhalsend",
+ defaults: ["android.hardware.automotive.can@defaults"],
+ srcs: [
+ "canhalsend.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.automotive.can@1.0",
+ "libhidlbase",
+ "libhidltransport",
+ ],
+}
diff --git a/automotive/can/1.0/tools/canhalctrl.cpp b/automotive/can/1.0/tools/canhalctrl.cpp
new file mode 100644
index 0000000..fa1048d
--- /dev/null
+++ b/automotive/can/1.0/tools/canhalctrl.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 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 <android-base/logging.h>
+#include <android/hardware/automotive/can/1.0/ICanController.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <hidl-utils/hidl-utils.h>
+
+#include <iostream>
+#include <string>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+
+using ICanController = V1_0::ICanController;
+
+static void usage() {
+ std::cerr << "CAN bus HAL Control tool" << std::endl;
+ std::cerr << std::endl << "usage:" << std::endl << std::endl;
+ std::cerr << "canhalctrl up <bus name> <type> <interface> [baudrate]" << std::endl;
+ std::cerr << "where:" << std::endl;
+ std::cerr << " bus name - name under which ICanBus will be published" << std::endl;
+ std::cerr << " type - one of: virtual, socketcan, slcan, indexed" << std::endl;
+ std::cerr << " interface - hardware identifier (like can0, vcan0, /dev/ttyUSB0)" << std::endl;
+ std::cerr << " baudrate - such as 100000, 125000, 250000, 500000" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "canhalctrl down <bus name>" << std::endl;
+ std::cerr << "where:" << std::endl;
+ std::cerr << " bus name - name under which ICanBus will be published" << std::endl;
+}
+
+static hidl_vec<hidl_string> getControlServices() {
+ auto manager = hidl::manager::V1_2::IServiceManager::getService();
+ hidl_vec<hidl_string> services;
+ manager->listManifestByInterface(ICanController::descriptor, hidl_utils::fill(&services));
+ if (services.size() == 0) {
+ std::cerr << "No ICanController services registered (missing privileges?)" << std::endl;
+ exit(-1);
+ }
+ return services;
+}
+
+static bool isSupported(sp<ICanController> ctrl, ICanController::InterfaceType iftype) {
+ hidl_vec<ICanController::InterfaceType> supported;
+ if (!ctrl->getSupportedInterfaceTypes(hidl_utils::fill(&supported)).isOk()) return false;
+ return supported.contains(iftype);
+}
+
+static int up(const std::string& busName, ICanController::InterfaceType type,
+ const std::string& interface, uint32_t baudrate) {
+ bool anySupported = false;
+ for (auto&& service : getControlServices()) {
+ auto ctrl = ICanController::getService(service);
+ if (ctrl == nullptr) {
+ std::cerr << "Couldn't open ICanController/" << service;
+ continue;
+ }
+
+ if (!isSupported(ctrl, type)) continue;
+ anySupported = true;
+
+ ICanController::BusConfiguration config = {};
+ config.name = busName;
+ config.iftype = type;
+ config.baudrate = baudrate;
+
+ if (type == ICanController::InterfaceType::INDEXED) {
+ config.interfaceId.index(std::stol(interface));
+ } else {
+ config.interfaceId.address(interface);
+ }
+
+ const auto upresult = ctrl->upInterface(config);
+ if (upresult == ICanController::Result::OK) return 0;
+ std::cerr << "Failed to bring interface up: " << toString(upresult) << std::endl;
+ // Let's continue the loop to try other controllers.
+ }
+
+ if (!anySupported) {
+ std::cerr << "No controller supports " << toString(type) << std::endl;
+ }
+ return -1;
+}
+
+static int down(const std::string& busName) {
+ for (auto&& service : getControlServices()) {
+ auto ctrl = ICanController::getService(service);
+ if (ctrl == nullptr) continue;
+
+ if (ctrl->downInterface(busName)) return 0;
+ }
+
+ std::cerr << "Failed to bring interface " << busName << " down (maybe it's down already?)"
+ << std::endl;
+ return -1;
+}
+
+static std::optional<ICanController::InterfaceType> parseInterfaceType(const std::string& str) {
+ if (str == "virtual") return ICanController::InterfaceType::VIRTUAL;
+ if (str == "socketcan") return ICanController::InterfaceType::SOCKETCAN;
+ if (str == "slcan") return ICanController::InterfaceType::SLCAN;
+ if (str == "indexed") return ICanController::InterfaceType::INDEXED;
+ return std::nullopt;
+}
+
+static int main(int argc, char* argv[]) {
+ base::SetDefaultTag("CanHalControl");
+ base::SetMinimumLogSeverity(android::base::VERBOSE);
+
+ if (argc == 0) {
+ usage();
+ return 0;
+ }
+
+ std::string cmd(argv[0]);
+ argv++;
+ argc--;
+
+ if (cmd == "up") {
+ if (argc < 3 || argc > 4) {
+ std::cerr << "Invalid number of arguments to up command: " << argc << std::endl;
+ usage();
+ return -1;
+ }
+
+ const std::string busName(argv[0]);
+ const std::string typeStr(argv[1]);
+ const std::string interface(argv[2]);
+
+ const auto type = parseInterfaceType(typeStr);
+ if (!type) {
+ std::cerr << "Invalid interface type: " << typeStr << std::endl;
+ usage();
+ return -1;
+ }
+
+ long long baudrate = 0;
+ if (argc == 4) {
+ baudrate = std::stoll(argv[3]);
+ }
+
+ return up(busName, *type, interface, baudrate);
+ } else if (cmd == "down") {
+ if (argc != 1) {
+ std::cerr << "Invalid number of arguments to down command: " << argc << std::endl;
+ usage();
+ return -1;
+ }
+
+ return down(argv[0]);
+ } else {
+ std::cerr << "Invalid command: " << cmd << std::endl;
+ usage();
+ return -1;
+ }
+}
+
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+int main(int argc, char* argv[]) {
+ if (argc < 1) return -1;
+ return ::android::hardware::automotive::can::main(--argc, ++argv);
+}
diff --git a/automotive/can/1.0/tools/canhaldump.cpp b/automotive/can/1.0/tools/canhaldump.cpp
new file mode 100644
index 0000000..5713d17
--- /dev/null
+++ b/automotive/can/1.0/tools/canhaldump.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 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 <android-base/logging.h>
+#include <android/hardware/automotive/can/1.0/ICanBus.h>
+#include <android/hardware/automotive/can/1.0/ICanMessageListener.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <hidl-utils/hidl-utils.h>
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+
+using namespace std::chrono_literals;
+
+using ICanBus = V1_0::ICanBus;
+using Result = V1_0::Result;
+
+struct CanMessageListener : public V1_0::ICanMessageListener {
+ const std::string name;
+
+ CanMessageListener(std::string name) : name(name) {}
+
+ virtual Return<void> onReceive(const V1_0::CanMessage& message) {
+ std::cout << " " << name << " " << std::hex << std::uppercase << std::setw(3)
+ << std::setfill('0') << message.id << std::setw(0);
+ if (message.remoteTransmissionRequest) {
+ std::cout << " RTR";
+ } else {
+ std::cout << " [" << message.payload.size() << "] ";
+ for (const auto byte : message.payload) {
+ std::cout << " " << unsigned(byte);
+ }
+ }
+ std::cout << std::nouppercase << std::dec << std::endl;
+ return {};
+ }
+
+ virtual Return<void> onError(V1_0::ErrorEvent error) {
+ std::cout << " " << name << " " << toString(error) << std::endl;
+ return {};
+ }
+};
+
+static void usage() {
+ std::cerr << "canhaldump - dump CAN bus traffic" << std::endl;
+ std::cerr << std::endl << "usage:" << std::endl << std::endl;
+ std::cerr << "canhaldump <bus name>" << std::endl;
+ std::cerr << "where:" << std::endl;
+ std::cerr << " bus name - name under which ICanBus is be published" << std::endl;
+}
+
+// TODO(b/135918744): extract to a new library
+static sp<ICanBus> tryOpen(const std::string& busname) {
+ auto bus = ICanBus::tryGetService(busname);
+ if (bus != nullptr) return bus;
+
+ /* Fallback for interfaces not registered in manifest. For testing purposes only,
+ * one should not depend on this in production deployment. */
+ auto manager = hidl::manager::V1_2::IServiceManager::getService();
+ auto ret = manager->get(ICanBus::descriptor, busname).withDefault(nullptr);
+ if (ret == nullptr) return nullptr;
+
+ std::cerr << "WARNING: bus " << busname << " is not registered in device manifest, "
+ << "trying to fetch it directly..." << std::endl;
+
+ return ICanBus::castFrom(ret);
+}
+
+static int candump(const std::string& busname) {
+ auto bus = tryOpen(busname);
+ if (bus == nullptr) {
+ std::cerr << "Bus " << busname << " is not available" << std::endl;
+ return -1;
+ }
+
+ Result result;
+ sp<V1_0::ICloseHandle> chnd;
+ // TODO(b/135918744): extract to library
+ bus->listen({}, new CanMessageListener(busname), hidl_utils::fill(&result, &chnd)).assertOk();
+
+ if (result != Result::OK) {
+ std::cerr << "Listen call failed: " << toString(result) << std::endl;
+ return -1;
+ }
+
+ while (true) std::this_thread::sleep_for(1h);
+}
+
+static int main(int argc, char* argv[]) {
+ base::SetDefaultTag("CanHalDump");
+ base::SetMinimumLogSeverity(android::base::VERBOSE);
+
+ if (argc == 0) {
+ usage();
+ return 0;
+ }
+
+ if (argc != 1) {
+ std::cerr << "Invalid number of arguments" << std::endl;
+ usage();
+ return -1;
+ }
+
+ return candump(argv[0]);
+}
+
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+int main(int argc, char* argv[]) {
+ if (argc < 1) return -1;
+ return ::android::hardware::automotive::can::main(--argc, ++argv);
+}
diff --git a/automotive/can/1.0/tools/canhalsend.cpp b/automotive/can/1.0/tools/canhalsend.cpp
new file mode 100644
index 0000000..29330c9
--- /dev/null
+++ b/automotive/can/1.0/tools/canhalsend.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 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 <android-base/logging.h>
+#include <android/hardware/automotive/can/1.0/ICanBus.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+
+#include <iostream>
+#include <string>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+
+using ICanBus = V1_0::ICanBus;
+using Result = V1_0::Result;
+
+static void usage() {
+ std::cerr << "cansend - simple command line tool to send raw CAN frames" << std::endl;
+ std::cerr << std::endl << "usage:" << std::endl << std::endl;
+ std::cerr << "canhalsend <bus name> <can id>#<data>" << std::endl;
+ std::cerr << "where:" << std::endl;
+ std::cerr << " bus name - name under which ICanBus is be published" << std::endl;
+ std::cerr << " can id - such as 1a5" << std::endl;
+ std::cerr << " data - such as deadbeef or 010203" << std::endl;
+}
+
+// TODO(b/135918744): extract to a new library
+static sp<ICanBus> tryOpen(const std::string& busname) {
+ auto bus = ICanBus::tryGetService(busname);
+ if (bus != nullptr) return bus;
+
+ /* Fallback for interfaces not registered in manifest. For testing purposes only,
+ * one should not depend on this in production deployment. */
+ auto manager = hidl::manager::V1_2::IServiceManager::getService();
+ auto ret = manager->get(ICanBus::descriptor, busname).withDefault(nullptr);
+ if (ret == nullptr) return nullptr;
+
+ std::cerr << "WARNING: bus " << busname << " is not registered in device manifest, "
+ << "trying to fetch it directly..." << std::endl;
+
+ return ICanBus::castFrom(ret);
+}
+
+static int cansend(const std::string& busname, V1_0::CanMessageId msgid,
+ std::vector<uint8_t> payload) {
+ auto bus = tryOpen(busname);
+ if (bus == nullptr) {
+ std::cerr << "Bus " << busname << " is not available" << std::endl;
+ return -1;
+ }
+
+ V1_0::CanMessage msg = {};
+ msg.id = msgid;
+ msg.payload = payload;
+
+ const auto result = bus->send(msg);
+ if (result != Result::OK) {
+ std::cerr << "Send call failed: " << toString(result) << std::endl;
+ return -1;
+ }
+ return 0;
+}
+
+static std::optional<std::tuple<V1_0::CanMessageId, std::vector<uint8_t>>> parseCanMessage(
+ const std::string& msg) {
+ const auto hashpos = msg.find("#");
+ if (hashpos == std::string::npos) return std::nullopt;
+
+ const std::string msgidStr = msg.substr(0, hashpos);
+ const std::string payloadStr = msg.substr(hashpos + 1);
+
+ size_t idx = 0;
+ V1_0::CanMessageId msgid = std::stoi(msgidStr, &idx, 16);
+ if (msgidStr[idx] != '\0') return std::nullopt;
+
+ std::vector<uint8_t> payload;
+ if (payloadStr.size() % 2 != 0) return std::nullopt;
+ for (size_t i = 0; i < payloadStr.size(); i += 2) {
+ std::string byteStr(payloadStr, i, 2);
+ payload.emplace_back(std::stoi(byteStr, &idx, 16));
+ if (byteStr[idx] != '\0') return std::nullopt;
+ }
+
+ return {{msgid, payload}};
+}
+
+static int main(int argc, char* argv[]) {
+ base::SetDefaultTag("CanHalSend");
+ base::SetMinimumLogSeverity(android::base::VERBOSE);
+
+ if (argc == 0) {
+ usage();
+ return 0;
+ }
+
+ if (argc != 2) {
+ std::cerr << "Invalid number of arguments" << std::endl;
+ usage();
+ return -1;
+ }
+
+ std::string busname(argv[0]);
+ const auto canmsg = parseCanMessage(argv[1]);
+ if (!canmsg) {
+ std::cerr << "Failed to parse CAN message argument" << std::endl;
+ return -1;
+ }
+ const auto [msgid, payload] = *canmsg;
+
+ return cansend(busname, msgid, payload);
+}
+
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+int main(int argc, char* argv[]) {
+ if (argc < 1) return -1;
+ return ::android::hardware::automotive::can::main(--argc, ++argv);
+}
diff --git a/automotive/can/1.0/types.hal b/automotive/can/1.0/types.hal
new file mode 100644
index 0000000..37877c6
--- /dev/null
+++ b/automotive/can/1.0/types.hal
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.automotive.can@1.0;
+
+/**
+ * CAN message ID.
+ *
+ * Does not include any flags like RTR nor ERR, only a plain 11-bit
+ * or 29-bit identifier, as defined in CAN 1.x/2.0x.
+ *
+ * Unused bits must be set to zero.
+ */
+typedef uint32_t CanMessageId;
+
+/**
+ * CAN message being sent or received.
+ */
+struct CanMessage {
+ CanMessageId id;
+
+ /**
+ * CAN message payload, as defined in CAN 1.x and CAN 2.x standards.
+ *
+ * The length of the payload vector directly translates to the length
+ * of the data frame (i.e. includes any padding bytes), so it may be in
+ * a range of:
+ * - 0-8 bytes for standard CAN;
+ * - up to 64 bytes for CAN FD.
+ * ISO TP is not supported directly for now.
+ */
+ vec<uint8_t> payload;
+
+ /**
+ * Time in nanoseconds since boot.
+ *
+ * Ignored for outgoing messages.
+ */
+ uint64_t timestamp;
+
+ /**
+ * A request to proactively pull certain data from other ECU in CAN network.
+ *
+ * For details please refer to CAN standard.
+ *
+ * If this flag is set, payload must be empty.
+ */
+ bool remoteTransmissionRequest;
+};
+
+/**
+ * Single filter rule for CAN messages.
+ *
+ * A filter is satisfied if:
+ * ((receivedId & mask) == (id & mask)) == !inverted
+ *
+ * In order for set of filters to match, at least one non-inverted filters must match (if there is
+ * one) and all inverted filters must match. In other words:
+ * - a single matching non-inverted filter makes the whole set matching;
+ * - a single non-matching inverted filter makes the whole set non-matching.
+ */
+struct CanMessageFilter {
+ CanMessageId id;
+ uint32_t mask;
+ bool inverted;
+};
+
+enum Result : uint8_t {
+ OK,
+ UNKNOWN_ERROR,
+ PAYLOAD_TOO_LONG,
+ INTERFACE_DOWN,
+ TRANSMISSION_FAILURE,
+ INVALID_ARGUMENTS,
+};
+
+/**
+ * @see ICanMessageListener#onError
+ */
+enum ErrorEvent : uint8_t {
+ UNKNOWN_ERROR,
+
+ /** A problem with CAN interface HW. */
+ HARDWARE_ERROR,
+
+ /** TX buffer overflow: client is sending too many packets. */
+ TX_OVERFLOW,
+
+ /** RX buffer overflow: client is not reading packets fast enough. */
+ RX_OVERFLOW,
+
+ /** Received malformed input. */
+ MALFORMED_INPUT,
+
+ /** Bus overload: there is too much traffic on the bus. */
+ BUS_OVERLOAD,
+
+ /** Bus error: shorted Hi/Lo line, bus off etc. */
+ BUS_ERROR,
+};
diff --git a/automotive/can/1.0/vts/functional/Android.bp b/automotive/can/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..b4d9132
--- /dev/null
+++ b/automotive/can/1.0/vts/functional/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_defaults {
+ name: "android.hardware.automotive.can@vts-defaults",
+ defaults: ["VtsHalTargetTestDefaults", "android.hardware.automotive.can@defaults"],
+ header_libs: [
+ "android.hardware.automotive.can@hidl-utils-lib",
+ "android.hardware.automotive.can@vts-utils-lib",
+ ],
+ static_libs: [
+ "android.hardware.automotive.can@1.0",
+ "libgmock",
+ ],
+ test_suites: ["general-tests"],
+}
+
+cc_test {
+ name: "VtsHalCanControllerV1_0TargetTest",
+ defaults: ["android.hardware.automotive.can@vts-defaults"],
+ srcs: ["VtsHalCanControllerV1_0TargetTest.cpp"],
+}
+
+cc_test {
+ name: "VtsHalCanBusV1_0TargetTest",
+ defaults: ["android.hardware.automotive.can@vts-defaults"],
+ srcs: ["VtsHalCanBusV1_0TargetTest.cpp"],
+}
+
+cc_test {
+ name: "VtsHalCanBusVirtualV1_0TargetTest",
+ defaults: ["android.hardware.automotive.can@vts-defaults"],
+ srcs: ["VtsHalCanBusVirtualV1_0TargetTest.cpp"],
+}
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanBusV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanBusV1_0TargetTest.cpp
new file mode 100644
index 0000000..215328e
--- /dev/null
+++ b/automotive/can/1.0/vts/functional/VtsHalCanBusV1_0TargetTest.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 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 <VtsHalHidlTargetTestBase.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/hardware/automotive/can/1.0/ICanBus.h>
+#include <android/hardware/automotive/can/1.0/types.h>
+#include <can-vts-utils/can-hal-printers.h>
+#include <can-vts-utils/environment-utils.h>
+#include <gmock/gmock.h>
+#include <hidl-utils/hidl-utils.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace vts {
+
+using hardware::hidl_vec;
+
+static utils::SimpleHidlEnvironment<ICanBus>* gEnv = nullptr;
+
+struct CanMessageListener : public can::V1_0::ICanMessageListener {
+ virtual Return<void> onReceive(const can::V1_0::CanMessage&) { return {}; }
+ virtual Return<void> onError(can::V1_0::ErrorEvent) { return {}; }
+};
+
+class CanBusHalTest : public ::testing::VtsHalHidlTargetTestBase {
+ protected:
+ virtual void SetUp() override;
+ virtual void TearDown() override;
+
+ std::tuple<Result, sp<ICloseHandle>> listen(const hidl_vec<CanMessageFilter>& filter,
+ const sp<ICanMessageListener>& listener);
+
+ sp<ICanBus> mCanBus;
+};
+
+void CanBusHalTest::SetUp() {
+ const auto serviceName = gEnv->getServiceName<ICanBus>();
+ mCanBus = getService<ICanBus>(serviceName);
+ ASSERT_TRUE(mCanBus) << "Couldn't open CAN Bus: " << serviceName;
+}
+
+void CanBusHalTest::TearDown() {
+ mCanBus.clear();
+}
+
+std::tuple<Result, sp<ICloseHandle>> CanBusHalTest::listen(
+ const hidl_vec<CanMessageFilter>& filter, const sp<ICanMessageListener>& listener) {
+ Result halResult;
+ sp<ICloseHandle> closeHandle;
+ mCanBus->listen(filter, listener, hidl_utils::fill(&halResult, &closeHandle)).assertOk();
+
+ return {halResult, closeHandle};
+}
+
+TEST_F(CanBusHalTest, SendNoPayload) {
+ CanMessage msg = {};
+ msg.id = 0x123;
+
+ const auto result = mCanBus->send(msg);
+ ASSERT_EQ(Result::OK, result);
+}
+
+TEST_F(CanBusHalTest, Send8B) {
+ CanMessage msg = {};
+ msg.id = 0x234;
+ msg.payload = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ const auto result = mCanBus->send(msg);
+ ASSERT_EQ(Result::OK, result);
+}
+
+TEST_F(CanBusHalTest, SendZeroId) {
+ CanMessage msg = {};
+ msg.payload = {1, 2, 3};
+
+ const auto result = mCanBus->send(msg);
+ ASSERT_EQ(Result::OK, result);
+}
+
+TEST_F(CanBusHalTest, SendTooLong) {
+ CanMessage msg = {};
+ msg.id = 0x123;
+ msg.payload = hidl_vec<uint8_t>(102400); // 100kiB
+
+ const auto result = mCanBus->send(msg);
+ ASSERT_EQ(Result::PAYLOAD_TOO_LONG, result);
+}
+
+TEST_F(CanBusHalTest, ListenNoFilter) {
+ const auto [result, closeHandle] = listen({}, new CanMessageListener());
+ ASSERT_EQ(Result::OK, result);
+
+ closeHandle->close().assertOk();
+}
+
+TEST_F(CanBusHalTest, ListenSomeFilter) {
+ hidl_vec<CanMessageFilter> filters = {
+ {0x123, 0x1FF, false},
+ {0x001, 0x00F, true},
+ {0x200, 0x100, false},
+ };
+
+ const auto [result, closeHandle] = listen(filters, new CanMessageListener());
+ ASSERT_EQ(Result::OK, result);
+
+ closeHandle->close().assertOk();
+}
+
+TEST_F(CanBusHalTest, ListenNull) {
+ const auto [result, closeHandle] = listen({}, nullptr);
+ ASSERT_EQ(Result::INVALID_ARGUMENTS, result);
+}
+
+TEST_F(CanBusHalTest, DoubleCloseListen) {
+ const auto [result, closeHandle] = listen({}, new CanMessageListener());
+ ASSERT_EQ(Result::OK, result);
+
+ closeHandle->close().assertOk();
+ closeHandle->close().assertOk();
+}
+
+TEST_F(CanBusHalTest, DontCloseListen) {
+ const auto [result, closeHandle] = listen({}, new CanMessageListener());
+ ASSERT_EQ(Result::OK, result);
+}
+
+} // namespace vts
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+/**
+ * Example manual invocation:
+ * adb shell /data/nativetest64/VtsHalCanBusV1_0TargetTest/VtsHalCanBusV1_0TargetTest \
+ * --hal_service_instance=android.hardware.automotive.can@1.0::ICanBus/test
+ */
+int main(int argc, char** argv) {
+ using android::hardware::automotive::can::V1_0::ICanBus;
+ using android::hardware::automotive::can::V1_0::vts::gEnv;
+ using android::hardware::automotive::can::V1_0::vts::utils::SimpleHidlEnvironment;
+ android::base::SetDefaultTag("CanBusVts");
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ gEnv = new SimpleHidlEnvironment<ICanBus>;
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ gEnv->init(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
new file mode 100644
index 0000000..699c138
--- /dev/null
+++ b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2019 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 <VtsHalHidlTargetTestBase.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/hardware/automotive/can/1.0/ICanBus.h>
+#include <android/hardware/automotive/can/1.0/ICanController.h>
+#include <android/hardware/automotive/can/1.0/types.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <can-vts-utils/can-hal-printers.h>
+#include <can-vts-utils/environment-utils.h>
+#include <gmock/gmock.h>
+#include <hidl-utils/hidl-utils.h>
+#include <utils/Mutex.h>
+#include <utils/SystemClock.h>
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace vts {
+
+using namespace std::chrono_literals;
+
+using hardware::hidl_vec;
+using InterfaceType = ICanController::InterfaceType;
+
+static utils::SimpleHidlEnvironment<ICanController>* gEnv = nullptr;
+
+struct CanMessageListener : public can::V1_0::ICanMessageListener {
+ DISALLOW_COPY_AND_ASSIGN(CanMessageListener);
+
+ CanMessageListener() {}
+
+ virtual Return<void> onReceive(const can::V1_0::CanMessage& msg) {
+ std::unique_lock<std::mutex> lk(mMessagesGuard);
+ mMessages.push_back(msg);
+ mMessagesUpdated.notify_one();
+ return {};
+ }
+
+ virtual Return<void> onError(can::V1_0::ErrorEvent event) {
+ EXPECT_TRUE(false) << "Got error: " << event;
+ return {};
+ }
+
+ virtual ~CanMessageListener() { mCloseHandle->close(); }
+
+ void assignCloseHandle(sp<ICloseHandle> closeHandle) {
+ EXPECT_TRUE(closeHandle);
+ EXPECT_FALSE(mCloseHandle);
+ mCloseHandle = closeHandle;
+ }
+
+ std::vector<can::V1_0::CanMessage> fetchMessages(std::chrono::milliseconds timeout,
+ unsigned atLeast = 1) {
+ std::unique_lock<std::mutex> lk(mMessagesGuard);
+ mMessagesUpdated.wait_for(lk, timeout, [&] { return mMessages.size() >= atLeast; });
+ const auto messages = mMessages;
+ mMessages.clear();
+ return messages;
+ }
+
+ private:
+ sp<ICloseHandle> mCloseHandle;
+
+ std::mutex mMessagesGuard;
+ std::condition_variable mMessagesUpdated GUARDED_BY(mMessagesGuard);
+ std::vector<can::V1_0::CanMessage> mMessages GUARDED_BY(mMessagesGuard);
+};
+
+struct Bus {
+ DISALLOW_COPY_AND_ASSIGN(Bus);
+
+ Bus(sp<ICanController> controller, const ICanController::BusConfiguration& config)
+ : mIfname(config.name), mController(controller) {
+ const auto result = controller->upInterface(config);
+ EXPECT_EQ(ICanController::Result::OK, result);
+
+ /* Not using ICanBus::getService here, since it ignores interfaces not in the manifest
+ * file -- this is a test, so we don't want to add dummy services to a device manifest.
+ */
+ auto manager = hidl::manager::V1_2::IServiceManager::getService();
+ auto service = manager->get(ICanBus::descriptor, config.name);
+ mBus = ICanBus::castFrom(service);
+ EXPECT_TRUE(mBus) << "ICanBus/" << config.name << " failed to register";
+ }
+
+ virtual ~Bus() { reset(); }
+
+ void reset() {
+ mBus.clear();
+ if (mController) {
+ const auto res = mController->downInterface(mIfname);
+ EXPECT_TRUE(res);
+ mController.clear();
+ }
+ }
+
+ ICanBus* operator->() const { return mBus.get(); }
+ sp<ICanBus> get() { return mBus; }
+
+ sp<CanMessageListener> listen(const hidl_vec<CanMessageFilter>& filter) {
+ sp<CanMessageListener> listener = new CanMessageListener();
+
+ Result result;
+ sp<ICloseHandle> closeHandle;
+ mBus->listen(filter, listener, hidl_utils::fill(&result, &closeHandle)).assertOk();
+ EXPECT_EQ(Result::OK, result);
+ listener->assignCloseHandle(closeHandle);
+
+ return listener;
+ }
+
+ void send(const CanMessage& msg) {
+ const auto result = mBus->send(msg);
+ EXPECT_EQ(Result::OK, result);
+ }
+
+ private:
+ const std::string mIfname;
+ sp<ICanController> mController;
+ sp<ICanBus> mBus;
+};
+
+class CanBusVirtualHalTest : public ::testing::VtsHalHidlTargetTestBase {
+ protected:
+ virtual void SetUp() override;
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ Bus makeBus();
+
+ private:
+ unsigned mLastIface = 0;
+ static sp<ICanController> mCanController;
+ static bool mVirtualSupported;
+};
+
+sp<ICanController> CanBusVirtualHalTest::mCanController = nullptr;
+bool CanBusVirtualHalTest::mVirtualSupported;
+
+static CanMessage makeMessage(CanMessageId id) {
+ CanMessage msg = {};
+ msg.id = id;
+ return msg;
+}
+
+static void clearTimestamps(std::vector<CanMessage>& messages) {
+ std::for_each(messages.begin(), messages.end(), [](auto& msg) { msg.timestamp = 0; });
+}
+
+void CanBusVirtualHalTest::SetUp() {
+ if (!mVirtualSupported) GTEST_SKIP();
+}
+
+void CanBusVirtualHalTest::SetUpTestCase() {
+ const auto serviceName = gEnv->getServiceName<ICanController>();
+ mCanController = getService<ICanController>(serviceName);
+ ASSERT_TRUE(mCanController) << "Couldn't open CAN Controller: " << serviceName;
+
+ hidl_vec<InterfaceType> supported;
+ mCanController->getSupportedInterfaceTypes(hidl_utils::fill(&supported)).assertOk();
+ mVirtualSupported = supported.contains(InterfaceType::VIRTUAL);
+}
+
+void CanBusVirtualHalTest::TearDownTestCase() {
+ mCanController.clear();
+}
+
+Bus CanBusVirtualHalTest::makeBus() {
+ const auto idx = ++mLastIface;
+
+ ICanController::BusConfiguration config = {};
+ config.name = "test" + std::to_string(idx);
+ config.iftype = InterfaceType::VIRTUAL;
+ config.interfaceId.address("vcan50");
+
+ return Bus(mCanController, config);
+}
+
+TEST_F(CanBusVirtualHalTest, Send) {
+ auto bus = makeBus();
+
+ CanMessage msg = {};
+ msg.id = 0x123;
+ msg.payload = {1, 2, 3};
+
+ bus.send(msg);
+}
+
+TEST_F(CanBusVirtualHalTest, SendAfterClose) {
+ auto bus = makeBus();
+ auto zombie = bus.get();
+ bus.reset();
+
+ const auto result = zombie->send({});
+ ASSERT_EQ(Result::INTERFACE_DOWN, result);
+}
+
+TEST_F(CanBusVirtualHalTest, SendAndRecv) {
+ auto bus1 = makeBus();
+ auto bus2 = makeBus();
+
+ auto listener = bus2.listen({});
+
+ CanMessage msg = {};
+ msg.id = 0x123;
+ msg.payload = {1, 2, 3};
+ bus1.send(msg);
+
+ auto messages = listener->fetchMessages(100ms);
+ ASSERT_EQ(1u, messages.size());
+ ASSERT_NEAR(uint64_t(elapsedRealtimeNano()), messages[0].timestamp,
+ std::chrono::nanoseconds(100ms).count());
+ clearTimestamps(messages);
+ ASSERT_EQ(msg, messages[0]);
+}
+
+TEST_F(CanBusVirtualHalTest, DownOneOfTwo) {
+ auto bus1 = makeBus();
+ auto bus2 = makeBus();
+
+ bus2.reset();
+
+ bus1.send({});
+}
+
+TEST_F(CanBusVirtualHalTest, Filter) {
+ auto bus1 = makeBus();
+ auto bus2 = makeBus();
+
+ hidl_vec<CanMessageFilter> filterPositive = {
+ {0x101, 0x100, false},
+ {0x010, 0x0F0, false},
+ };
+ auto listenerPositive = bus2.listen(filterPositive);
+
+ hidl_vec<CanMessageFilter> filterNegative = {
+ {0x123, 0x0FF, true},
+ {0x004, 0x00F, true},
+ };
+ auto listenerNegative = bus2.listen(filterNegative);
+
+ bus1.send(makeMessage(0));
+ bus1.send(makeMessage(0x1A0));
+ bus1.send(makeMessage(0x1A1));
+ bus1.send(makeMessage(0x2A0));
+ bus1.send(makeMessage(0x3A0));
+ bus1.send(makeMessage(0x010));
+ bus1.send(makeMessage(0x123));
+ bus1.send(makeMessage(0x023));
+ bus1.send(makeMessage(0x124));
+
+ std::vector<can::V1_0::CanMessage> expectedPositive{
+ makeMessage(0x1A0), //
+ makeMessage(0x1A1), //
+ makeMessage(0x3A0), //
+ makeMessage(0x010), //
+ makeMessage(0x123), //
+ makeMessage(0x124), //
+ };
+ std::vector<can::V1_0::CanMessage> expectedNegative{
+ makeMessage(0), //
+ makeMessage(0x1A0), //
+ makeMessage(0x1A1), //
+ makeMessage(0x2A0), //
+ makeMessage(0x3A0), //
+ makeMessage(0x010), //
+ };
+
+ auto messagesNegative = listenerNegative->fetchMessages(100ms, expectedNegative.size());
+ auto messagesPositive = listenerPositive->fetchMessages(100ms, expectedPositive.size());
+ clearTimestamps(messagesNegative);
+ clearTimestamps(messagesPositive);
+ ASSERT_EQ(expectedNegative, messagesNegative);
+ ASSERT_EQ(expectedPositive, messagesPositive);
+}
+
+} // namespace vts
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+/**
+ * Example manual invocation:
+ * adb shell /data/nativetest64/VtsHalCanBusVirtualV1_0TargetTest/VtsHalCanBusVirtualV1_0TargetTest\
+ * --hal_service_instance=android.hardware.automotive.can@1.0::ICanController/socketcan
+ */
+int main(int argc, char** argv) {
+ using android::hardware::automotive::can::V1_0::ICanController;
+ using android::hardware::automotive::can::V1_0::vts::gEnv;
+ using android::hardware::automotive::can::V1_0::vts::utils::SimpleHidlEnvironment;
+ android::base::SetDefaultTag("CanBusVirtualVts");
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ gEnv = new SimpleHidlEnvironment<ICanController>;
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ gEnv->init(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
new file mode 100644
index 0000000..70f9fe4
--- /dev/null
+++ b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2019 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 <VtsHalHidlTargetTestBase.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/hardware/automotive/can/1.0/ICanBus.h>
+#include <android/hardware/automotive/can/1.0/ICanController.h>
+#include <android/hardware/automotive/can/1.0/types.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <can-vts-utils/can-hal-printers.h>
+#include <can-vts-utils/environment-utils.h>
+#include <gmock/gmock.h>
+#include <hidl-utils/hidl-utils.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace vts {
+
+using hardware::hidl_vec;
+using InterfaceType = ICanController::InterfaceType;
+
+static utils::SimpleHidlEnvironment<ICanController>* gEnv = nullptr;
+
+class CanControllerHalTest : public ::testing::VtsHalHidlTargetTestBase {
+ protected:
+ virtual void SetUp() override;
+ virtual void TearDown() override;
+
+ hidl_vec<InterfaceType> getSupportedInterfaceTypes();
+ bool isSupported(InterfaceType iftype);
+
+ bool up(InterfaceType iftype, const std::string srvname, std::string ifname,
+ ICanController::Result expected);
+ void assertRegistered(const std::string srvname, bool expectRegistered);
+
+ sp<ICanController> mCanController;
+};
+
+void CanControllerHalTest::SetUp() {
+ const auto serviceName = gEnv->getServiceName<ICanController>();
+ mCanController = getService<ICanController>(serviceName);
+ ASSERT_TRUE(mCanController) << "Couldn't open CAN Controller: " << serviceName;
+}
+
+void CanControllerHalTest::TearDown() {
+ mCanController.clear();
+}
+
+hidl_vec<InterfaceType> CanControllerHalTest::getSupportedInterfaceTypes() {
+ hidl_vec<InterfaceType> iftypesResult;
+ mCanController->getSupportedInterfaceTypes(hidl_utils::fill(&iftypesResult)).assertOk();
+ return iftypesResult;
+}
+
+bool CanControllerHalTest::isSupported(InterfaceType iftype) {
+ const auto supported = getSupportedInterfaceTypes();
+ return std::find(supported.begin(), supported.end(), iftype) != supported.end();
+}
+
+bool CanControllerHalTest::up(InterfaceType iftype, std::string srvname, std::string ifname,
+ ICanController::Result expected) {
+ ICanController::BusConfiguration config = {};
+ config.name = srvname;
+ config.iftype = iftype;
+ config.interfaceId.address(ifname);
+
+ const auto upresult = mCanController->upInterface(config);
+
+ if (!isSupported(iftype)) {
+ LOG(INFO) << iftype << " interfaces not supported";
+ EXPECT_EQ(ICanController::Result::NOT_SUPPORTED, upresult);
+ return false;
+ }
+
+ EXPECT_EQ(expected, upresult);
+ return true;
+}
+
+void CanControllerHalTest::assertRegistered(std::string srvname, bool expectRegistered) {
+ /* Not using ICanBus::tryGetService here, since it ignores interfaces not in the manifest
+ * file -- this is a test, so we don't want to add dummy services to a device manifest.
+ */
+ auto manager = hidl::manager::V1_2::IServiceManager::getService();
+ auto busService = manager->get(ICanBus::descriptor, srvname);
+ ASSERT_EQ(expectRegistered, busService.withDefault(nullptr) != nullptr)
+ << "ICanBus/" << srvname << (expectRegistered ? " is not " : " is ") << "registered"
+ << " (should be otherwise)";
+}
+
+TEST_F(CanControllerHalTest, SupportsSomething) {
+ const auto supported = getSupportedInterfaceTypes();
+ ASSERT_GT(supported.size(), 0u);
+}
+
+TEST_F(CanControllerHalTest, BringUpDown) {
+ const std::string name = "dummy";
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::OK)) GTEST_SKIP();
+ assertRegistered(name, true);
+
+ const auto dnresult = mCanController->downInterface(name);
+ ASSERT_TRUE(dnresult);
+
+ assertRegistered(name, false);
+}
+
+TEST_F(CanControllerHalTest, DownDummy) {
+ const auto result = mCanController->downInterface("imnotup");
+ ASSERT_FALSE(result);
+}
+
+TEST_F(CanControllerHalTest, UpTwice) {
+ const std::string name = "dummy";
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::VIRTUAL, name, "vcan72", ICanController::Result::OK)) GTEST_SKIP();
+ assertRegistered(name, true);
+ if (!up(InterfaceType::VIRTUAL, name, "vcan73", ICanController::Result::INVALID_STATE)) {
+ GTEST_SKIP();
+ }
+ assertRegistered(name, true);
+
+ const auto result = mCanController->downInterface(name);
+ ASSERT_TRUE(result);
+ assertRegistered(name, false);
+}
+
+TEST_F(CanControllerHalTest, IdentifierCompatibility) {
+ using IdDisc = ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator;
+ static const std::map<InterfaceType, std::vector<IdDisc>> compatMatrix = {
+ {InterfaceType::VIRTUAL, {IdDisc::address}},
+ {InterfaceType::SOCKETCAN, {IdDisc::address, IdDisc::serialno}},
+ {InterfaceType::SLCAN, {IdDisc::address, IdDisc::serialno}},
+ {InterfaceType::INDEXED, {IdDisc::index}},
+ };
+ static const std::vector<IdDisc> allDisc = {IdDisc::address, IdDisc::index, IdDisc::serialno};
+
+ for (const auto [iftype, supported] : compatMatrix) {
+ for (const auto iddisc : allDisc) {
+ LOG(INFO) << "Compatibility testing: " << iftype << " / " << iddisc;
+
+ ICanController::BusConfiguration config = {};
+ config.name = "compattestsrv";
+ config.iftype = iftype;
+ config.baudrate = 125000;
+
+ // using random-ish addresses, which may not be valid - we can't test the success case
+ if (iddisc == IdDisc::address) {
+ config.interfaceId.address("can0");
+ } else if (iddisc == IdDisc::index) {
+ config.interfaceId.index(0);
+ } else if (iddisc == IdDisc::serialno) {
+ config.interfaceId.serialno({"dummy", "dummier"});
+ }
+
+ const auto upresult = mCanController->upInterface(config);
+
+ if (!isSupported(iftype)) {
+ ASSERT_EQ(ICanController::Result::NOT_SUPPORTED, upresult);
+ continue;
+ }
+ ASSERT_NE(ICanController::Result::NOT_SUPPORTED, upresult);
+
+ bool isSupportedDisc =
+ std::find(supported.begin(), supported.end(), iddisc) != supported.end();
+ if (!isSupportedDisc) {
+ ASSERT_EQ(ICanController::Result::BAD_ADDRESS, upresult);
+ continue;
+ }
+
+ if (upresult == ICanController::Result::OK) {
+ const auto dnresult = mCanController->downInterface(config.name);
+ ASSERT_TRUE(dnresult);
+ continue;
+ }
+ }
+ }
+}
+
+TEST_F(CanControllerHalTest, FailEmptyName) {
+ const std::string name = "";
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::UNKNOWN_ERROR)) {
+ GTEST_SKIP();
+ }
+ assertRegistered(name, false);
+}
+
+TEST_F(CanControllerHalTest, FailBadName) {
+ // 33 characters (name can be at most 32 characters long)
+ const std::string name = "ab012345678901234567890123456789c";
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::UNKNOWN_ERROR)) {
+ GTEST_SKIP();
+ }
+ assertRegistered(name, false);
+}
+
+TEST_F(CanControllerHalTest, FailBadVirtualAddress) {
+ const std::string name = "dummy";
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::VIRTUAL, name, "", ICanController::Result::BAD_ADDRESS)) GTEST_SKIP();
+ assertRegistered(name, false);
+}
+
+TEST_F(CanControllerHalTest, FailBadSocketcanAddress) {
+ const std::string name = "dummy";
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::SOCKETCAN, name, "can87", ICanController::Result::BAD_ADDRESS)) {
+ GTEST_SKIP();
+ }
+ assertRegistered(name, false);
+}
+
+} // namespace vts
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+/**
+ * Example manual invocation:
+ * adb shell /data/nativetest64/VtsHalCanControllerV1_0TargetTest/VtsHalCanControllerV1_0TargetTest\
+ * --hal_service_instance=android.hardware.automotive.can@1.0::ICanController/socketcan
+ */
+int main(int argc, char** argv) {
+ using android::hardware::automotive::can::V1_0::ICanController;
+ using android::hardware::automotive::can::V1_0::vts::gEnv;
+ using android::hardware::automotive::can::V1_0::vts::utils::SimpleHidlEnvironment;
+ android::base::SetDefaultTag("CanControllerVts");
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ gEnv = new SimpleHidlEnvironment<ICanController>;
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ gEnv->init(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/automotive/can/1.0/vts/utils/Android.bp b/automotive/can/1.0/vts/utils/Android.bp
new file mode 100644
index 0000000..e925c8f
--- /dev/null
+++ b/automotive/can/1.0/vts/utils/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_library_headers {
+ name: "android.hardware.automotive.can@vts-utils-lib",
+ export_include_dirs: ["include"],
+}
diff --git a/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h b/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
new file mode 100644
index 0000000..0923998
--- /dev/null
+++ b/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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/ICanController.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+
+/**
+ * Define gTest printer for a given HIDL type, but skip definition for Return<T>.
+ */
+#define DEFINE_CAN_HAL_PRINTER_SIMPLE(T, converter) \
+ std::ostream& operator<<(std::ostream& os, const T& v) { return os << converter(v); }
+
+/**
+ * Define gTest printer for a given HIDL type.
+ */
+#define DEFINE_CAN_HAL_PRINTER(T, converter) \
+ DEFINE_CAN_HAL_PRINTER_SIMPLE(T, converter) \
+ std::ostream& operator<<(std::ostream& os, const Return<T>& v) { return os << converter(v); }
+
+DEFINE_CAN_HAL_PRINTER(CanMessage, toString)
+DEFINE_CAN_HAL_PRINTER(ErrorEvent, toString)
+DEFINE_CAN_HAL_PRINTER_SIMPLE(
+ ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator, int)
+DEFINE_CAN_HAL_PRINTER(ICanController::InterfaceType, toString)
+DEFINE_CAN_HAL_PRINTER(ICanController::Result, toString)
+DEFINE_CAN_HAL_PRINTER(Result, toString)
+
+#undef DEFINE_CAN_HAL_PRINTER
+#undef DEFINE_CAN_HAL_PRINTER_SIMPLE
+
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/can/1.0/vts/utils/include/can-vts-utils/environment-utils.h b/automotive/can/1.0/vts/utils/include/can-vts-utils/environment-utils.h
new file mode 100644
index 0000000..a722dd0
--- /dev/null
+++ b/automotive/can/1.0/vts/utils/include/can-vts-utils/environment-utils.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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 <VtsHalHidlTargetTestEnvBase.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace can {
+namespace V1_0 {
+namespace vts {
+namespace utils {
+
+/**
+ * Simple test environment.
+ *
+ * This is a helper class to instantiate a test environment without boilerplate code for cases where
+ * there is no need to pass more parameters than just HIDL service instance name.
+ *
+ * The class implements registerTestServices() by calling registerTestService() on every HIDL
+ * interface provided as parameter to this template.
+ *
+ * Example usage:
+ * static utils::SimpleHidlEnvironment<IMyService>* gEnv = nullptr;
+ *
+ * void CanBusHalTest::SetUp() {
+ * const auto serviceName = gEnv->getServiceName<IMyService>();
+ * (...)
+ * }
+ *
+ * int main(int argc, char** argv) {
+ * gEnv = new SimpleHidlEnvironment<IMyService>;
+ * ::testing::AddGlobalTestEnvironment(gEnv);
+ * ::testing::InitGoogleTest(&argc, argv);
+ * gEnv->init(&argc, argv);
+ * return RUN_ALL_TESTS();
+ * }
+ *
+ * \param T... HIDL interface names to register for a test service
+ */
+template <typename... T>
+class SimpleHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+ virtual void registerTestServices() override {
+ // Call registerTestService() for every HIDL interface using this template.
+ using expander = int[];
+ (void)expander{0, (registerTestService<T>(), 0)...};
+ }
+};
+
+} // namespace utils
+} // namespace vts
+} // namespace V1_0
+} // namespace can
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal
index e7f6bb7..21ca79e 100644
--- a/automotive/evs/1.1/IEvsCamera.hal
+++ b/automotive/evs/1.1/IEvsCamera.hal
@@ -17,6 +17,7 @@
package android.hardware.automotive.evs@1.1;
import @1.0::IEvsCamera;
+import @1.0::IEvsDisplay;
import @1.0::EvsResult;
import IEvsCameraStream;
@@ -54,4 +55,76 @@
* @return result Return EvsResult::OK if this call is successful.
*/
doneWithFrame_1_1(BufferDesc buffer) generates (EvsResult result);
+
+ /**
+ * Requests to be a master client.
+ *
+ * When multiple clients subscribe to a single camera hardware and one of
+ * them adjusts a camera parameter such as the contrast, it may disturb
+ * other clients' operations. Therefore, the client must call this method
+ * to be a master client. Once it becomes a master, it will be able to
+ * change camera parameters until either it dies or explicitly gives up the
+ * role.
+ *
+ * @return result EvsResult::OK if a master role is granted.
+ * EvsResult::OWNERSHIP_LOST if there is already a
+ * master client.
+ */
+ setMaster() generates (EvsResult result);
+
+ /**
+ * Sets to be a master client forcibly.
+ *
+ * The client, which owns the display, has a high priority and can take over
+ * a master role from other clients without the display.
+ *
+ * @param display IEvsDisplay handle. If a given display is in either
+ * NOT_VISIBLE, VISIBLE_ON_NEXT_FRAME, or VISIBLE state, the
+ * calling client is considered as the high priority client
+ * and therefore allowed to take over a master role from
+ * existing master client.
+ *
+ * @return result EvsResult::OK if a master role is granted.
+ * EvsResult::INVALID_ARG if a given display handle is null
+ * or in valid states.
+ */
+ forceMaster(IEvsDisplay display) generates (EvsResult result);
+
+ /**
+ * Retires from a master client role.
+ *
+ * @return result EvsResult::OK if this call is successful.
+ * EvsResult::INVALID_ARG if the caller client is not a
+ * master client.
+ */
+ unsetMaster() generates (EvsResult result);
+
+ /**
+ * Requests to set a camera parameter.
+ *
+ * @param id The identifier of camera parameter, CameraParam enum.
+ * value A desired parameter value.
+ * @return result EvsResult::OK if it succeeds to set a parameter.
+ * EvsResult::INVALID_ARG if either the request is
+ * not made by a master client, or a requested
+ * parameter is not supported.
+ * EvsResult::UNDERLYING_SERVICE_ERROR if it fails to
+ * program a value by any other reason.
+ * effectiveValue A programmed parameter value. This may differ
+ * from what the client gives if, for example, the
+ * driver does not support a target parameter.
+ */
+ setParameter(CameraParam id, int32_t value)
+ generates (EvsResult result, int32_t effectiveValue);
+
+ /**
+ * Retrieves a value of given camera parameter.
+ *
+ * @param id The identifier of camera parameter, CameraParam enum.
+ * @return result EvsResult::OK if it succeeds to read a parameter.
+ * EvsResult::INVALID_ARG if either a requested parameter is
+ * not supported.
+ * value A value of requested camera parameter.
+ */
+ getParameter(CameraParam id) generates(EvsResult result, int32_t value);
};
diff --git a/automotive/evs/1.1/IEvsCameraStream.hal b/automotive/evs/1.1/IEvsCameraStream.hal
index cd058a5..7c7f832 100644
--- a/automotive/evs/1.1/IEvsCameraStream.hal
+++ b/automotive/evs/1.1/IEvsCameraStream.hal
@@ -19,7 +19,7 @@
import @1.0::IEvsCameraStream;
/**
- * Implemented on client side to receive asynchronous video frame deliveries.
+ * Implemented on client side to receive asynchronous streaming event deliveries.
*/
interface IEvsCameraStream extends @1.0::IEvsCameraStream {
/**
diff --git a/automotive/evs/1.1/default/EvsCamera.cpp b/automotive/evs/1.1/default/EvsCamera.cpp
index 62d9826..2d55566 100644
--- a/automotive/evs/1.1/default/EvsCamera.cpp
+++ b/automotive/evs/1.1/default/EvsCamera.cpp
@@ -258,6 +258,44 @@
}
+Return<EvsResult> EvsCamera::setMaster() {
+ // Default implementation does not expect multiple subscribers and therefore
+ // return a success code always.
+ return EvsResult::OK;
+}
+
+Return<EvsResult> EvsCamera::forceMaster(const sp<IEvsDisplay>& ) {
+ // Default implementation does not expect multiple subscribers and therefore
+ // return a success code always.
+ return EvsResult::OK;
+}
+
+
+Return<EvsResult> EvsCamera::unsetMaster() {
+ // Default implementation does not expect multiple subscribers and therefore
+ // return a success code always.
+ return EvsResult::OK;
+}
+
+
+Return<void> EvsCamera::setParameter(CameraParam id, int32_t value,
+ setParameter_cb _hidl_cb) {
+ // Default implementation does not support this.
+ (void)id;
+ (void)value;
+ _hidl_cb(EvsResult::INVALID_ARG, 0);
+ return Void();
+}
+
+
+Return<void> EvsCamera::getParameter(CameraParam id, getParameter_cb _hidl_cb) {
+ // Default implementation does not support this.
+ (void)id;
+ _hidl_cb(EvsResult::INVALID_ARG, 0);
+ return Void();
+}
+
+
bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
if (bufferCount < 1) {
ALOGE("Ignoring request to set buffer count to zero");
@@ -468,7 +506,9 @@
// If we've been asked to stop, send an event to signal the actual end of stream
EvsEvent event;
- event.info(EvsEventType::STREAM_STOPPED);
+ InfoEventDesc desc = {};
+ desc.aType = InfoEventType::STREAM_STOPPED;
+ event.info(desc);
auto result = mStream->notifyEvent(event);
if (!result.isOk()) {
ALOGE("Error delivering end of stream marker");
diff --git a/automotive/evs/1.1/default/EvsCamera.h b/automotive/evs/1.1/default/EvsCamera.h
index 0982464..47a3164 100644
--- a/automotive/evs/1.1/default/EvsCamera.h
+++ b/automotive/evs/1.1/default/EvsCamera.h
@@ -20,6 +20,7 @@
#include <android/hardware/automotive/evs/1.1/types.h>
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
#include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h>
+#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
#include <ui/GraphicBuffer.h>
#include <thread>
@@ -30,6 +31,7 @@
using IEvsCameraStream_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCameraStream;
using ::android::hardware::automotive::evs::V1_0::EvsResult;
using ::android::hardware::automotive::evs::V1_0::CameraDesc;
+using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
namespace android {
@@ -60,6 +62,12 @@
Return<EvsResult> pauseVideoStream() override;
Return<EvsResult> resumeVideoStream() override;
Return<EvsResult> doneWithFrame_1_1(const BufferDesc_1_1& buffer) override;
+ Return<EvsResult> setMaster() override;
+ Return<EvsResult> forceMaster(const sp<IEvsDisplay>& display) override;
+ Return<EvsResult> unsetMaster() override;
+ Return<void> setParameter(CameraParam id, int32_t value,
+ setParameter_cb _hidl_cb) override;
+ Return<void> getParameter(CameraParam id, getParameter_cb _hidl_cb) override;
// Implementation details
EvsCamera(const char *id);
diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal
index ff6ab4e..2c6b2ed 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -48,9 +48,9 @@
};
/**
- * EVS event types
+ * Types of informative streaming events
*/
-enum EvsEventType : uint32_t {
+enum InfoEventType : uint32_t {
/**
* Video stream is started
*/
@@ -67,6 +67,29 @@
* Timeout happens
*/
TIMEOUT,
+ /**
+ * Camera parameter is changed; payload contains a changed parameter ID and
+ * its value
+ */
+ PARAMETER_CHANGED,
+ /**
+ * Master role has become available
+ */
+ MASTER_RELEASED,
+};
+
+/**
+ * Structure that describes informative events occurred during EVS is streaming
+ */
+struct InfoEventDesc {
+ /**
+ * Type of an informative event
+ */
+ InfoEventType aType;
+ /**
+ * Possible additional information
+ */
+ uint32_t[4] payload;
};
/**
@@ -80,5 +103,74 @@
/**
* General streaming events
*/
- EvsEventType info;
+ InfoEventDesc info;
+};
+
+/**
+ * EVS Camera Parameter
+ */
+enum CameraParam : uint32_t {
+ /**
+ * The brightness of image frames
+ */
+ BRIGHTNESS,
+ /**
+ * The contrast of image frames
+ */
+ CONTRAST,
+ /**
+ * Automatic gain/exposure control
+ */
+ AUTOGAIN,
+ /**
+ * Gain control
+ */
+ GAIN,
+ /**
+ * Mirror the image horizontally
+ */
+ HFLIP,
+ /**
+ * Mirror the image vertically
+ */
+ VFLIP,
+ /**
+ * Automatic Whitebalance
+ */
+ AUTO_WHITE_BALANCE,
+ /**
+ * Manual white balance setting as a color temperature in Kelvin.
+ */
+ WHITE_BALANCE_TEMPERATURE,
+ /**
+ * Image sharpness adjustment
+ */
+ SHARPNESS,
+ /**
+ * Auto Exposure Control modes; auto, manual, shutter priority, or
+ * aperture priority.
+ */
+ AUTO_EXPOSURE,
+ /**
+ * Manual exposure time of the camera
+ */
+ ABSOLUTE_EXPOSURE,
+ /**
+ * When AEC is running in either auto or aperture priority, this parameter
+ * sets whether a frame rate varies.
+ */
+ AUTO_EXPOSURE_PRIORITY,
+ /**
+ * Set the focal point of the camera to the specified position. This
+ * parameter may not be effective when auto focus is enabled.
+ */
+ ABSOLUTE_FOCUS,
+ /**
+ * Enables continuous automatic focus adjustments.
+ */
+ AUTO_FOCUS,
+ /**
+ * Specify the objective lens focal length as an absolute value.
+ */
+ ABSOLUTE_ZOOM,
};
diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.cpp b/automotive/evs/1.1/vts/functional/FrameHandler.cpp
index b7c7f21..1627689 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.cpp
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.cpp
@@ -21,11 +21,14 @@
#include <stdio.h>
#include <string.h>
+#include <chrono>
#include <android/log.h>
#include <cutils/native_handle.h>
#include <ui/GraphicBuffer.h>
+using namespace std::chrono_literals;
+
FrameHandler::FrameHandler(android::sp <IEvsCamera> pCamera, CameraDesc cameraInfo,
android::sp <IEvsDisplay> pDisplay,
BufferControlFlag mode) :
@@ -79,7 +82,7 @@
// Wait until the stream has actually stopped
std::unique_lock<std::mutex> lock(mLock);
if (mRunning) {
- mSignal.wait(lock, [this]() { return !mRunning; });
+ mEventSignal.wait(lock, [this]() { return !mRunning; });
}
}
@@ -110,7 +113,9 @@
void FrameHandler::waitForFrameCount(unsigned frameCount) {
// Wait until we've seen at least the requested number of frames (could be more)
std::unique_lock<std::mutex> lock(mLock);
- mSignal.wait(lock, [this, frameCount](){ return mFramesReceived >= frameCount; });
+ mFrameSignal.wait(lock, [this, frameCount](){
+ return mFramesReceived >= frameCount;
+ });
}
@@ -135,16 +140,21 @@
Return<void> FrameHandler::notifyEvent(const EvsEvent& event) {
// Local flag we use to keep track of when the stream is stopping
- bool timeToStop = false;
-
auto type = event.getDiscriminator();
if (type == EvsEvent::hidl_discriminator::info) {
- if (event.info() == EvsEventType::STREAM_STOPPED) {
+ mLock.lock();
+ mLatestEventDesc = event.info();
+ if (mLatestEventDesc.aType == InfoEventType::STREAM_STOPPED) {
// Signal that the last frame has been received and the stream is stopped
- timeToStop = true;
+ mRunning = false;
+ } else if (mLatestEventDesc.aType == InfoEventType::PARAMETER_CHANGED) {
+ ALOGD("Camera parameter 0x%X is changed to 0x%X",
+ mLatestEventDesc.payload[0], mLatestEventDesc.payload[1]);
} else {
- ALOGD("Received an event 0x%X", event.info());
+ ALOGD("Received an event %s", eventToString(mLatestEventDesc.aType));
}
+ mLock.unlock();
+ mEventSignal.notify_all();
} else {
auto bufDesc = event.buffer();
const AHardwareBuffer_Desc* pDesc =
@@ -207,21 +217,14 @@
mHeldBuffers.push(bufDesc);
}
+ mLock.lock();
+ ++mFramesReceived;
+ mLock.unlock();
+ mFrameSignal.notify_all();
ALOGD("Frame handling complete");
}
-
- // Update our received frame count and notify anybody who cares that things have changed
- mLock.lock();
- if (timeToStop) {
- mRunning = false;
- } else {
- mFramesReceived++;
- }
- mLock.unlock();
- mSignal.notify_all();
-
return Void();
}
@@ -338,3 +341,43 @@
*height = mFrameHeight;
}
}
+
+bool FrameHandler::waitForEvent(const InfoEventType aTargetEvent,
+ InfoEventDesc &eventDesc) {
+ // Wait until we get an expected parameter change event.
+ std::unique_lock<std::mutex> lock(mLock);
+ auto now = std::chrono::system_clock::now();
+ bool result = mEventSignal.wait_until(lock, now + 5s,
+ [this, aTargetEvent, &eventDesc](){
+ bool flag = mLatestEventDesc.aType == aTargetEvent;
+ if (flag) {
+ eventDesc.aType = mLatestEventDesc.aType;
+ eventDesc.payload[0] = mLatestEventDesc.payload[0];
+ eventDesc.payload[1] = mLatestEventDesc.payload[1];
+ }
+
+ return flag;
+ }
+ );
+
+ return !result;
+}
+
+const char *FrameHandler::eventToString(const InfoEventType aType) {
+ switch (aType) {
+ case InfoEventType::STREAM_STARTED:
+ return "STREAM_STARTED";
+ case InfoEventType::STREAM_STOPPED:
+ return "STREAM_STOPPED";
+ case InfoEventType::FRAME_DROPPED:
+ return "FRAME_DROPPED";
+ case InfoEventType::TIMEOUT:
+ return "TIMEOUT";
+ case InfoEventType::PARAMETER_CHANGED:
+ return "PARAMETER_CHANGED";
+ case InfoEventType::MASTER_RELEASED:
+ return "MASTER_RELEASED";
+ default:
+ return "Unknown";
+ }
+}
diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.h b/automotive/evs/1.1/vts/functional/FrameHandler.h
index 49fa736..7f87cb4 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.h
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.h
@@ -67,6 +67,8 @@
bool isRunning();
void waitForFrameCount(unsigned frameCount);
+ bool waitForEvent(const InfoEventType aTargetEvent,
+ InfoEventDesc &eventDesc);
void getFramesCounters(unsigned* received, unsigned* displayed);
void getFrameDimension(unsigned* width, unsigned* height);
@@ -77,6 +79,7 @@
// Local implementation details
bool copyBufferContents(const BufferDesc_1_0& tgtBuffer, const BufferDesc_1_1& srcBuffer);
+ const char *eventToString(const InfoEventType aType);
// Values initialized as startup
android::sp <IEvsCamera> mCamera;
@@ -88,7 +91,8 @@
// we need to protect all member variables that may be modified while we're streaming
// (ie: those below)
std::mutex mLock;
- std::condition_variable mSignal;
+ std::condition_variable mEventSignal;
+ std::condition_variable mFrameSignal;
std::queue<BufferDesc_1_1> mHeldBuffers;
bool mRunning = false;
@@ -96,6 +100,7 @@
unsigned mFramesDisplayed = 0; // Simple counter -- rolls over eventually!
unsigned mFrameWidth = 0;
unsigned mFrameHeight = 0;
+ InfoEventDesc mLatestEventDesc;
};
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 4f7082a..a6e4881 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -124,8 +124,8 @@
sp<IEvsEnumerator> pEnumerator; // Every test needs access to the service
std::vector <CameraDesc> cameraInfo; // Empty unless/until loadCameraList() is called
- bool mIsHwModule; // boolean to tell current module under testing
- // is HW module implementation.
+ bool mIsHwModule; // boolean to tell current module under testing
+ // is HW module implementation.
};
@@ -516,6 +516,551 @@
}
+/*
+ * CameraParameter:
+ * Verify that a client can adjust a camera parameter.
+ */
+TEST_F(EvsHidlTest, CameraParameter) {
+ ALOGI("Starting CameraParameter test");
+
+ // Get the camera list
+ loadCameraList();
+
+ // Test each reported camera
+ for (auto&& cam: cameraInfo) {
+ // Create a camera client
+ sp<IEvsCamera_1_1> pCam =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCam, nullptr);
+
+ // Set up per-client frame receiver objects which will fire up its own thread
+ sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam,
+ nullptr,
+ FrameHandler::eAutoReturn);
+ ASSERT_NE(frameHandler, nullptr);
+
+ // Start the camera's video stream
+ bool startResult = frameHandler->startStream();
+ ASSERT_TRUE(startResult);
+
+ // Ensure the stream starts
+ frameHandler->waitForFrameCount(1);
+
+ // Try to program few parameters
+ EvsResult result = EvsResult::OK;
+ int32_t val0 = 100;
+ int32_t val1 = 0;
+
+ result = pCam->setMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ pCam->setParameter(CameraParam::BRIGHTNESS, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+
+ if (result == EvsResult::OK) {
+ pCam->getParameter(CameraParam::BRIGHTNESS,
+ [&result, &val1](auto status, auto value) {
+ result = status;
+ if (status == EvsResult::OK) {
+ val1 = value;
+ }
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+ ASSERT_EQ(val0, val1) << "Values are not matched.";
+ }
+
+ val0 = 80;
+ val1 = 0;
+ pCam->setParameter(CameraParam::CONTRAST, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+
+ if (result == EvsResult::OK) {
+ pCam->getParameter(CameraParam::CONTRAST,
+ [&result, &val1](auto status, auto value) {
+ result = status;
+ if (status == EvsResult::OK) {
+ val1 = value;
+ }
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+ ASSERT_EQ(val0, val1) << "Values are not matched.";
+ }
+
+ val0 = 300;
+ val1 = 0;
+ pCam->setParameter(CameraParam::ABSOLUTE_ZOOM, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+
+ if (result == EvsResult::OK) {
+ pCam->getParameter(CameraParam::ABSOLUTE_ZOOM,
+ [&result, &val1](auto status, auto value) {
+ result = status;
+ if (status == EvsResult::OK) {
+ val1 = value;
+ }
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+ ASSERT_EQ(val0, val1) << "Values are not matched.";
+ }
+
+ result = pCam->unsetMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ // Shutdown
+ frameHandler->shutdown();
+
+ // Explicitly release the camera
+ pEnumerator->closeCamera(pCam);
+ }
+}
+
+
+/*
+ * CameraMasterRelease
+ * Verify that non-master client gets notified when the master client either
+ * terminates or releases a role.
+ */
+TEST_F(EvsHidlTest, CameraMasterRelease) {
+ ALOGI("Starting CameraMasterRelease test");
+
+ if (mIsHwModule) {
+ // This test is not for HW module implementation.
+ return;
+ }
+
+ // Get the camera list
+ loadCameraList();
+
+ // Test each reported camera
+ for (auto&& cam: cameraInfo) {
+ // Create two camera clients.
+ sp<IEvsCamera_1_1> pCamMaster =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCamMaster, nullptr);
+ sp<IEvsCamera_1_1> pCamNonMaster =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCamNonMaster, nullptr);
+
+ // Set up per-client frame receiver objects which will fire up its own thread
+ sp<FrameHandler> frameHandlerMaster =
+ new FrameHandler(pCamMaster, cam,
+ nullptr,
+ FrameHandler::eAutoReturn);
+ ASSERT_NE(frameHandlerMaster, nullptr);
+ sp<FrameHandler> frameHandlerNonMaster =
+ new FrameHandler(pCamNonMaster, cam,
+ nullptr,
+ FrameHandler::eAutoReturn);
+ ASSERT_NE(frameHandlerNonMaster, nullptr);
+
+ // Set one client as the master
+ EvsResult result = pCamMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ // Try to set another client as the master.
+ result = pCamNonMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST);
+
+ // Start the camera's video stream via a master client.
+ bool startResult = frameHandlerMaster->startStream();
+ ASSERT_TRUE(startResult);
+
+ // Ensure the stream starts
+ frameHandlerMaster->waitForFrameCount(1);
+
+ // Start the camera's video stream via another client
+ startResult = frameHandlerNonMaster->startStream();
+ ASSERT_TRUE(startResult);
+
+ // Ensure the stream starts
+ frameHandlerNonMaster->waitForFrameCount(1);
+
+ // Non-master client expects to receive a master role relesed
+ // notification.
+ InfoEventDesc aNotification = {};
+
+ // Release a master role.
+ pCamMaster->unsetMaster();
+
+ // Verify a change notification.
+ frameHandlerNonMaster->waitForEvent(InfoEventType::MASTER_RELEASED, aNotification);
+ ASSERT_EQ(InfoEventType::MASTER_RELEASED,
+ static_cast<InfoEventType>(aNotification.aType));
+
+ // Non-master becomes a master.
+ result = pCamNonMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ // Previous master client fails to become a master.
+ result = pCamMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST);
+
+ // Closing current master client.
+ frameHandlerNonMaster->shutdown();
+
+ // Verify a change notification.
+ frameHandlerMaster->waitForEvent(InfoEventType::MASTER_RELEASED, aNotification);
+ ASSERT_EQ(InfoEventType::MASTER_RELEASED,
+ static_cast<InfoEventType>(aNotification.aType));
+
+ // Closing another stream.
+ frameHandlerMaster->shutdown();
+
+ // Explicitly release the camera
+ pEnumerator->closeCamera(pCamMaster);
+ pEnumerator->closeCamera(pCamNonMaster);
+ }
+
+
+}
+
+
+/*
+ * MultiCameraParameter:
+ * Verify that master and non-master clients behave as expected when they try to adjust
+ * camera parameters.
+ */
+TEST_F(EvsHidlTest, MultiCameraParameter) {
+ ALOGI("Starting MultiCameraParameter test");
+
+ if (mIsHwModule) {
+ // This test is not for HW module implementation.
+ return;
+ }
+
+ // Get the camera list
+ loadCameraList();
+
+ // Test each reported camera
+ for (auto&& cam: cameraInfo) {
+ // Create two camera clients.
+ sp<IEvsCamera_1_1> pCamMaster =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCamMaster, nullptr);
+ sp<IEvsCamera_1_1> pCamNonMaster =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCamNonMaster, nullptr);
+
+ // Set up per-client frame receiver objects which will fire up its own thread
+ sp<FrameHandler> frameHandlerMaster =
+ new FrameHandler(pCamMaster, cam,
+ nullptr,
+ FrameHandler::eAutoReturn);
+ ASSERT_NE(frameHandlerMaster, nullptr);
+ sp<FrameHandler> frameHandlerNonMaster =
+ new FrameHandler(pCamNonMaster, cam,
+ nullptr,
+ FrameHandler::eAutoReturn);
+ ASSERT_NE(frameHandlerNonMaster, nullptr);
+
+ // Set one client as the master
+ EvsResult result = pCamMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ // Try to set another client as the master.
+ result = pCamNonMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST);
+
+ // Start the camera's video stream via a master client.
+ bool startResult = frameHandlerMaster->startStream();
+ ASSERT_TRUE(startResult);
+
+ // Ensure the stream starts
+ frameHandlerMaster->waitForFrameCount(1);
+
+ // Start the camera's video stream via another client
+ startResult = frameHandlerNonMaster->startStream();
+ ASSERT_TRUE(startResult);
+
+ // Ensure the stream starts
+ frameHandlerNonMaster->waitForFrameCount(1);
+
+ // Try to program CameraParam::BRIGHTNESS
+ int32_t val0 = 100;
+ int32_t val1 = 0;
+
+ pCamMaster->setParameter(CameraParam::BRIGHTNESS, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program
+ result == EvsResult::INVALID_ARG); // Camera parameter is not supported
+
+ // Non-master client expects to receive a parameter change notification
+ // whenever a master client adjusts it.
+ InfoEventDesc aNotification = {};
+
+ pCamMaster->getParameter(CameraParam::BRIGHTNESS,
+ [&result, &val1](auto status, auto value) {
+ result = status;
+ if (status == EvsResult::OK) {
+ val1 = value;
+ }
+ });
+ ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program
+ result == EvsResult::INVALID_ARG); // Camera parameter is not supported
+ if (result == EvsResult::OK) {
+ ASSERT_EQ(val0, val1) << "Values are not matched.";
+
+ // Verify a change notification
+ frameHandlerNonMaster->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification);
+ ASSERT_EQ(InfoEventType::PARAMETER_CHANGED,
+ static_cast<InfoEventType>(aNotification.aType));
+ ASSERT_EQ(CameraParam::BRIGHTNESS,
+ static_cast<CameraParam>(aNotification.payload[0]));
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(aNotification.payload[1]));
+ }
+
+ // Try to program CameraParam::CONTRAST
+ val0 = 80;
+ val1 = 0;
+ pCamMaster->setParameter(CameraParam::CONTRAST, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program
+ result == EvsResult::INVALID_ARG); // Camera parameter is not supported
+
+ if (result == EvsResult::OK) {
+ pCamMaster->getParameter(CameraParam::CONTRAST,
+ [&result, &val1](auto status, auto value) {
+ result = status;
+ if (status == EvsResult::OK) {
+ val1 = value;
+ }
+ });
+ ASSERT_TRUE(result == EvsResult::OK);
+ ASSERT_EQ(val0, val1) << "Values are not matched.";
+
+
+ // Verify a change notification
+ frameHandlerNonMaster->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification);
+ ASSERT_EQ(InfoEventType::PARAMETER_CHANGED,
+ static_cast<InfoEventType>(aNotification.aType));
+ ASSERT_EQ(CameraParam::CONTRAST,
+ static_cast<CameraParam>(aNotification.payload[0]));
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(aNotification.payload[1]));
+ }
+
+ // Try to adjust a parameter via non-master client
+ pCamNonMaster->setParameter(CameraParam::CONTRAST, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::INVALID_ARG);
+
+ // Non-master client attemps to be a master
+ result = pCamNonMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST);
+
+ // Master client retires from a master role
+ result = pCamMaster->unsetMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ // Try to adjust a parameter after being retired
+ pCamMaster->setParameter(CameraParam::BRIGHTNESS, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::INVALID_ARG);
+
+ // Non-master client becomes a master
+ result = pCamNonMaster->setMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ // Try to adjust a parameter via new master client
+ pCamNonMaster->setParameter(CameraParam::BRIGHTNESS, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK || // Succeeded to program
+ result == EvsResult::INVALID_ARG); // Camera parameter is not supported
+
+ // Wait a moment
+ sleep(1);
+
+ // Verify a change notification
+ if (result == EvsResult::OK) {
+ frameHandlerMaster->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification);
+ ASSERT_EQ(static_cast<InfoEventType>(aNotification.aType),
+ InfoEventType::PARAMETER_CHANGED);
+ ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
+ CameraParam::BRIGHTNESS);
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(aNotification.payload[1]));
+ }
+
+ // New master retires from a master role
+ result = pCamNonMaster->unsetMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ // Shutdown
+ frameHandlerMaster->shutdown();
+ frameHandlerNonMaster->shutdown();
+
+ // Explicitly release the camera
+ pEnumerator->closeCamera(pCamMaster);
+ pEnumerator->closeCamera(pCamNonMaster);
+ }
+}
+
+
+/*
+ * HighPriorityCameraClient:
+ * EVS client, which owns the display, is priortized and therefore can take over
+ * a master role from other EVS clients without the display.
+ */
+TEST_F(EvsHidlTest, HighPriorityCameraClient) {
+ ALOGI("Starting HighPriorityCameraClient test");
+
+ // Get the camera list
+ loadCameraList();
+
+ // Request exclusive access to the EVS display
+ sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
+ ASSERT_NE(pDisplay, nullptr);
+
+ // Test each reported camera
+ for (auto&& cam: cameraInfo) {
+ // Create two clients
+ sp<IEvsCamera_1_1> pCam0 =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCam0, nullptr);
+
+ sp<IEvsCamera_1_1> pCam1 =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+ .withDefault(nullptr);
+ ASSERT_NE(pCam1, nullptr);
+
+ // Set up a frame receiver object which will fire up its own thread.
+ sp<FrameHandler> frameHandler0 = new FrameHandler(pCam0, cam,
+ pDisplay,
+ FrameHandler::eAutoReturn);
+ sp<FrameHandler> frameHandler1 = new FrameHandler(pCam1, cam,
+ nullptr,
+ FrameHandler::eAutoReturn);
+
+ // Activate the display
+ pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
+
+ // Start the camera's video stream
+ ASSERT_TRUE(frameHandler0->startStream());
+ ASSERT_TRUE(frameHandler1->startStream());
+
+ // Ensure the stream starts
+ frameHandler0->waitForFrameCount(1);
+ frameHandler1->waitForFrameCount(1);
+
+ // Client 1 becomes a master and programs a brightness.
+ EvsResult result = EvsResult::OK;
+ int32_t val0 = 100;
+ int32_t val1 = 0;
+
+ result = pCam1->setMaster();
+ ASSERT_TRUE(result == EvsResult::OK);
+
+ pCam1->setParameter(CameraParam::BRIGHTNESS, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+
+
+ // Verify a change notification
+ InfoEventDesc aNotification = {};
+ if (result == EvsResult::OK) {
+ bool timeout =
+ frameHandler0->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification);
+ ASSERT_FALSE(timeout) << "Expected event does not arrive";
+ ASSERT_EQ(static_cast<InfoEventType>(aNotification.aType),
+ InfoEventType::PARAMETER_CHANGED);
+ ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
+ CameraParam::BRIGHTNESS);
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(aNotification.payload[1]));
+ }
+
+ // Client 0 steals a master role
+ ASSERT_EQ(EvsResult::OK, pCam0->forceMaster(pDisplay));
+
+ frameHandler1->waitForEvent(InfoEventType::MASTER_RELEASED, aNotification);
+ ASSERT_EQ(static_cast<InfoEventType>(aNotification.aType),
+ InfoEventType::MASTER_RELEASED);
+
+ // Client 0 programs a brightness
+ val0 = 50;
+ val1 = 0;
+ pCam0->setParameter(CameraParam::BRIGHTNESS, val0,
+ [&result, &val1](auto status, auto effectiveValue) {
+ result = status;
+ val1 = effectiveValue;
+ });
+ ASSERT_TRUE(result == EvsResult::OK ||
+ result == EvsResult::INVALID_ARG);
+
+ // Verify a change notification
+ if (result == EvsResult::OK) {
+ bool timeout =
+ frameHandler1->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification);
+ ASSERT_FALSE(timeout) << "Expected event does not arrive";
+ ASSERT_EQ(static_cast<InfoEventType>(aNotification.aType),
+ InfoEventType::PARAMETER_CHANGED);
+ ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
+ CameraParam::BRIGHTNESS);
+ ASSERT_EQ(val1,
+ static_cast<int32_t>(aNotification.payload[1]));
+ }
+
+ // Turn off the display (yes, before the stream stops -- it should be handled)
+ pDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
+
+ // Shut down the streamer
+ frameHandler0->shutdown();
+ frameHandler1->shutdown();
+
+ // Explicitly release the camera
+ pEnumerator->closeCamera(pCam0);
+ pEnumerator->closeCamera(pCam1);
+ }
+
+ // Explicitly release the display
+ pEnumerator->closeDisplay(pDisplay);
+}
+
+
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance());
::testing::InitGoogleTest(&argc, argv);
diff --git a/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
index 94ace45..24b777c 100644
--- a/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
@@ -50,12 +50,18 @@
VehiclePropValue* valueToUpdate = const_cast<VehiclePropValue*>(getValueOrNullLocked(recId));
if (valueToUpdate == nullptr) {
mPropertyValues.insert({ recId, propValue });
- } else {
- valueToUpdate->timestamp = propValue.timestamp;
- valueToUpdate->value = propValue.value;
- if (updateStatus) {
- valueToUpdate->status = propValue.status;
- }
+ return true;
+ }
+
+ // propValue is outdated and drops it.
+ if (valueToUpdate->timestamp > propValue.timestamp) {
+ return false;
+ }
+ // update the propertyValue.
+ valueToUpdate->timestamp = propValue.timestamp;
+ valueToUpdate->value = propValue.value;
+ if (updateStatus) {
+ valueToUpdate->status = propValue.status;
}
return true;
}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 8a008d3..4a42d79 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -85,6 +85,12 @@
0x0666 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
/**
+ * This property is used for test purpose. End to end tests use this property to test set and get
+ * method for MIXED type properties.
+ */
+const int32_t kMixedTypePropertyForTest =
+ 0x1111 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
+/**
* FakeDataCommand enum defines the supported command type for kGenerateFakeDataControllingProperty.
* All those commands can be send independently with each other. And each will override the one sent
* previously.
@@ -592,6 +598,14 @@
{.config =
{
+ .prop = toInt(VehicleProperty::TURN_SIGNAL_STATE),
+ .access = VehiclePropertyAccess::READ,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ },
+ .initialValue = {.int32Values = {toInt(VehicleTurnSignal::NONE)}}},
+
+ {.config =
+ {
.prop = toInt(VehicleProperty::IGNITION_STATE),
.access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
@@ -625,6 +639,14 @@
},
},
+ {.config = {.prop = kMixedTypePropertyForTest,
+ .access = VehiclePropertyAccess::READ_WRITE,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ .configArray = {1, 1, 0, 2, 0, 0, 1, 0, 0}},
+ .initialValue = {.stringValue = "MIXED property",
+ .int32Values = {1 /* indicate TRUE boolean value */, 2, 3},
+ .floatValues = {4.5f}}},
+
{.config = {.prop = toInt(VehicleProperty::DOOR_LOCK),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index ba81a52..b4f1f07 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -455,7 +455,7 @@
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
if (updatedPropValue) {
- updatedPropValue->timestamp = elapsedRealtimeNano();
+ updatedPropValue->timestamp = value.timestamp;
updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus);
auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode;
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 955283e..8c84c0a 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -35,6 +35,21 @@
/**
* Any combination of scalar or vector types. The exact format must be
* provided in the description of the property.
+ *
+ * For vendor MIXED type properties, configArray needs to be formatted in this
+ * structure.
+ * configArray[0], 1 indicates the property has a String value
+ * configArray[1], 1 indicates the property has a Boolean value .
+ * configArray[2], 1 indicates the property has an Integer value.
+ * configArray[3], the number indicates the size of Integer[] in the property.
+ * configArray[4], 1 indicates the property has a Long value.
+ * configArray[5], the number indicates the size of Long[] in the property.
+ * configArray[6], 1 indicates the property has a Float value.
+ * configArray[7], the number indicates the size of Float[] in the property.
+ * configArray[8], the number indicates the size of byte[] in the property.
+ * For example:
+ * {@code configArray = {1, 1, 1, 3, 0, 0, 0, 0, 0}} indicates the property has
+ * a String value, a Boolean value, an Integer value and an array with 3 integers.
*/
MIXED = 0x00e00000,
diff --git a/camera/common/1.0/default/Android.bp b/camera/common/1.0/default/Android.bp
index 3e5c6d7..f4390b2 100644
--- a/camera/common/1.0/default/Android.bp
+++ b/camera/common/1.0/default/Android.bp
@@ -21,6 +21,7 @@
"libcamera_metadata",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"libexif",
],
include_dirs: ["system/media/private/camera/include"],
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index b8c40e9..76f9778 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -27,7 +27,9 @@
using MapperErrorV2 = android::hardware::graphics::mapper::V2_0::Error;
using MapperErrorV3 = android::hardware::graphics::mapper::V3_0::Error;
+using MapperErrorV4 = android::hardware::graphics::mapper::V4_0::Error;
using IMapperV3 = android::hardware::graphics::mapper::V3_0::IMapper;
+using IMapperV4 = android::hardware::graphics::mapper::V4_0::IMapper;
HandleImporter::HandleImporter() : mInitialized(false) {}
@@ -36,6 +38,12 @@
return;
}
+ mMapperV4 = IMapperV4::getService();
+ if (mMapperV4 != nullptr) {
+ mInitialized = true;
+ return;
+ }
+
mMapperV3 = IMapperV3::getService();
if (mMapperV3 != nullptr) {
mInitialized = true;
@@ -53,6 +61,7 @@
}
void HandleImporter::cleanup() {
+ mMapperV4.clear();
mMapperV3.clear();
mMapperV2.clear();
mInitialized = false;
@@ -151,6 +160,10 @@
initializeLocked();
}
+ if (mMapperV4 != nullptr) {
+ return importBufferInternal<IMapperV4, MapperErrorV4>(mMapperV4, handle);
+ }
+
if (mMapperV3 != nullptr) {
return importBufferInternal<IMapperV3, MapperErrorV3>(mMapperV3, handle);
}
@@ -159,7 +172,7 @@
return importBufferInternal<IMapper, MapperErrorV2>(mMapperV2, handle);
}
- ALOGE("%s: mMapperV3 and mMapperV2 are both null!", __FUNCTION__);
+ ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
return false;
}
@@ -169,12 +182,17 @@
}
Mutex::Autolock lock(mLock);
- if (mMapperV3 == nullptr && mMapperV2 == nullptr) {
- ALOGE("%s: mMapperV3 and mMapperV2 are both null!", __FUNCTION__);
+ if (mMapperV4 == nullptr && mMapperV3 == nullptr && mMapperV2 == nullptr) {
+ ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
return;
}
- if (mMapperV3 != nullptr) {
+ if (mMapperV4 != nullptr) {
+ auto ret = mMapperV4->freeBuffer(const_cast<native_handle_t*>(handle));
+ if (!ret.isOk()) {
+ ALOGE("%s: mapper freeBuffer failed: %s", __FUNCTION__, ret.description().c_str());
+ }
+ } else if (mMapperV3 != nullptr) {
auto ret = mMapperV3->freeBuffer(const_cast<native_handle_t*>(handle));
if (!ret.isOk()) {
ALOGE("%s: mapper freeBuffer failed: %s",
@@ -222,14 +240,27 @@
initializeLocked();
}
- if (mMapperV3 == nullptr && mMapperV2 == nullptr) {
- ALOGE("%s: mMapperV3 and mMapperV2 are both null!", __FUNCTION__);
+ if (mMapperV4 == nullptr && mMapperV3 == nullptr && mMapperV2 == nullptr) {
+ ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
return ret;
}
hidl_handle acquireFenceHandle;
auto buffer = const_cast<native_handle_t*>(buf);
- if (mMapperV3 != nullptr) {
+ if (mMapperV4 != nullptr) {
+ IMapperV4::Rect accessRegion{0, 0, static_cast<int>(size), 1};
+ // No need to use bytesPerPixel and bytesPerStride because we are using
+ // an 1-D buffer and accressRegion.
+ mMapperV4->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpPtr, const auto& /*bytesPerPixel*/,
+ const auto& /*bytesPerStride*/) {
+ if (tmpError == MapperErrorV4::NONE) {
+ ret = tmpPtr;
+ } else {
+ ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
+ }
+ });
+ } else if (mMapperV3 != nullptr) {
IMapperV3::Rect accessRegion { 0, 0, static_cast<int>(size), 1 };
// No need to use bytesPerPixel and bytesPerStride because we are using
// an 1-D buffer and accressRegion.
@@ -269,6 +300,10 @@
initializeLocked();
}
+ if (mMapperV4 != nullptr) {
+ return lockYCbCrInternal<IMapperV4, MapperErrorV4>(mMapperV4, buf, cpuUsage, accessRegion);
+ }
+
if (mMapperV3 != nullptr) {
return lockYCbCrInternal<IMapperV3, MapperErrorV3>(
mMapperV3, buf, cpuUsage, accessRegion);
@@ -279,11 +314,14 @@
mMapperV2, buf, cpuUsage, accessRegion);
}
- ALOGE("%s: mMapperV3 and mMapperV2 are both null!", __FUNCTION__);
+ ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
return {};
}
int HandleImporter::unlock(buffer_handle_t& buf) {
+ if (mMapperV4 != nullptr) {
+ return unlockInternal<IMapperV4, MapperErrorV4>(mMapperV4, buf);
+ }
if (mMapperV3 != nullptr) {
return unlockInternal<IMapperV3, MapperErrorV3>(mMapperV3, buf);
}
@@ -291,7 +329,7 @@
return unlockInternal<IMapper, MapperErrorV2>(mMapperV2, buf);
}
- ALOGE("%s: mMapperV3 and mMapperV2 are both null!", __FUNCTION__);
+ ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
return -1;
}
diff --git a/camera/common/1.0/default/include/HandleImporter.h b/camera/common/1.0/default/include/HandleImporter.h
index a93d455..fc2bbd1 100644
--- a/camera/common/1.0/default/include/HandleImporter.h
+++ b/camera/common/1.0/default/include/HandleImporter.h
@@ -17,10 +17,11 @@
#ifndef CAMERA_COMMON_1_0_HANDLEIMPORTED_H
#define CAMERA_COMMON_1_0_HANDLEIMPORTED_H
-#include <utils/Mutex.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
#include <cutils/native_handle.h>
+#include <utils/Mutex.h>
using android::hardware::graphics::mapper::V2_0::IMapper;
using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
@@ -70,6 +71,7 @@
bool mInitialized;
sp<IMapper> mMapperV2;
sp<graphics::mapper::V3_0::IMapper> mMapperV3;
+ sp<graphics::mapper::V4_0::IMapper> mMapperV4;
};
} // namespace helper
diff --git a/camera/device/1.0/default/Android.bp b/camera/device/1.0/default/Android.bp
index aa3b941..c3518d3 100644
--- a/camera/device/1.0/default/Android.bp
+++ b/camera/device/1.0/default/Android.bp
@@ -16,6 +16,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"android.hardware.graphics.common@1.0",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
diff --git a/camera/device/3.2/default/Android.bp b/camera/device/3.2/default/Android.bp
index edb008e..edc2988 100644
--- a/camera/device/3.2/default/Android.bp
+++ b/camera/device/3.2/default/Android.bp
@@ -14,6 +14,7 @@
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"liblog",
"libhardware",
"libcamera_metadata",
diff --git a/camera/device/3.3/default/Android.bp b/camera/device/3.3/default/Android.bp
index 39d379d..f3c2e0e 100644
--- a/camera/device/3.3/default/Android.bp
+++ b/camera/device/3.3/default/Android.bp
@@ -16,6 +16,7 @@
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"liblog",
"libhardware",
"libcamera_metadata",
diff --git a/camera/device/3.4/default/Android.bp b/camera/device/3.4/default/Android.bp
index c22b13c..8e699d8 100644
--- a/camera/device/3.4/default/Android.bp
+++ b/camera/device/3.4/default/Android.bp
@@ -49,6 +49,7 @@
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"liblog",
"libhardware",
"libcamera_metadata",
@@ -86,6 +87,7 @@
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"liblog",
"libhardware",
"libcamera_metadata",
diff --git a/camera/device/3.5/default/Android.bp b/camera/device/3.5/default/Android.bp
index 26b3b67..dde585e 100644
--- a/camera/device/3.5/default/Android.bp
+++ b/camera/device/3.5/default/Android.bp
@@ -50,6 +50,7 @@
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"liblog",
"libhardware",
"libcamera_metadata",
@@ -83,7 +84,8 @@
"android.hardware.camera.device@3.5",
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"liblog",
"libhardware",
"libcamera_metadata",
diff --git a/camera/metadata/3.5/Android.bp b/camera/metadata/3.5/Android.bp
new file mode 100644
index 0000000..4ebd069
--- /dev/null
+++ b/camera/metadata/3.5/Android.bp
@@ -0,0 +1,19 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.camera.metadata@3.5",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ ],
+ interfaces: [
+ "android.hardware.camera.metadata@3.2",
+ "android.hardware.camera.metadata@3.3",
+ "android.hardware.camera.metadata@3.4",
+ ],
+ gen_java: true,
+}
+
diff --git a/camera/metadata/3.5/types.hal b/camera/metadata/3.5/types.hal
new file mode 100644
index 0000000..0fec947
--- /dev/null
+++ b/camera/metadata/3.5/types.hal
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/*
+ * Autogenerated from camera metadata definitions in
+ * /system/media/camera/docs/metadata_definitions.xml
+ * *** DO NOT EDIT BY HAND ***
+ */
+
+package android.hardware.camera.metadata@3.5;
+
+import android.hardware.camera.metadata@3.2;
+import android.hardware.camera.metadata@3.3;
+import android.hardware.camera.metadata@3.4;
+
+// No new metadata sections added in this revision
+
+/*
+ * Enumeration definitions for the various entries that need them
+ */
+
+/** android.request.availableCapabilities enumeration values added since v3.4
+ * @see ANDROID_REQUEST_AVAILABLE_CAPABILITIES
+ */
+enum CameraMetadataEnumAndroidRequestAvailableCapabilities :
+ @3.4::CameraMetadataEnumAndroidRequestAvailableCapabilities {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA,
+};
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index cb78fcb..313b29b 100644
--- a/camera/provider/2.4/default/Android.bp
+++ b/camera/provider/2.4/default/Android.bp
@@ -13,6 +13,7 @@
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"camera.device@1.0-impl",
@@ -52,6 +53,7 @@
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"camera.device@3.3-impl",
@@ -95,6 +97,8 @@
"android.hardware.camera.provider@2.4-external",
"android.hardware.camera.provider@2.4-legacy",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"camera.device@1.0-impl",
@@ -140,6 +144,8 @@
"android.hardware.camera.device@3.5",
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"libbinder",
diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp
index 2c3ed37..5fe7b19 100644
--- a/camera/provider/2.4/vts/functional/Android.bp
+++ b/camera/provider/2.4/vts/functional/Android.bp
@@ -43,9 +43,11 @@
"android.hardware.camera.provider@2.5",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.common@1.0",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"android.hidl.allocator@1.0",
"libgrallocusage",
"libhidlmemory",
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index e0d41ba..67d5bbe 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -55,9 +55,11 @@
#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <android/hardware/graphics/mapper/2.0/types.h>
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMapper.h>
#include <android/hidl/memory/1.0/IMemory.h>
@@ -6213,13 +6215,44 @@
android::hardware::graphics::allocator::V2_0::IAllocator::getService();
sp<android::hardware::graphics::allocator::V3_0::IAllocator> allocatorV3 =
android::hardware::graphics::allocator::V3_0::IAllocator::getService();
+ sp<android::hardware::graphics::allocator::V4_0::IAllocator> allocatorV4 =
+ android::hardware::graphics::allocator::V4_0::IAllocator::getService();
+ sp<android::hardware::graphics::mapper::V4_0::IMapper> mapperV4 =
+ android::hardware::graphics::mapper::V4_0::IMapper::getService();
sp<android::hardware::graphics::mapper::V3_0::IMapper> mapperV3 =
android::hardware::graphics::mapper::V3_0::IMapper::getService();
sp<android::hardware::graphics::mapper::V2_0::IMapper> mapper =
android::hardware::graphics::mapper::V2_0::IMapper::getService();
::android::hardware::hidl_vec<uint32_t> descriptor;
- if (mapperV3 != nullptr && allocatorV3 != nullptr) {
+ if (mapperV4 != nullptr && allocatorV4 != nullptr) {
+ android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo descriptorInfo{};
+ descriptorInfo.width = width;
+ descriptorInfo.height = height;
+ descriptorInfo.layerCount = 1;
+ descriptorInfo.format =
+ static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(format);
+ descriptorInfo.usage = usage;
+
+ auto ret = mapperV4->createDescriptor(
+ descriptorInfo, [&descriptor](android::hardware::graphics::mapper::V4_0::Error err,
+ ::android::hardware::hidl_vec<uint32_t> desc) {
+ ASSERT_EQ(err, android::hardware::graphics::mapper::V4_0::Error::NONE);
+ descriptor = desc;
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ ret = allocatorV4->allocate(
+ descriptor, 1u,
+ [&](android::hardware::graphics::mapper::V4_0::Error err, uint32_t /*stride*/,
+ const ::android::hardware::hidl_vec<::android::hardware::hidl_handle>&
+ buffers) {
+ ASSERT_EQ(android::hardware::graphics::mapper::V4_0::Error::NONE, err);
+ ASSERT_EQ(buffers.size(), 1u);
+ *buffer_handle = buffers[0];
+ });
+ ASSERT_TRUE(ret.isOk());
+ } else if (mapperV3 != nullptr && allocatorV3 != nullptr) {
android::hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo descriptorInfo {};
descriptorInfo.width = width;
descriptorInfo.height = height;
diff --git a/camera/provider/2.5/default/service.cpp b/camera/provider/2.5/default/service.cpp
index 604215d..ec30cbc 100644
--- a/camera/provider/2.5/default/service.cpp
+++ b/camera/provider/2.5/default/service.cpp
@@ -49,8 +49,8 @@
status_t status;
if (kLazyService) {
- auto serviceRegistrar = std::make_shared<::android::hardware::LazyServiceRegistrar>();
- status = serviceRegistrar->registerService(provider, "legacy/0");
+ auto serviceRegistrar = ::android::hardware::LazyServiceRegistrar::getInstance();
+ status = serviceRegistrar.registerService(provider, "legacy/0");
} else {
status = provider->registerAsService("legacy/0");
}
diff --git a/cas/1.0/default/service.cpp b/cas/1.0/default/service.cpp
index 516acfb..754c0c5 100644
--- a/cas/1.0/default/service.cpp
+++ b/cas/1.0/default/service.cpp
@@ -46,8 +46,8 @@
android::sp<IMediaCasService> service = new MediaCasService();
android::status_t status;
if (kLazyService) {
- auto serviceRegistrar = std::make_shared<LazyServiceRegistrar>();
- status = serviceRegistrar->registerService(service);
+ auto serviceRegistrar = LazyServiceRegistrar::getInstance();
+ status = serviceRegistrar.registerService(service);
} else {
status = service->registerAsService();
}
diff --git a/cas/1.1/default/service.cpp b/cas/1.1/default/service.cpp
index 9625303..bf0e159 100644
--- a/cas/1.1/default/service.cpp
+++ b/cas/1.1/default/service.cpp
@@ -46,8 +46,8 @@
android::sp<IMediaCasService> service = new MediaCasService();
android::status_t status;
if (kLazyService) {
- auto serviceRegistrar = std::make_shared<LazyServiceRegistrar>();
- status = serviceRegistrar->registerService(service);
+ auto serviceRegistrar = LazyServiceRegistrar::getInstance();
+ status = serviceRegistrar.registerService(service);
} else {
status = service->registerAsService();
}
diff --git a/graphics/composer/2.1/default/Android.bp b/graphics/composer/2.1/default/Android.bp
index 63accff..c4feae5 100644
--- a/graphics/composer/2.1/default/Android.bp
+++ b/graphics/composer/2.1/default/Android.bp
@@ -9,8 +9,7 @@
],
shared_libs: [
"android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.composer@2.1-resources",
"libbase",
"libcutils",
"libfmq",
diff --git a/graphics/composer/2.1/utils/hal/Android.bp b/graphics/composer/2.1/utils/hal/Android.bp
index 7a501fc..ea3666d 100644
--- a/graphics/composer/2.1/utils/hal/Android.bp
+++ b/graphics/composer/2.1/utils/hal/Android.bp
@@ -19,14 +19,12 @@
vendor_available: true,
shared_libs: [
"android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.composer@2.1-resources",
"libhardware", // TODO remove hwcomposer2.h dependency
],
export_shared_lib_headers: [
"android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.composer@2.1-resources",
"libhardware",
],
header_libs: [
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
index 095189f..47ead41 100644
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
+++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
@@ -27,7 +27,7 @@
#include <android/hardware/graphics/composer/2.1/IComposerClient.h>
#include <composer-hal/2.1/ComposerCommandEngine.h>
#include <composer-hal/2.1/ComposerHal.h>
-#include <composer-hal/2.1/ComposerResources.h>
+#include <composer-resources/2.1/ComposerResources.h>
#include <log/log.h>
namespace android {
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
index d87110a..53b9202 100644
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
+++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerCommandEngine.h
@@ -24,7 +24,7 @@
#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
#include <composer-hal/2.1/ComposerHal.h>
-#include <composer-hal/2.1/ComposerResources.h>
+#include <composer-resources/2.1/ComposerResources.h>
// TODO remove hwcomposer_defs.h dependency
#include <hardware/hwcomposer_defs.h>
#include <log/log.h>
@@ -195,7 +195,7 @@
bool closeFence = true;
const native_handle_t* clientTarget;
- ComposerResources::ReplacedBufferHandle replacedClientTarget;
+ ComposerResources::ReplacedHandle replacedClientTarget(true);
auto err = mResources->getDisplayClientTarget(mCurrentDisplay, slot, useCache, rawHandle,
&clientTarget, &replacedClientTarget);
if (err == Error::NONE) {
@@ -226,7 +226,7 @@
bool closeFence = true;
const native_handle_t* outputBuffer;
- ComposerResources::ReplacedBufferHandle replacedOutputBuffer;
+ ComposerResources::ReplacedHandle replacedOutputBuffer(true);
auto err = mResources->getDisplayOutputBuffer(mCurrentDisplay, slot, useCache, rawhandle,
&outputBuffer, &replacedOutputBuffer);
if (err == Error::NONE) {
@@ -369,7 +369,7 @@
bool closeFence = true;
const native_handle_t* buffer;
- ComposerResources::ReplacedBufferHandle replacedBuffer;
+ ComposerResources::ReplacedHandle replacedBuffer(true);
auto err = mResources->getLayerBuffer(mCurrentDisplay, mCurrentLayer, slot, useCache,
rawHandle, &buffer, &replacedBuffer);
if (err == Error::NONE) {
@@ -489,7 +489,7 @@
auto rawHandle = readHandle();
const native_handle_t* stream;
- ComposerResources::ReplacedStreamHandle replacedStream;
+ ComposerResources::ReplacedHandle replacedStream(false);
auto err = mResources->getLayerSidebandStream(mCurrentDisplay, mCurrentLayer, rawHandle,
&stream, &replacedStream);
if (err == Error::NONE) {
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h
deleted file mode 100644
index 18d184e..0000000
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h
+++ /dev/null
@@ -1,592 +0,0 @@
-/*
- * Copyright 2018 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
-
-#ifndef LOG_TAG
-#warning "ComposerResources.h included without LOG_TAG"
-#endif
-
-#include <memory>
-#include <mutex>
-#include <unordered_map>
-#include <vector>
-
-#include <android/hardware/graphics/mapper/2.0/IMapper.h>
-#include <android/hardware/graphics/mapper/3.0/IMapper.h>
-#include <log/log.h>
-
-namespace android {
-namespace hardware {
-namespace graphics {
-namespace composer {
-namespace V2_1 {
-namespace hal {
-
-// wrapper for IMapper to import buffers and sideband streams
-class ComposerHandleImporter {
- public:
- bool init() {
- mMapper3 = mapper::V3_0::IMapper::getService();
- if (mMapper3) {
- return true;
- }
- ALOGD_IF(!mMapper3, "failed to get mapper 3.0 service, falling back to mapper 2.0");
-
- mMapper2 = mapper::V2_0::IMapper::getService();
- ALOGE_IF(!mMapper2, "failed to get mapper 2.0 service");
-
- return mMapper2 != nullptr;
- }
-
- Error importBuffer(const native_handle_t* rawHandle, const native_handle_t** outBufferHandle) {
- if (!rawHandle || (!rawHandle->numFds && !rawHandle->numInts)) {
- *outBufferHandle = nullptr;
- return Error::NONE;
- }
-
- const native_handle_t* bufferHandle;
- if (mMapper2) {
- mapper::V2_0::Error error;
- mMapper2->importBuffer(
- rawHandle, [&](const auto& tmpError, const auto& tmpBufferHandle) {
- error = tmpError;
- bufferHandle = static_cast<const native_handle_t*>(tmpBufferHandle);
- });
- if (error != mapper::V2_0::Error::NONE) {
- return Error::NO_RESOURCES;
- }
- }
- if (mMapper3) {
- mapper::V3_0::Error error;
- mMapper3->importBuffer(
- rawHandle, [&](const auto& tmpError, const auto& tmpBufferHandle) {
- error = tmpError;
- bufferHandle = static_cast<const native_handle_t*>(tmpBufferHandle);
- });
- if (error != mapper::V3_0::Error::NONE) {
- return Error::NO_RESOURCES;
- }
- }
-
- *outBufferHandle = bufferHandle;
- return Error::NONE;
- }
-
- void freeBuffer(const native_handle_t* bufferHandle) {
- if (bufferHandle) {
- if (mMapper2) {
- mMapper2->freeBuffer(
- static_cast<void*>(const_cast<native_handle_t*>(bufferHandle)));
- } else if (mMapper3) {
- mMapper3->freeBuffer(
- static_cast<void*>(const_cast<native_handle_t*>(bufferHandle)));
- }
- }
- }
-
- Error importStream(const native_handle_t* rawHandle, const native_handle_t** outStreamHandle) {
- const native_handle_t* streamHandle = nullptr;
- if (rawHandle) {
- streamHandle = native_handle_clone(rawHandle);
- if (!streamHandle) {
- return Error::NO_RESOURCES;
- }
- }
-
- *outStreamHandle = streamHandle;
- return Error::NONE;
- }
-
- void freeStream(const native_handle_t* streamHandle) {
- if (streamHandle) {
- native_handle_close(streamHandle);
- native_handle_delete(const_cast<native_handle_t*>(streamHandle));
- }
- }
-
- private:
- sp<mapper::V2_0::IMapper> mMapper2;
- sp<mapper::V3_0::IMapper> mMapper3;
-};
-
-class ComposerHandleCache {
- public:
- enum class HandleType {
- INVALID,
- BUFFER,
- STREAM,
- };
-
- ComposerHandleCache(ComposerHandleImporter& importer, HandleType type, uint32_t cacheSize)
- : mImporter(importer), mHandleType(type), mHandles(cacheSize, nullptr) {}
-
- // must be initialized later with initCache
- ComposerHandleCache(ComposerHandleImporter& importer) : mImporter(importer) {}
-
- ~ComposerHandleCache() {
- switch (mHandleType) {
- case HandleType::BUFFER:
- for (auto handle : mHandles) {
- mImporter.freeBuffer(handle);
- }
- break;
- case HandleType::STREAM:
- for (auto handle : mHandles) {
- mImporter.freeStream(handle);
- }
- break;
- default:
- break;
- }
- }
-
- ComposerHandleCache(const ComposerHandleCache&) = delete;
- ComposerHandleCache& operator=(const ComposerHandleCache&) = delete;
-
- bool initCache(HandleType type, uint32_t cacheSize) {
- // already initialized
- if (mHandleType != HandleType::INVALID) {
- return false;
- }
-
- mHandleType = type;
- mHandles.resize(cacheSize, nullptr);
-
- return true;
- }
-
- Error lookupCache(uint32_t slot, const native_handle_t** outHandle) {
- if (slot >= 0 && slot < mHandles.size()) {
- *outHandle = mHandles[slot];
- return Error::NONE;
- } else {
- return Error::BAD_PARAMETER;
- }
- }
-
- Error updateCache(uint32_t slot, const native_handle_t* handle,
- const native_handle** outReplacedHandle) {
- if (slot >= 0 && slot < mHandles.size()) {
- auto& cachedHandle = mHandles[slot];
- *outReplacedHandle = cachedHandle;
- cachedHandle = handle;
- return Error::NONE;
- } else {
- return Error::BAD_PARAMETER;
- }
- }
-
- // when fromCache is true, look up in the cache; otherwise, update the cache
- Error getHandle(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
- const native_handle_t** outHandle, const native_handle** outReplacedHandle) {
- if (fromCache) {
- *outReplacedHandle = nullptr;
- return lookupCache(slot, outHandle);
- } else {
- *outHandle = inHandle;
- return updateCache(slot, inHandle, outReplacedHandle);
- }
- }
-
- private:
- ComposerHandleImporter& mImporter;
- HandleType mHandleType = HandleType::INVALID;
- std::vector<const native_handle_t*> mHandles;
-};
-
-// layer resource
-class ComposerLayerResource {
- public:
- ComposerLayerResource(ComposerHandleImporter& importer, uint32_t bufferCacheSize)
- : mBufferCache(importer, ComposerHandleCache::HandleType::BUFFER, bufferCacheSize),
- mSidebandStreamCache(importer, ComposerHandleCache::HandleType::STREAM, 1) {}
-
- virtual ~ComposerLayerResource() = default;
-
- Error getBuffer(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
- const native_handle_t** outHandle, const native_handle** outReplacedHandle) {
- return mBufferCache.getHandle(slot, fromCache, inHandle, outHandle, outReplacedHandle);
- }
-
- Error getSidebandStream(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
- const native_handle_t** outHandle,
- const native_handle** outReplacedHandle) {
- return mSidebandStreamCache.getHandle(slot, fromCache, inHandle, outHandle,
- outReplacedHandle);
- }
-
- protected:
- ComposerHandleCache mBufferCache;
- ComposerHandleCache mSidebandStreamCache;
-};
-
-// display resource
-class ComposerDisplayResource {
- public:
- enum class DisplayType {
- PHYSICAL,
- VIRTUAL,
- };
-
- virtual ~ComposerDisplayResource() = default;
-
- ComposerDisplayResource(DisplayType type, ComposerHandleImporter& importer,
- uint32_t outputBufferCacheSize)
- : mType(type),
- mClientTargetCache(importer),
- mOutputBufferCache(importer, ComposerHandleCache::HandleType::BUFFER,
- outputBufferCacheSize),
- mMustValidate(true) {}
-
- bool initClientTargetCache(uint32_t cacheSize) {
- return mClientTargetCache.initCache(ComposerHandleCache::HandleType::BUFFER, cacheSize);
- }
-
- bool isVirtual() const { return mType == DisplayType::VIRTUAL; }
-
- Error getClientTarget(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
- const native_handle_t** outHandle,
- const native_handle** outReplacedHandle) {
- return mClientTargetCache.getHandle(slot, fromCache, inHandle, outHandle,
- outReplacedHandle);
- }
-
- Error getOutputBuffer(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
- const native_handle_t** outHandle,
- const native_handle** outReplacedHandle) {
- return mOutputBufferCache.getHandle(slot, fromCache, inHandle, outHandle,
- outReplacedHandle);
- }
-
- bool addLayer(Layer layer, std::unique_ptr<ComposerLayerResource> layerResource) {
- auto result = mLayerResources.emplace(layer, std::move(layerResource));
- return result.second;
- }
-
- bool removeLayer(Layer layer) { return mLayerResources.erase(layer) > 0; }
-
- ComposerLayerResource* findLayerResource(Layer layer) {
- auto layerIter = mLayerResources.find(layer);
- if (layerIter == mLayerResources.end()) {
- return nullptr;
- }
-
- return layerIter->second.get();
- }
-
- std::vector<Layer> getLayers() const {
- std::vector<Layer> layers;
- layers.reserve(mLayerResources.size());
- for (const auto& layerKey : mLayerResources) {
- layers.push_back(layerKey.first);
- }
- return layers;
- }
-
- void setMustValidateState(bool mustValidate) { mMustValidate = mustValidate; }
-
- bool mustValidate() const { return mMustValidate; }
-
- protected:
- const DisplayType mType;
- ComposerHandleCache mClientTargetCache;
- ComposerHandleCache mOutputBufferCache;
- bool mMustValidate;
-
- std::unordered_map<Layer, std::unique_ptr<ComposerLayerResource>> mLayerResources;
-};
-
-class ComposerResources {
- private:
- template <bool isBuffer>
- class ReplacedHandle;
-
- public:
- static std::unique_ptr<ComposerResources> create() {
- auto resources = std::make_unique<ComposerResources>();
- return resources->init() ? std::move(resources) : nullptr;
- }
-
- ComposerResources() = default;
- virtual ~ComposerResources() = default;
-
- bool init() { return mImporter.init(); }
-
- using RemoveDisplay =
- std::function<void(Display display, bool isVirtual, const std::vector<Layer>& layers)>;
- void clear(RemoveDisplay removeDisplay) {
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- for (const auto& displayKey : mDisplayResources) {
- Display display = displayKey.first;
- const ComposerDisplayResource& displayResource = *displayKey.second;
- removeDisplay(display, displayResource.isVirtual(), displayResource.getLayers());
- }
- mDisplayResources.clear();
- }
-
- Error addPhysicalDisplay(Display display) {
- auto displayResource =
- createDisplayResource(ComposerDisplayResource::DisplayType::PHYSICAL, 0);
-
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- auto result = mDisplayResources.emplace(display, std::move(displayResource));
- return result.second ? Error::NONE : Error::BAD_DISPLAY;
- }
-
- Error addVirtualDisplay(Display display, uint32_t outputBufferCacheSize) {
- auto displayResource = createDisplayResource(ComposerDisplayResource::DisplayType::VIRTUAL,
- outputBufferCacheSize);
-
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- auto result = mDisplayResources.emplace(display, std::move(displayResource));
- return result.second ? Error::NONE : Error::BAD_DISPLAY;
- }
-
- Error removeDisplay(Display display) {
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- return mDisplayResources.erase(display) > 0 ? Error::NONE : Error::BAD_DISPLAY;
- }
-
- Error setDisplayClientTargetCacheSize(Display display, uint32_t clientTargetCacheSize) {
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
- if (!displayResource) {
- return Error::BAD_DISPLAY;
- }
-
- return displayResource->initClientTargetCache(clientTargetCacheSize) ? Error::NONE
- : Error::BAD_PARAMETER;
- }
-
- Error addLayer(Display display, Layer layer, uint32_t bufferCacheSize) {
- auto layerResource = createLayerResource(bufferCacheSize);
-
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
- if (!displayResource) {
- return Error::BAD_DISPLAY;
- }
-
- return displayResource->addLayer(layer, std::move(layerResource)) ? Error::NONE
- : Error::BAD_LAYER;
- }
-
- Error removeLayer(Display display, Layer layer) {
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
- if (!displayResource) {
- return Error::BAD_DISPLAY;
- }
-
- return displayResource->removeLayer(layer) ? Error::NONE : Error::BAD_LAYER;
- }
-
- using ReplacedBufferHandle = ReplacedHandle<true>;
- using ReplacedStreamHandle = ReplacedHandle<false>;
-
- Error getDisplayClientTarget(Display display, uint32_t slot, bool fromCache,
- const native_handle_t* rawHandle,
- const native_handle_t** outBufferHandle,
- ReplacedBufferHandle* outReplacedBuffer) {
- return getHandle<Cache::CLIENT_TARGET>(display, 0, slot, fromCache, rawHandle,
- outBufferHandle, outReplacedBuffer);
- }
-
- Error getDisplayOutputBuffer(Display display, uint32_t slot, bool fromCache,
- const native_handle_t* rawHandle,
- const native_handle_t** outBufferHandle,
- ReplacedBufferHandle* outReplacedBuffer) {
- return getHandle<Cache::OUTPUT_BUFFER>(display, 0, slot, fromCache, rawHandle,
- outBufferHandle, outReplacedBuffer);
- }
-
- Error getLayerBuffer(Display display, Layer layer, uint32_t slot, bool fromCache,
- const native_handle_t* rawHandle, const native_handle_t** outBufferHandle,
- ReplacedBufferHandle* outReplacedBuffer) {
- return getHandle<Cache::LAYER_BUFFER>(display, layer, slot, fromCache, rawHandle,
- outBufferHandle, outReplacedBuffer);
- }
-
- Error getLayerSidebandStream(Display display, Layer layer, const native_handle_t* rawHandle,
- const native_handle_t** outStreamHandle,
- ReplacedStreamHandle* outReplacedStream) {
- return getHandle<Cache::LAYER_SIDEBAND_STREAM>(display, layer, 0, false, rawHandle,
- outStreamHandle, outReplacedStream);
- }
-
- void setDisplayMustValidateState(Display display, bool mustValidate) {
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- auto* displayResource = findDisplayResourceLocked(display);
- if (displayResource) {
- displayResource->setMustValidateState(mustValidate);
- }
- }
-
- bool mustValidateDisplay(Display display) {
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
- auto* displayResource = findDisplayResourceLocked(display);
- if (displayResource) {
- return displayResource->mustValidate();
- }
- return false;
- }
-
- protected:
- virtual std::unique_ptr<ComposerDisplayResource> createDisplayResource(
- ComposerDisplayResource::DisplayType type, uint32_t outputBufferCacheSize) {
- return std::make_unique<ComposerDisplayResource>(type, mImporter, outputBufferCacheSize);
- }
-
- virtual std::unique_ptr<ComposerLayerResource> createLayerResource(uint32_t bufferCacheSize) {
- return std::make_unique<ComposerLayerResource>(mImporter, bufferCacheSize);
- }
-
- ComposerDisplayResource* findDisplayResourceLocked(Display display) {
- auto iter = mDisplayResources.find(display);
- if (iter == mDisplayResources.end()) {
- return nullptr;
- }
- return iter->second.get();
- }
-
- ComposerHandleImporter mImporter;
-
- std::mutex mDisplayResourcesMutex;
- std::unordered_map<Display, std::unique_ptr<ComposerDisplayResource>> mDisplayResources;
-
- private:
- enum class Cache {
- CLIENT_TARGET,
- OUTPUT_BUFFER,
- LAYER_BUFFER,
- LAYER_SIDEBAND_STREAM,
- };
-
- // When a buffer in the cache is replaced by a new one, we must keep it
- // alive until it has been replaced in ComposerHal.
- template <bool isBuffer>
- class ReplacedHandle {
- public:
- ReplacedHandle() = default;
- ReplacedHandle(const ReplacedHandle&) = delete;
- ReplacedHandle& operator=(const ReplacedHandle&) = delete;
-
- ~ReplacedHandle() { reset(); }
-
- void reset(ComposerHandleImporter* importer = nullptr,
- const native_handle_t* handle = nullptr) {
- if (mHandle) {
- if (isBuffer) {
- mImporter->freeBuffer(mHandle);
- } else {
- mImporter->freeStream(mHandle);
- }
- }
-
- mImporter = importer;
- mHandle = handle;
- }
-
- private:
- ComposerHandleImporter* mImporter = nullptr;
- const native_handle_t* mHandle = nullptr;
- };
-
- template <Cache cache, bool isBuffer>
- Error getHandle(Display display, Layer layer, uint32_t slot, bool fromCache,
- const native_handle_t* rawHandle, const native_handle_t** outHandle,
- ReplacedHandle<isBuffer>* outReplacedHandle) {
- Error error;
-
- // import the raw handle (or ignore raw handle when fromCache is true)
- const native_handle_t* importedHandle = nullptr;
- if (!fromCache) {
- error = (isBuffer) ? mImporter.importBuffer(rawHandle, &importedHandle)
- : mImporter.importStream(rawHandle, &importedHandle);
- if (error != Error::NONE) {
- return error;
- }
- }
-
- std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
-
- // find display/layer resource
- const bool needLayerResource =
- (cache == Cache::LAYER_BUFFER || cache == Cache::LAYER_SIDEBAND_STREAM);
- ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
- ComposerLayerResource* layerResource = (displayResource && needLayerResource)
- ? displayResource->findLayerResource(layer)
- : nullptr;
-
- // lookup or update cache
- const native_handle_t* replacedHandle = nullptr;
- if (displayResource && (!needLayerResource || layerResource)) {
- switch (cache) {
- case Cache::CLIENT_TARGET:
- error = displayResource->getClientTarget(slot, fromCache, importedHandle,
- outHandle, &replacedHandle);
- break;
- case Cache::OUTPUT_BUFFER:
- error = displayResource->getOutputBuffer(slot, fromCache, importedHandle,
- outHandle, &replacedHandle);
- break;
- case Cache::LAYER_BUFFER:
- error = layerResource->getBuffer(slot, fromCache, importedHandle, outHandle,
- &replacedHandle);
- break;
- case Cache::LAYER_SIDEBAND_STREAM:
- error = layerResource->getSidebandStream(slot, fromCache, importedHandle,
- outHandle, &replacedHandle);
- break;
- default:
- error = Error::BAD_PARAMETER;
- break;
- }
-
- if (error != Error::NONE) {
- ALOGW("invalid cache %d slot %d", int(cache), int(slot));
- }
- } else if (!displayResource) {
- error = Error::BAD_DISPLAY;
- } else {
- error = Error::BAD_LAYER;
- }
-
- // clean up on errors
- if (error != Error::NONE) {
- if (!fromCache) {
- if (isBuffer) {
- mImporter.freeBuffer(importedHandle);
- } else {
- mImporter.freeStream(importedHandle);
- }
- }
- return error;
- }
-
- outReplacedHandle->reset(&mImporter, replacedHandle);
-
- return Error::NONE;
- }
-};
-
-} // namespace hal
-} // namespace V2_1
-} // namespace composer
-} // namespace graphics
-} // namespace hardware
-} // namespace android
diff --git a/graphics/composer/2.1/utils/resources/Android.bp b/graphics/composer/2.1/utils/resources/Android.bp
new file mode 100644
index 0000000..ed827fe
--- /dev/null
+++ b/graphics/composer/2.1/utils/resources/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2019 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.
+
+cc_library_shared {
+ name: "android.hardware.graphics.composer@2.1-resources",
+ defaults: ["hidl_defaults"],
+ vendor_available: true,
+ shared_libs: [
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
+ "libcutils",
+ "libhardware", // TODO remove hwcomposer2.h dependency
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+ header_libs: [
+ "android.hardware.graphics.composer@2.1-command-buffer",
+ ],
+ export_header_lib_headers: [
+ "android.hardware.graphics.composer@2.1-command-buffer",
+ ],
+ export_include_dirs: ["include"],
+ srcs: [
+ "ComposerResources.cpp",
+ ],
+}
diff --git a/graphics/composer/2.1/utils/resources/ComposerResources.cpp b/graphics/composer/2.1/utils/resources/ComposerResources.cpp
new file mode 100644
index 0000000..21f6035
--- /dev/null
+++ b/graphics/composer/2.1/utils/resources/ComposerResources.cpp
@@ -0,0 +1,503 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#define LOG_TAG "ComposerResources"
+
+#include "composer-resources/2.1/ComposerResources.h"
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_1 {
+namespace hal {
+
+bool ComposerHandleImporter::init() {
+ mMapper4 = mapper::V4_0::IMapper::getService();
+ if (mMapper4) {
+ return true;
+ }
+ ALOGI_IF(!mMapper4, "failed to get mapper 4.0 service, falling back to mapper 3.0");
+
+ mMapper3 = mapper::V3_0::IMapper::getService();
+ if (mMapper3) {
+ return true;
+ }
+ ALOGI_IF(!mMapper3, "failed to get mapper 3.0 service, falling back to mapper 2.0");
+
+ mMapper2 = mapper::V2_0::IMapper::getService();
+ ALOGE_IF(!mMapper2, "failed to get mapper 2.0 service");
+
+ return mMapper2 != nullptr;
+}
+
+Error ComposerHandleImporter::importBuffer(const native_handle_t* rawHandle,
+ const native_handle_t** outBufferHandle) {
+ if (!rawHandle || (!rawHandle->numFds && !rawHandle->numInts)) {
+ *outBufferHandle = nullptr;
+ return Error::NONE;
+ }
+
+ const native_handle_t* bufferHandle;
+ if (mMapper2) {
+ mapper::V2_0::Error error;
+ mMapper2->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBufferHandle) {
+ error = tmpError;
+ bufferHandle = static_cast<const native_handle_t*>(tmpBufferHandle);
+ });
+ if (error != mapper::V2_0::Error::NONE) {
+ return Error::NO_RESOURCES;
+ }
+ }
+ if (mMapper3) {
+ mapper::V3_0::Error error;
+ mMapper3->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBufferHandle) {
+ error = tmpError;
+ bufferHandle = static_cast<const native_handle_t*>(tmpBufferHandle);
+ });
+ if (error != mapper::V3_0::Error::NONE) {
+ return Error::NO_RESOURCES;
+ }
+ }
+ if (mMapper4) {
+ mapper::V4_0::Error error;
+ mMapper4->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBufferHandle) {
+ error = tmpError;
+ bufferHandle = static_cast<const native_handle_t*>(tmpBufferHandle);
+ });
+ if (error != mapper::V4_0::Error::NONE) {
+ return Error::NO_RESOURCES;
+ }
+ }
+
+ *outBufferHandle = bufferHandle;
+ return Error::NONE;
+}
+
+void ComposerHandleImporter::freeBuffer(const native_handle_t* bufferHandle) {
+ if (bufferHandle) {
+ if (mMapper2) {
+ mMapper2->freeBuffer(static_cast<void*>(const_cast<native_handle_t*>(bufferHandle)));
+ } else if (mMapper3) {
+ mMapper3->freeBuffer(static_cast<void*>(const_cast<native_handle_t*>(bufferHandle)));
+ } else if (mMapper4) {
+ mMapper4->freeBuffer(static_cast<void*>(const_cast<native_handle_t*>(bufferHandle)));
+ }
+ }
+}
+
+Error ComposerHandleImporter::importStream(const native_handle_t* rawHandle,
+ const native_handle_t** outStreamHandle) {
+ const native_handle_t* streamHandle = nullptr;
+ if (rawHandle) {
+ streamHandle = native_handle_clone(rawHandle);
+ if (!streamHandle) {
+ return Error::NO_RESOURCES;
+ }
+ }
+
+ *outStreamHandle = streamHandle;
+ return Error::NONE;
+}
+
+void ComposerHandleImporter::freeStream(const native_handle_t* streamHandle) {
+ if (streamHandle) {
+ native_handle_close(streamHandle);
+ native_handle_delete(const_cast<native_handle_t*>(streamHandle));
+ }
+}
+
+ComposerHandleCache::ComposerHandleCache(ComposerHandleImporter& importer, HandleType type,
+ uint32_t cacheSize)
+ : mImporter(importer), mHandleType(type), mHandles(cacheSize, nullptr) {}
+
+// must be initialized later with initCache
+ComposerHandleCache::ComposerHandleCache(ComposerHandleImporter& importer) : mImporter(importer) {}
+
+ComposerHandleCache::~ComposerHandleCache() {
+ switch (mHandleType) {
+ case HandleType::BUFFER:
+ for (auto handle : mHandles) {
+ mImporter.freeBuffer(handle);
+ }
+ break;
+ case HandleType::STREAM:
+ for (auto handle : mHandles) {
+ mImporter.freeStream(handle);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+bool ComposerHandleCache::initCache(HandleType type, uint32_t cacheSize) {
+ // already initialized
+ if (mHandleType != HandleType::INVALID) {
+ return false;
+ }
+
+ mHandleType = type;
+ mHandles.resize(cacheSize, nullptr);
+
+ return true;
+}
+
+Error ComposerHandleCache::lookupCache(uint32_t slot, const native_handle_t** outHandle) {
+ if (slot >= 0 && slot < mHandles.size()) {
+ *outHandle = mHandles[slot];
+ return Error::NONE;
+ } else {
+ return Error::BAD_PARAMETER;
+ }
+}
+
+Error ComposerHandleCache::updateCache(uint32_t slot, const native_handle_t* handle,
+ const native_handle** outReplacedHandle) {
+ if (slot >= 0 && slot < mHandles.size()) {
+ auto& cachedHandle = mHandles[slot];
+ *outReplacedHandle = cachedHandle;
+ cachedHandle = handle;
+ return Error::NONE;
+ } else {
+ return Error::BAD_PARAMETER;
+ }
+}
+
+// when fromCache is true, look up in the cache; otherwise, update the cache
+Error ComposerHandleCache::getHandle(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle) {
+ if (fromCache) {
+ *outReplacedHandle = nullptr;
+ return lookupCache(slot, outHandle);
+ } else {
+ *outHandle = inHandle;
+ return updateCache(slot, inHandle, outReplacedHandle);
+ }
+}
+
+ComposerLayerResource::ComposerLayerResource(ComposerHandleImporter& importer,
+ uint32_t bufferCacheSize)
+ : mBufferCache(importer, ComposerHandleCache::HandleType::BUFFER, bufferCacheSize),
+ mSidebandStreamCache(importer, ComposerHandleCache::HandleType::STREAM, 1) {}
+
+Error ComposerLayerResource::getBuffer(uint32_t slot, bool fromCache,
+ const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle) {
+ return mBufferCache.getHandle(slot, fromCache, inHandle, outHandle, outReplacedHandle);
+}
+
+Error ComposerLayerResource::getSidebandStream(uint32_t slot, bool fromCache,
+ const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle) {
+ return mSidebandStreamCache.getHandle(slot, fromCache, inHandle, outHandle, outReplacedHandle);
+}
+
+ComposerDisplayResource::ComposerDisplayResource(DisplayType type, ComposerHandleImporter& importer,
+ uint32_t outputBufferCacheSize)
+ : mType(type),
+ mClientTargetCache(importer),
+ mOutputBufferCache(importer, ComposerHandleCache::HandleType::BUFFER, outputBufferCacheSize),
+ mMustValidate(true) {}
+
+bool ComposerDisplayResource::initClientTargetCache(uint32_t cacheSize) {
+ return mClientTargetCache.initCache(ComposerHandleCache::HandleType::BUFFER, cacheSize);
+}
+
+bool ComposerDisplayResource::isVirtual() const {
+ return mType == DisplayType::VIRTUAL;
+}
+
+Error ComposerDisplayResource::getClientTarget(uint32_t slot, bool fromCache,
+ const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle) {
+ return mClientTargetCache.getHandle(slot, fromCache, inHandle, outHandle, outReplacedHandle);
+}
+
+Error ComposerDisplayResource::getOutputBuffer(uint32_t slot, bool fromCache,
+ const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle) {
+ return mOutputBufferCache.getHandle(slot, fromCache, inHandle, outHandle, outReplacedHandle);
+}
+
+bool ComposerDisplayResource::addLayer(Layer layer,
+ std::unique_ptr<ComposerLayerResource> layerResource) {
+ auto result = mLayerResources.emplace(layer, std::move(layerResource));
+ return result.second;
+}
+
+bool ComposerDisplayResource::removeLayer(Layer layer) {
+ return mLayerResources.erase(layer) > 0;
+}
+
+ComposerLayerResource* ComposerDisplayResource::findLayerResource(Layer layer) {
+ auto layerIter = mLayerResources.find(layer);
+ if (layerIter == mLayerResources.end()) {
+ return nullptr;
+ }
+
+ return layerIter->second.get();
+}
+
+std::vector<Layer> ComposerDisplayResource::getLayers() const {
+ std::vector<Layer> layers;
+ layers.reserve(mLayerResources.size());
+ for (const auto& layerKey : mLayerResources) {
+ layers.push_back(layerKey.first);
+ }
+ return layers;
+}
+
+void ComposerDisplayResource::setMustValidateState(bool mustValidate) {
+ mMustValidate = mustValidate;
+}
+
+bool ComposerDisplayResource::mustValidate() const {
+ return mMustValidate;
+}
+
+std::unique_ptr<ComposerResources> ComposerResources::create() {
+ auto resources = std::make_unique<ComposerResources>();
+ return resources->init() ? std::move(resources) : nullptr;
+}
+
+bool ComposerResources::init() {
+ return mImporter.init();
+}
+
+void ComposerResources::clear(RemoveDisplay removeDisplay) {
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ for (const auto& displayKey : mDisplayResources) {
+ Display display = displayKey.first;
+ const ComposerDisplayResource& displayResource = *displayKey.second;
+ removeDisplay(display, displayResource.isVirtual(), displayResource.getLayers());
+ }
+ mDisplayResources.clear();
+}
+
+Error ComposerResources::addPhysicalDisplay(Display display) {
+ auto displayResource = createDisplayResource(ComposerDisplayResource::DisplayType::PHYSICAL, 0);
+
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ auto result = mDisplayResources.emplace(display, std::move(displayResource));
+ return result.second ? Error::NONE : Error::BAD_DISPLAY;
+}
+
+Error ComposerResources::addVirtualDisplay(Display display, uint32_t outputBufferCacheSize) {
+ auto displayResource = createDisplayResource(ComposerDisplayResource::DisplayType::VIRTUAL,
+ outputBufferCacheSize);
+
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ auto result = mDisplayResources.emplace(display, std::move(displayResource));
+ return result.second ? Error::NONE : Error::BAD_DISPLAY;
+}
+
+Error ComposerResources::removeDisplay(Display display) {
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ return mDisplayResources.erase(display) > 0 ? Error::NONE : Error::BAD_DISPLAY;
+}
+
+Error ComposerResources::setDisplayClientTargetCacheSize(Display display,
+ uint32_t clientTargetCacheSize) {
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
+ if (!displayResource) {
+ return Error::BAD_DISPLAY;
+ }
+
+ return displayResource->initClientTargetCache(clientTargetCacheSize) ? Error::NONE
+ : Error::BAD_PARAMETER;
+}
+
+Error ComposerResources::addLayer(Display display, Layer layer, uint32_t bufferCacheSize) {
+ auto layerResource = createLayerResource(bufferCacheSize);
+
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
+ if (!displayResource) {
+ return Error::BAD_DISPLAY;
+ }
+
+ return displayResource->addLayer(layer, std::move(layerResource)) ? Error::NONE
+ : Error::BAD_LAYER;
+}
+
+Error ComposerResources::removeLayer(Display display, Layer layer) {
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
+ if (!displayResource) {
+ return Error::BAD_DISPLAY;
+ }
+
+ return displayResource->removeLayer(layer) ? Error::NONE : Error::BAD_LAYER;
+}
+
+Error ComposerResources::getDisplayClientTarget(Display display, uint32_t slot, bool fromCache,
+ const native_handle_t* rawHandle,
+ const native_handle_t** outBufferHandle,
+ ReplacedHandle* outReplacedBuffer) {
+ return getHandle(display, 0, slot, Cache::CLIENT_TARGET, fromCache, rawHandle, outBufferHandle,
+ outReplacedBuffer);
+}
+
+Error ComposerResources::getDisplayOutputBuffer(Display display, uint32_t slot, bool fromCache,
+ const native_handle_t* rawHandle,
+ const native_handle_t** outBufferHandle,
+ ReplacedHandle* outReplacedBuffer) {
+ return getHandle(display, 0, slot, Cache::OUTPUT_BUFFER, fromCache, rawHandle, outBufferHandle,
+ outReplacedBuffer);
+}
+
+Error ComposerResources::getLayerBuffer(Display display, Layer layer, uint32_t slot, bool fromCache,
+ const native_handle_t* rawHandle,
+ const native_handle_t** outBufferHandle,
+ ReplacedHandle* outReplacedBuffer) {
+ return getHandle(display, layer, slot, Cache::LAYER_BUFFER, fromCache, rawHandle,
+ outBufferHandle, outReplacedBuffer);
+}
+
+Error ComposerResources::getLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* rawHandle,
+ const native_handle_t** outStreamHandle,
+ ReplacedHandle* outReplacedStream) {
+ return getHandle(display, layer, 0, Cache::LAYER_SIDEBAND_STREAM, false, rawHandle,
+ outStreamHandle, outReplacedStream);
+}
+
+void ComposerResources::setDisplayMustValidateState(Display display, bool mustValidate) {
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ auto* displayResource = findDisplayResourceLocked(display);
+ if (displayResource) {
+ displayResource->setMustValidateState(mustValidate);
+ }
+}
+
+bool ComposerResources::mustValidateDisplay(Display display) {
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+ auto* displayResource = findDisplayResourceLocked(display);
+ if (displayResource) {
+ return displayResource->mustValidate();
+ }
+ return false;
+}
+
+std::unique_ptr<ComposerDisplayResource> ComposerResources::createDisplayResource(
+ ComposerDisplayResource::DisplayType type, uint32_t outputBufferCacheSize) {
+ return std::make_unique<ComposerDisplayResource>(type, mImporter, outputBufferCacheSize);
+}
+
+std::unique_ptr<ComposerLayerResource> ComposerResources::createLayerResource(
+ uint32_t bufferCacheSize) {
+ return std::make_unique<ComposerLayerResource>(mImporter, bufferCacheSize);
+}
+
+ComposerDisplayResource* ComposerResources::findDisplayResourceLocked(Display display) {
+ auto iter = mDisplayResources.find(display);
+ if (iter == mDisplayResources.end()) {
+ return nullptr;
+ }
+ return iter->second.get();
+}
+
+Error ComposerResources::getHandle(Display display, Layer layer, uint32_t slot, Cache cache,
+ bool fromCache, const native_handle_t* rawHandle,
+ const native_handle_t** outHandle,
+ ReplacedHandle* outReplacedHandle) {
+ Error error;
+
+ // import the raw handle (or ignore raw handle when fromCache is true)
+ const native_handle_t* importedHandle = nullptr;
+ if (!fromCache) {
+ error = (outReplacedHandle->isBuffer())
+ ? mImporter.importBuffer(rawHandle, &importedHandle)
+ : mImporter.importStream(rawHandle, &importedHandle);
+ if (error != Error::NONE) {
+ return error;
+ }
+ }
+
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+
+ // find display/layer resource
+ const bool needLayerResource = (cache == ComposerResources::Cache::LAYER_BUFFER ||
+ cache == ComposerResources::Cache::LAYER_SIDEBAND_STREAM);
+ ComposerDisplayResource* displayResource = findDisplayResourceLocked(display);
+ ComposerLayerResource* layerResource = (displayResource && needLayerResource)
+ ? displayResource->findLayerResource(layer)
+ : nullptr;
+
+ // lookup or update cache
+ const native_handle_t* replacedHandle = nullptr;
+ if (displayResource && (!needLayerResource || layerResource)) {
+ switch (cache) {
+ case ComposerResources::Cache::CLIENT_TARGET:
+ error = displayResource->getClientTarget(slot, fromCache, importedHandle, outHandle,
+ &replacedHandle);
+ break;
+ case ComposerResources::Cache::OUTPUT_BUFFER:
+ error = displayResource->getOutputBuffer(slot, fromCache, importedHandle, outHandle,
+ &replacedHandle);
+ break;
+ case ComposerResources::Cache::LAYER_BUFFER:
+ error = layerResource->getBuffer(slot, fromCache, importedHandle, outHandle,
+ &replacedHandle);
+ break;
+ case ComposerResources::Cache::LAYER_SIDEBAND_STREAM:
+ error = layerResource->getSidebandStream(slot, fromCache, importedHandle, outHandle,
+ &replacedHandle);
+ break;
+ default:
+ error = Error::BAD_PARAMETER;
+ break;
+ }
+
+ if (error != Error::NONE) {
+ ALOGW("invalid cache %d slot %d", int(cache), int(slot));
+ }
+ } else if (!displayResource) {
+ error = Error::BAD_DISPLAY;
+ } else {
+ error = Error::BAD_LAYER;
+ }
+
+ // clean up on errors
+ if (error != Error::NONE) {
+ if (!fromCache) {
+ if (outReplacedHandle->isBuffer()) {
+ mImporter.freeBuffer(importedHandle);
+ } else {
+ mImporter.freeStream(importedHandle);
+ }
+ }
+ return error;
+ }
+
+ outReplacedHandle->reset(&mImporter, replacedHandle);
+
+ return Error::NONE;
+}
+
+} // namespace hal
+} // namespace V2_1
+} // namespace composer
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/graphics/composer/2.1/utils/resources/include/composer-resources/2.1/ComposerResources.h b/graphics/composer/2.1/utils/resources/include/composer-resources/2.1/ComposerResources.h
new file mode 100644
index 0000000..3738278
--- /dev/null
+++ b/graphics/composer/2.1/utils/resources/include/composer-resources/2.1/ComposerResources.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2018 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
+
+#ifndef LOG_TAG
+#warning "ComposerResources.h included without LOG_TAG"
+#endif
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include <android/hardware/graphics/composer/2.1/types.h>
+
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_1 {
+namespace hal {
+
+// wrapper for IMapper to import buffers and sideband streams
+class ComposerHandleImporter {
+ public:
+ bool init();
+
+ Error importBuffer(const native_handle_t* rawHandle, const native_handle_t** outBufferHandle);
+ void freeBuffer(const native_handle_t* bufferHandle);
+ Error importStream(const native_handle_t* rawHandle, const native_handle_t** outStreamHandle);
+ void freeStream(const native_handle_t* streamHandle);
+
+ private:
+ sp<mapper::V2_0::IMapper> mMapper2;
+ sp<mapper::V3_0::IMapper> mMapper3;
+ sp<mapper::V4_0::IMapper> mMapper4;
+};
+
+class ComposerHandleCache {
+ public:
+ enum class HandleType {
+ INVALID,
+ BUFFER,
+ STREAM,
+ };
+
+ ComposerHandleCache(ComposerHandleImporter& importer, HandleType type, uint32_t cacheSize);
+
+ // must be initialized later with initCache
+ ComposerHandleCache(ComposerHandleImporter& importer);
+
+ ~ComposerHandleCache();
+
+ ComposerHandleCache(const ComposerHandleCache&) = delete;
+ ComposerHandleCache& operator=(const ComposerHandleCache&) = delete;
+
+ bool initCache(HandleType type, uint32_t cacheSize);
+ Error lookupCache(uint32_t slot, const native_handle_t** outHandle);
+ Error updateCache(uint32_t slot, const native_handle_t* handle,
+ const native_handle** outReplacedHandle);
+
+ // when fromCache is true, look up in the cache; otherwise, update the cache
+ Error getHandle(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
+ const native_handle_t** outHandle, const native_handle** outReplacedHandle);
+
+ private:
+ ComposerHandleImporter& mImporter;
+ HandleType mHandleType = HandleType::INVALID;
+ std::vector<const native_handle_t*> mHandles;
+};
+
+// layer resource
+class ComposerLayerResource {
+ public:
+ ComposerLayerResource(ComposerHandleImporter& importer, uint32_t bufferCacheSize);
+
+ virtual ~ComposerLayerResource() = default;
+
+ Error getBuffer(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
+ const native_handle_t** outHandle, const native_handle** outReplacedHandle);
+ Error getSidebandStream(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle);
+
+ protected:
+ ComposerHandleCache mBufferCache;
+ ComposerHandleCache mSidebandStreamCache;
+};
+
+// display resource
+class ComposerDisplayResource {
+ public:
+ enum class DisplayType {
+ PHYSICAL,
+ VIRTUAL,
+ };
+
+ virtual ~ComposerDisplayResource() = default;
+
+ ComposerDisplayResource(DisplayType type, ComposerHandleImporter& importer,
+ uint32_t outputBufferCacheSize);
+
+ bool initClientTargetCache(uint32_t cacheSize);
+
+ bool isVirtual() const;
+
+ Error getClientTarget(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle);
+
+ Error getOutputBuffer(uint32_t slot, bool fromCache, const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle);
+
+ bool addLayer(Layer layer, std::unique_ptr<ComposerLayerResource> layerResource);
+ bool removeLayer(Layer layer);
+ ComposerLayerResource* findLayerResource(Layer layer);
+ std::vector<Layer> getLayers() const;
+
+ void setMustValidateState(bool mustValidate);
+
+ bool mustValidate() const;
+
+ protected:
+ const DisplayType mType;
+ ComposerHandleCache mClientTargetCache;
+ ComposerHandleCache mOutputBufferCache;
+ bool mMustValidate;
+
+ std::unordered_map<Layer, std::unique_ptr<ComposerLayerResource>> mLayerResources;
+};
+
+class ComposerResources {
+ public:
+ static std::unique_ptr<ComposerResources> create();
+
+ ComposerResources() = default;
+ virtual ~ComposerResources() = default;
+
+ bool init();
+
+ using RemoveDisplay =
+ std::function<void(Display display, bool isVirtual, const std::vector<Layer>& layers)>;
+ void clear(RemoveDisplay removeDisplay);
+
+ Error addPhysicalDisplay(Display display);
+ Error addVirtualDisplay(Display display, uint32_t outputBufferCacheSize);
+
+ Error removeDisplay(Display display);
+
+ Error setDisplayClientTargetCacheSize(Display display, uint32_t clientTargetCacheSize);
+
+ Error addLayer(Display display, Layer layer, uint32_t bufferCacheSize);
+ Error removeLayer(Display display, Layer layer);
+
+ void setDisplayMustValidateState(Display display, bool mustValidate);
+
+ bool mustValidateDisplay(Display display);
+
+ // When a buffer in the cache is replaced by a new one, we must keep it
+ // alive until it has been replaced in ComposerHal.
+ class ReplacedHandle {
+ public:
+ explicit ReplacedHandle(bool isBuffer) : mIsBuffer(isBuffer) {}
+ ReplacedHandle(const ReplacedHandle&) = delete;
+ ReplacedHandle& operator=(const ReplacedHandle&) = delete;
+
+ ~ReplacedHandle() { reset(); }
+
+ bool isBuffer() { return mIsBuffer; }
+
+ void reset(ComposerHandleImporter* importer = nullptr,
+ const native_handle_t* handle = nullptr) {
+ if (mHandle) {
+ if (mIsBuffer) {
+ mImporter->freeBuffer(mHandle);
+ } else {
+ mImporter->freeStream(mHandle);
+ }
+ }
+
+ mImporter = importer;
+ mHandle = handle;
+ }
+
+ private:
+ bool mIsBuffer;
+ ComposerHandleImporter* mImporter = nullptr;
+ const native_handle_t* mHandle = nullptr;
+ };
+
+ Error getDisplayClientTarget(Display display, uint32_t slot, bool fromCache,
+ const native_handle_t* rawHandle,
+ const native_handle_t** outBufferHandle,
+ ReplacedHandle* outReplacedBuffer);
+
+ Error getDisplayOutputBuffer(Display display, uint32_t slot, bool fromCache,
+ const native_handle_t* rawHandle,
+ const native_handle_t** outBufferHandle,
+ ReplacedHandle* outReplacedBuffer);
+
+ Error getLayerBuffer(Display display, Layer layer, uint32_t slot, bool fromCache,
+ const native_handle_t* rawHandle, const native_handle_t** outBufferHandle,
+ ReplacedHandle* outReplacedBuffer);
+
+ Error getLayerSidebandStream(Display display, Layer layer, const native_handle_t* rawHandle,
+ const native_handle_t** outStreamHandle,
+ ReplacedHandle* outReplacedStream);
+
+ protected:
+ virtual std::unique_ptr<ComposerDisplayResource> createDisplayResource(
+ ComposerDisplayResource::DisplayType type, uint32_t outputBufferCacheSize);
+
+ virtual std::unique_ptr<ComposerLayerResource> createLayerResource(uint32_t bufferCacheSize);
+
+ ComposerDisplayResource* findDisplayResourceLocked(Display display);
+
+ ComposerHandleImporter mImporter;
+
+ std::mutex mDisplayResourcesMutex;
+ std::unordered_map<Display, std::unique_ptr<ComposerDisplayResource>> mDisplayResources;
+
+ private:
+ enum class Cache {
+ CLIENT_TARGET,
+ OUTPUT_BUFFER,
+ LAYER_BUFFER,
+ LAYER_SIDEBAND_STREAM,
+ };
+
+ Error getHandle(Display display, Layer layer, uint32_t slot, Cache cache, bool fromCache,
+ const native_handle_t* rawHandle, const native_handle_t** outHandle,
+ ReplacedHandle* outReplacedHandle);
+};
+
+} // namespace hal
+} // namespace V2_1
+} // namespace composer
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/graphics/composer/2.1/utils/vts/Android.bp b/graphics/composer/2.1/utils/vts/Android.bp
index 88b1a8b..cdc0f35 100644
--- a/graphics/composer/2.1/utils/vts/Android.bp
+++ b/graphics/composer/2.1/utils/vts/Android.bp
@@ -27,12 +27,14 @@
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.mapper@2.0-vts",
"android.hardware.graphics.mapper@3.0-vts",
+ "android.hardware.graphics.mapper@4.0-vts",
],
export_static_lib_headers: [
"VtsHalHidlTargetTestBase",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.mapper@2.0-vts",
"android.hardware.graphics.mapper@3.0-vts",
+ "android.hardware.graphics.mapper@4.0-vts",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/graphics/composer/2.1/utils/vts/ComposerVts.cpp b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
index c5d5823..a0745ce 100644
--- a/graphics/composer/2.1/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
@@ -317,11 +317,16 @@
Gralloc::Gralloc() {
[this] {
- ASSERT_NO_FATAL_FAILURE(mGralloc3 = std::make_shared<Gralloc3>("default", "default",
+ ASSERT_NO_FATAL_FAILURE(mGralloc4 = std::make_shared<Gralloc4>("default", "default",
/*errOnFailure=*/false));
- if (mGralloc3->getAllocator() == nullptr || mGralloc3->getMapper() == nullptr) {
- mGralloc3 = nullptr;
- ASSERT_NO_FATAL_FAILURE(mGralloc2 = std::make_shared<Gralloc2>());
+ if (mGralloc4->getAllocator() == nullptr || mGralloc4->getMapper() == nullptr) {
+ mGralloc4 = nullptr;
+ ASSERT_NO_FATAL_FAILURE(mGralloc3 = std::make_shared<Gralloc3>("default", "default",
+ /*errOnFailure=*/false));
+ if (mGralloc3->getAllocator() == nullptr || mGralloc3->getMapper() == nullptr) {
+ mGralloc3 = nullptr;
+ ASSERT_NO_FATAL_FAILURE(mGralloc2 = std::make_shared<Gralloc2>());
+ }
}
}();
}
@@ -329,7 +334,15 @@
const native_handle_t* Gralloc::allocate(uint32_t width, uint32_t height, uint32_t layerCount,
PixelFormat format, uint64_t usage, bool import,
uint32_t* outStride) {
- if (mGralloc3) {
+ if (mGralloc4) {
+ IMapper4::BufferDescriptorInfo info{};
+ info.width = width;
+ info.height = height;
+ info.layerCount = layerCount;
+ info.format = static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(format);
+ info.usage = usage;
+ return mGralloc4->allocate(info, import, outStride);
+ } else if (mGralloc3) {
IMapper3::BufferDescriptorInfo info{};
info.width = width;
info.height = height;
@@ -350,7 +363,17 @@
void* Gralloc::lock(const native_handle_t* bufferHandle, uint64_t cpuUsage,
const AccessRegion& accessRegionRect, int acquireFence) {
- if (mGralloc3) {
+ if (mGralloc4) {
+ IMapper4::Rect accessRegion;
+ accessRegion.left = accessRegionRect.left;
+ accessRegion.top = accessRegionRect.top;
+ accessRegion.width = accessRegionRect.width;
+ accessRegion.height = accessRegionRect.height;
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+ return mGralloc4->lock(bufferHandle, cpuUsage, accessRegion, acquireFence, &bytesPerPixel,
+ &bytesPerStride);
+ } else if (mGralloc3) {
IMapper3::Rect accessRegion;
accessRegion.left = accessRegionRect.left;
accessRegion.top = accessRegionRect.top;
@@ -371,7 +394,9 @@
}
int Gralloc::unlock(const native_handle_t* bufferHandle) {
- if (mGralloc3) {
+ if (mGralloc4) {
+ return mGralloc4->unlock(bufferHandle);
+ } else if (mGralloc3) {
return mGralloc3->unlock(bufferHandle);
} else {
return mGralloc2->unlock(bufferHandle);
@@ -379,7 +404,9 @@
}
void Gralloc::freeBuffer(const native_handle_t* bufferHandle) {
- if (mGralloc3) {
+ if (mGralloc4) {
+ mGralloc4->freeBuffer(bufferHandle);
+ } else if (mGralloc3) {
mGralloc3->freeBuffer(bufferHandle);
} else {
mGralloc2->freeBuffer(bufferHandle);
diff --git a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h
index 7811048..4294657 100644
--- a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h
+++ b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/ComposerVts.h
@@ -27,6 +27,7 @@
#include <composer-vts/2.1/TestCommandReader.h>
#include <mapper-vts/2.0/MapperVts.h>
#include <mapper-vts/3.0/MapperVts.h>
+#include <mapper-vts/4.0/MapperVts.h>
#include <utils/StrongPointer.h>
#include "gtest/gtest.h"
@@ -44,8 +45,10 @@
using android::hardware::graphics::common::V1_0::PixelFormat;
using IMapper2 = android::hardware::graphics::mapper::V2_0::IMapper;
using IMapper3 = android::hardware::graphics::mapper::V3_0::IMapper;
+using IMapper4 = android::hardware::graphics::mapper::V4_0::IMapper;
using Gralloc2 = android::hardware::graphics::mapper::V2_0::vts::Gralloc;
using Gralloc3 = android::hardware::graphics::mapper::V3_0::vts::Gralloc;
+using Gralloc4 = android::hardware::graphics::mapper::V4_0::vts::Gralloc;
class ComposerClient;
@@ -153,6 +156,7 @@
protected:
std::shared_ptr<Gralloc2> mGralloc2 = nullptr;
std::shared_ptr<Gralloc3> mGralloc3 = nullptr;
+ std::shared_ptr<Gralloc4> mGralloc4 = nullptr;
};
} // namespace vts
diff --git a/graphics/composer/2.1/vts/functional/Android.bp b/graphics/composer/2.1/vts/functional/Android.bp
index d54da60..799ca91 100644
--- a/graphics/composer/2.1/vts/functional/Android.bp
+++ b/graphics/composer/2.1/vts/functional/Android.bp
@@ -27,12 +27,17 @@
static_libs: [
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.1-vts",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.0-vts",
+ "android.hardware.graphics.mapper@2.1",
+ "android.hardware.graphics.mapper@2.1-vts",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@3.0-vts",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hardware.graphics.mapper@4.0-vts",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
index fa5ace6..5d2f65d 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
@@ -22,6 +22,7 @@
#include <composer-vts/2.1/TestCommandReader.h>
#include <mapper-vts/2.0/MapperVts.h>
#include <mapper-vts/3.0/MapperVts.h>
+#include <mapper-vts/4.0/MapperVts.h>
#include <VtsHalHidlTargetTestBase.h>
#include <VtsHalHidlTargetTestEnvBase.h>
diff --git a/graphics/composer/2.2/default/Android.mk b/graphics/composer/2.2/default/Android.mk
index 7dedf61..cb551c6 100644
--- a/graphics/composer/2.2/default/Android.mk
+++ b/graphics/composer/2.2/default/Android.mk
@@ -11,8 +11,8 @@
LOCAL_SHARED_LIBRARIES := \
android.hardware.graphics.composer@2.1 \
android.hardware.graphics.composer@2.2 \
- android.hardware.graphics.mapper@2.0 \
- android.hardware.graphics.mapper@3.0 \
+ android.hardware.graphics.composer@2.1-resources \
+ android.hardware.graphics.composer@2.2-resources \
libbase \
libbinder \
libcutils \
diff --git a/graphics/composer/2.2/utils/hal/Android.bp b/graphics/composer/2.2/utils/hal/Android.bp
index 10dcae4..f334a11 100644
--- a/graphics/composer/2.2/utils/hal/Android.bp
+++ b/graphics/composer/2.2/utils/hal/Android.bp
@@ -19,9 +19,11 @@
vendor_available: true,
shared_libs: [
"android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.2-resources",
],
export_shared_lib_headers: [
"android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.2-resources",
],
header_libs: [
"android.hardware.graphics.composer@2.2-command-buffer",
diff --git a/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerClient.h b/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerClient.h
index c760d0a..512d39d 100644
--- a/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerClient.h
+++ b/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerClient.h
@@ -24,7 +24,7 @@
#include <composer-hal/2.1/ComposerClient.h>
#include <composer-hal/2.2/ComposerCommandEngine.h>
#include <composer-hal/2.2/ComposerHal.h>
-#include <composer-hal/2.2/ComposerResources.h>
+#include <composer-resources/2.2/ComposerResources.h>
namespace android {
namespace hardware {
@@ -89,7 +89,7 @@
auto resources = static_cast<ComposerResources*>(mResources.get());
const native_handle_t* readbackBuffer;
- ComposerResources::ReplacedBufferHandle replacedReadbackBuffer;
+ ComposerResources::ReplacedHandle replacedReadbackBuffer(true);
error = resources->getDisplayReadbackBuffer(display, buffer.getNativeHandle(),
&readbackBuffer, &replacedReadbackBuffer);
if (error != Error::NONE) {
diff --git a/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerCommandEngine.h b/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerCommandEngine.h
index 97e3a9e..d9f6226 100644
--- a/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerCommandEngine.h
+++ b/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerCommandEngine.h
@@ -23,7 +23,7 @@
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <composer-hal/2.1/ComposerCommandEngine.h>
#include <composer-hal/2.2/ComposerHal.h>
-#include <composer-hal/2.2/ComposerResources.h>
+#include <composer-resources/2.2/ComposerResources.h>
namespace android {
namespace hardware {
diff --git a/graphics/composer/2.2/utils/resources/Android.bp b/graphics/composer/2.2/utils/resources/Android.bp
new file mode 100644
index 0000000..752eb81
--- /dev/null
+++ b/graphics/composer/2.2/utils/resources/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+
+cc_library_shared {
+ name: "android.hardware.graphics.composer@2.2-resources",
+ defaults: ["hidl_defaults"],
+ vendor_available: true,
+ shared_libs: [
+ "android.hardware.graphics.composer@2.1-resources",
+ "android.hardware.graphics.composer@2.2",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.graphics.composer@2.1-resources",
+ "android.hardware.graphics.composer@2.2",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/2.2/utils/resources/ComposerResources.cpp b/graphics/composer/2.2/utils/resources/ComposerResources.cpp
new file mode 100644
index 0000000..a0610a3
--- /dev/null
+++ b/graphics/composer/2.2/utils/resources/ComposerResources.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#define LOG_TAG "ComposerResources 2.2"
+
+#include "composer-resources/2.2/ComposerResources.h"
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+namespace hal {
+
+using V2_1::Display;
+using V2_1::Error;
+using V2_1::Layer;
+using V2_1::hal::ComposerHandleCache;
+using V2_1::hal::ComposerHandleImporter;
+
+Error ComposerDisplayResource::getReadbackBuffer(const native_handle_t* inHandle,
+ const native_handle_t** outHandle,
+ const native_handle** outReplacedHandle) {
+ const uint32_t slot = 0;
+ const bool fromCache = false;
+ return mReadbackBufferCache.getHandle(slot, fromCache, inHandle, outHandle, outReplacedHandle);
+}
+
+std::unique_ptr<ComposerResources> ComposerResources::create() {
+ auto resources = std::make_unique<ComposerResources>();
+ return resources->init() ? std::move(resources) : nullptr;
+}
+
+Error ComposerResources::getDisplayReadbackBuffer(Display display, const native_handle_t* rawHandle,
+ const native_handle_t** outHandle,
+ ReplacedHandle* outReplacedHandle) {
+ // import buffer
+ const native_handle_t* importedHandle;
+ Error error = mImporter.importBuffer(rawHandle, &importedHandle);
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
+
+ auto iter = mDisplayResources.find(display);
+ if (iter == mDisplayResources.end()) {
+ mImporter.freeBuffer(importedHandle);
+ return Error::BAD_DISPLAY;
+ }
+ ComposerDisplayResource& displayResource =
+ *static_cast<ComposerDisplayResource*>(iter->second.get());
+
+ // update cache
+ const native_handle_t* replacedHandle;
+ error = displayResource.getReadbackBuffer(importedHandle, outHandle, &replacedHandle);
+ if (error != Error::NONE) {
+ mImporter.freeBuffer(importedHandle);
+ return error;
+ }
+
+ outReplacedHandle->reset(&mImporter, replacedHandle);
+ return Error::NONE;
+}
+
+} // namespace hal
+} // namespace V2_2
+} // namespace composer
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerResources.h b/graphics/composer/2.2/utils/resources/include/composer-resources/2.2/ComposerResources.h
similarity index 90%
rename from graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerResources.h
rename to graphics/composer/2.2/utils/resources/include/composer-resources/2.2/ComposerResources.h
index 85b6651..33012e9 100644
--- a/graphics/composer/2.2/utils/hal/include/composer-hal/2.2/ComposerResources.h
+++ b/graphics/composer/2.2/utils/resources/include/composer-resources/2.2/ComposerResources.h
@@ -20,7 +20,7 @@
#warning "ComposerResources.h included without LOG_TAG"
#endif
-#include <composer-hal/2.1/ComposerResources.h>
+#include <composer-resources/2.1/ComposerResources.h>
namespace android {
namespace hardware {
@@ -33,7 +33,7 @@
using V2_1::hal::ComposerHandleImporter;
class ComposerDisplayResource : public V2_1::hal::ComposerDisplayResource {
- public:
+ public:
ComposerDisplayResource(DisplayType type, ComposerHandleImporter& importer,
uint32_t outputBufferCacheSize)
: V2_1::hal::ComposerDisplayResource(type, importer, outputBufferCacheSize),
@@ -47,12 +47,12 @@
outReplacedHandle);
}
- protected:
+ protected:
ComposerHandleCache mReadbackBufferCache;
};
class ComposerResources : public V2_1::hal::ComposerResources {
- public:
+ public:
static std::unique_ptr<ComposerResources> create() {
auto resources = std::make_unique<ComposerResources>();
return resources->init() ? std::move(resources) : nullptr;
@@ -60,7 +60,7 @@
Error getDisplayReadbackBuffer(Display display, const native_handle_t* rawHandle,
const native_handle_t** outHandle,
- ReplacedBufferHandle* outReplacedHandle) {
+ ReplacedHandle* outReplacedHandle) {
// import buffer
const native_handle_t* importedHandle;
Error error = mImporter.importBuffer(rawHandle, &importedHandle);
@@ -76,7 +76,7 @@
return Error::BAD_DISPLAY;
}
ComposerDisplayResource& displayResource =
- *static_cast<ComposerDisplayResource*>(iter->second.get());
+ *static_cast<ComposerDisplayResource*>(iter->second.get());
// update cache
const native_handle_t* replacedHandle;
@@ -90,9 +90,9 @@
return Error::NONE;
}
- protected:
+ protected:
std::unique_ptr<V2_1::hal::ComposerDisplayResource> createDisplayResource(
- ComposerDisplayResource::DisplayType type, uint32_t outputBufferCacheSize) override {
+ ComposerDisplayResource::DisplayType type, uint32_t outputBufferCacheSize) override {
return std::make_unique<ComposerDisplayResource>(type, mImporter, outputBufferCacheSize);
}
};
diff --git a/graphics/composer/2.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp
index 1754a43..5432882 100644
--- a/graphics/composer/2.2/utils/vts/Android.bp
+++ b/graphics/composer/2.2/utils/vts/Android.bp
@@ -34,6 +34,10 @@
"libmath",
"libnativewindow",
"librenderengine",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@3.0-vts",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hardware.graphics.mapper@4.0-vts",
],
export_static_lib_headers: [
"VtsHalHidlTargetTestBase",
diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
index a380fc0..93b67f0 100644
--- a/graphics/composer/2.2/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
@@ -182,17 +182,23 @@
Gralloc::Gralloc() {
[this] {
- ALOGD("Attempting to initialize gralloc3");
- ASSERT_NO_FATAL_FAILURE(mGralloc3 = std::make_shared<Gralloc3>("default", "default",
+ ALOGD("Attempting to initialize gralloc4");
+ ASSERT_NO_FATAL_FAILURE(mGralloc4 = std::make_shared<Gralloc4>("default", "default",
/*errOnFailure=*/false));
- if (mGralloc3->getMapper() == nullptr || mGralloc3->getAllocator() == nullptr) {
- mGralloc3 = nullptr;
- ALOGD("Failed to initialize gralloc3, initializing gralloc2_1");
- mGralloc2_1 = std::make_shared<Gralloc2_1>(/*errOnFailure*/ false);
- if (!mGralloc2_1->getMapper()) {
- mGralloc2_1 = nullptr;
- ALOGD("Failed to initialize gralloc2_1, initializing gralloc2");
- ASSERT_NO_FATAL_FAILURE(mGralloc2 = std::make_shared<Gralloc2>());
+ if (mGralloc4->getMapper() == nullptr || mGralloc4->getAllocator() == nullptr) {
+ mGralloc4 = nullptr;
+ ALOGD("Failed to initialize gralloc4, initializing gralloc3");
+ ASSERT_NO_FATAL_FAILURE(mGralloc3 = std::make_shared<Gralloc3>("default", "default",
+ /*errOnFailure=*/false));
+ if (mGralloc3->getMapper() == nullptr || mGralloc3->getAllocator() == nullptr) {
+ mGralloc3 = nullptr;
+ ALOGD("Failed to initialize gralloc3, initializing gralloc2_1");
+ mGralloc2_1 = std::make_shared<Gralloc2_1>(/*errOnFailure*/ false);
+ if (!mGralloc2_1->getMapper()) {
+ mGralloc2_1 = nullptr;
+ ALOGD("Failed to initialize gralloc2_1, initializing gralloc2");
+ ASSERT_NO_FATAL_FAILURE(mGralloc2 = std::make_shared<Gralloc2>());
+ }
}
}
}();
@@ -201,7 +207,15 @@
bool Gralloc::validateBufferSize(const native_handle_t* bufferHandle, uint32_t width,
uint32_t height, uint32_t layerCount, PixelFormat format,
uint64_t usage, uint32_t stride) {
- if (mGralloc3) {
+ if (mGralloc4) {
+ IMapper4::BufferDescriptorInfo info{};
+ info.width = width;
+ info.height = height;
+ info.layerCount = layerCount;
+ info.format = static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(format);
+ info.usage = usage;
+ return mGralloc4->validateBufferSize(bufferHandle, info, stride);
+ } else if (mGralloc3) {
IMapper3::BufferDescriptorInfo info{};
info.width = width;
info.height = height;
diff --git a/graphics/composer/2.2/utils/vts/ReadbackVts.cpp b/graphics/composer/2.2/utils/vts/ReadbackVts.cpp
index d479aa7..7bb9121 100644
--- a/graphics/composer/2.2/utils/vts/ReadbackVts.cpp
+++ b/graphics/composer/2.2/utils/vts/ReadbackVts.cpp
@@ -81,14 +81,14 @@
static_cast<float>(mDisplayFrame.left), static_cast<float>(mDisplayFrame.top),
static_cast<float>(mDisplayFrame.right), static_cast<float>(mDisplayFrame.bottom));
- const mat4 translation = mat4::translate(vec4(
- (mTransform & Transform::FLIP_H ? -1.0f : 0.0f) * mDisplayFrame.right,
- (mTransform & Transform::FLIP_V ? -1.0f : 0.0f) * mDisplayFrame.bottom, 0.0f, 1.0f));
+ const mat4 translation = mat4::translate(
+ vec4((mTransform & Transform::FLIP_H ? -mDisplayFrame.right : 0.0f),
+ (mTransform & Transform::FLIP_V ? -mDisplayFrame.bottom : 0.0f), 0.0f, 1.0f));
const mat4 scale = mat4::scale(vec4(mTransform & Transform::FLIP_H ? -1.0f : 1.0f,
mTransform & Transform::FLIP_V ? -1.0f : 1.0f, 1.0f, 1.0f));
- layerSettings.geometry.positionTransform = translation * scale;
+ layerSettings.geometry.positionTransform = scale * translation;
return layerSettings;
}
@@ -296,9 +296,9 @@
layerSettings.source.buffer.buffer =
new GraphicBuffer(mBufferHandle, GraphicBuffer::CLONE_HANDLE, mWidth, mHeight,
static_cast<int32_t>(mFormat), 1, mUsage, mStride);
- // TODO(b/136483187): Why does this break the premultiply test
- // layerSettings.source.buffer.usePremultipliedAlpha =
- // mBlendMode == IComposerClient::BlendMode::PREMULTIPLIED;
+
+ layerSettings.source.buffer.usePremultipliedAlpha =
+ mBlendMode == IComposerClient::BlendMode::PREMULTIPLIED;
const float scaleX = (mSourceCrop.right - mSourceCrop.left) / (mWidth);
const float scaleY = (mSourceCrop.bottom - mSourceCrop.top) / (mHeight);
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
index 8fa9b7b..5d22305 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
@@ -44,9 +44,11 @@
using common::V1_1::RenderIntent;
using IMapper2_1 = android::hardware::graphics::mapper::V2_1::IMapper;
using IMapper3 = android::hardware::graphics::mapper::V3_0::IMapper;
+using IMapper4 = android::hardware::graphics::mapper::V4_0::IMapper;
using Gralloc2 = android::hardware::graphics::mapper::V2_0::vts::Gralloc;
using Gralloc2_1 = android::hardware::graphics::mapper::V2_1::vts::Gralloc;
using Gralloc3 = android::hardware::graphics::mapper::V3_0::vts::Gralloc;
+using Gralloc4 = android::hardware::graphics::mapper::V4_0::vts::Gralloc;
class ComposerClient;
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index 1319035..2872880 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -19,31 +19,27 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"VtsHalGraphicsComposerV2_2ReadbackTest.cpp",
- "VtsHalGraphicsComposerV2_2TargetTest.cpp",
- "VtsHalGraphicsComposerV2_2CompositionComparisonTest.cpp",
+ "VtsHalGraphicsComposerV2_2TargetTest.cpp"
],
// TODO(b/64437680): Assume these libs are always available on the device.
shared_libs: [
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
"libfmq",
+ "libgui",
"libhidlbase",
"libhidltransport",
"libhwbinder",
"libprocessgroup",
"libsync",
"libui",
- "libgui",
- "libEGL",
- "libGLESv1_CM",
- "libGLESv2",
],
static_libs: [
- "librenderengine",
- "libmath",
- "libarect",
- "libnativewindow",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.common@1.1",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.1-vts",
@@ -55,6 +51,9 @@
"android.hardware.graphics.mapper@2.1-vts",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@3.0-vts",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hardware.graphics.mapper@4.0-vts",
+ "librenderengine"
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2CompositionComparisonTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2CompositionComparisonTest.cpp
deleted file mode 100644
index d694a5b..0000000
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2CompositionComparisonTest.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#define LOG_TAG "graphics_composer_hidl_hal_readback_tests@2.2"
-
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
-#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
-#include <composer-vts/2.1/GraphicsComposerCallback.h>
-#include <composer-vts/2.1/TestCommandReader.h>
-#include <composer-vts/2.2/ComposerVts.h>
-#include <composer-vts/2.2/ReadbackVts.h>
-#include <composer-vts/2.2/RenderEngineVts.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicBufferAllocator.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
-namespace android {
-namespace hardware {
-namespace graphics {
-namespace composer {
-namespace V2_2 {
-namespace vts {
-namespace {
-
-using android::GraphicBuffer;
-using android::Rect;
-using android::hardware::hidl_handle;
-using common::V1_1::BufferUsage;
-using common::V1_1::Dataspace;
-using common::V1_1::PixelFormat;
-using mapper::V2_1::IMapper;
-using V2_1::Config;
-using V2_1::Display;
-using V2_1::vts::TestCommandReader;
-using vts::Gralloc;
-
-// Test environment for graphics.composer
-class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static GraphicsComposerHidlEnvironment* Instance() {
- static GraphicsComposerHidlEnvironment* instance = new GraphicsComposerHidlEnvironment;
- return instance;
- }
- virtual void registerTestServices() override { registerTestService<IComposer>(); }
-
- private:
- GraphicsComposerHidlEnvironment() {}
- GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment);
-};
-
-class GraphicsCompositionComparisonTest : public ::testing::VtsHalHidlTargetTestBase {
- protected:
- using PowerMode = V2_1::IComposerClient::PowerMode;
- void SetUp() override {
- VtsHalHidlTargetTestBase::SetUp();
- ASSERT_NO_FATAL_FAILURE(
- mComposer = std::make_unique<Composer>(
- GraphicsComposerHidlEnvironment::Instance()->getServiceName<IComposer>()));
- ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient());
- mComposerCallback = new V2_1::vts::GraphicsComposerCallback;
- mComposerClient->registerCallback(mComposerCallback);
-
- // assume the first display is primary and is never removed
- mPrimaryDisplay = waitForFirstDisplay();
- Config activeConfig;
- ASSERT_NO_FATAL_FAILURE(activeConfig = mComposerClient->getActiveConfig(mPrimaryDisplay));
- ASSERT_NO_FATAL_FAILURE(
- mDisplayWidth = mComposerClient->getDisplayAttribute(
- mPrimaryDisplay, activeConfig, IComposerClient::Attribute::WIDTH));
- ASSERT_NO_FATAL_FAILURE(
- mDisplayHeight = mComposerClient->getDisplayAttribute(
- mPrimaryDisplay, activeConfig, IComposerClient::Attribute::HEIGHT));
-
- setTestColorModes();
-
- // explicitly disable vsync
- ASSERT_NO_FATAL_FAILURE(mComposerClient->setVsyncEnabled(mPrimaryDisplay, false));
- mComposerCallback->setVsyncAllowed(false);
-
- // set up command writer/reader and gralloc
- mWriter = std::make_shared<CommandWriterBase>(1024);
- mReader = std::make_unique<TestCommandReader>();
- mGralloc = std::make_shared<Gralloc>();
-
- ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, PowerMode::ON));
-
- ASSERT_NO_FATAL_FAILURE(
- mTestRenderEngine = std::unique_ptr<TestRenderEngine>(new TestRenderEngine(
- PixelFormat::RGBA_8888,
- renderengine::RenderEngine::USE_COLOR_MANAGEMENT |
- renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT)));
-
- renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = Rect(mDisplayWidth, mDisplayHeight);
- clientCompositionDisplay.clip = clientCompositionDisplay.physicalDisplay;
- clientCompositionDisplay.clearRegion = Region(clientCompositionDisplay.physicalDisplay);
-
- mTestRenderEngine->initGraphicBuffer(
- static_cast<uint32_t>(mDisplayWidth), static_cast<uint32_t>(mDisplayHeight), 1,
- static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN));
- mTestRenderEngine->setDisplaySettings(clientCompositionDisplay);
- }
-
- void TearDown() override {
- ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, PowerMode::OFF));
- EXPECT_EQ(0, mReader->mErrors.size());
- EXPECT_EQ(0, mReader->mCompositionChanges.size());
- if (mComposerCallback != nullptr) {
- EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
- EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
- EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount());
- }
- VtsHalHidlTargetTestBase::TearDown();
- }
-
- void clearCommandReaderState() {
- mReader->mCompositionChanges.clear();
- mReader->mErrors.clear();
- }
-
- void writeLayers(const std::vector<std::shared_ptr<TestLayer>>& layers) {
- for (auto layer : layers) {
- layer->write(mWriter);
- }
- execute();
- }
-
- void execute() {
- ASSERT_NO_FATAL_FAILURE(mComposerClient->execute(mReader.get(), mWriter.get()));
- }
-
- std::unique_ptr<Composer> mComposer;
- std::shared_ptr<ComposerClient> mComposerClient;
-
- sp<V2_1::vts::GraphicsComposerCallback> mComposerCallback;
- // the first display and is assumed never to be removed
- Display mPrimaryDisplay;
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
- std::vector<ColorMode> mTestColorModes;
- std::shared_ptr<CommandWriterBase> mWriter;
- std::unique_ptr<TestCommandReader> mReader;
- std::shared_ptr<Gralloc> mGralloc;
- std::unique_ptr<TestRenderEngine> mTestRenderEngine;
-
- bool mHasReadbackBuffer;
- PixelFormat mPixelFormat;
- Dataspace mDataspace;
-
- private:
- Display waitForFirstDisplay() {
- while (true) {
- std::vector<Display> displays = mComposerCallback->getDisplays();
- if (displays.empty()) {
- usleep(5 * 1000);
- continue;
- }
- return displays[0];
- }
- }
-
- void setTestColorModes() {
- mTestColorModes.clear();
- mComposerClient->getRaw()->getColorModes_2_2(mPrimaryDisplay, [&](const auto& tmpError,
- const auto& tmpModes) {
- ASSERT_EQ(Error::NONE, tmpError);
- for (ColorMode mode : tmpModes) {
- if (std::find(ReadbackHelper::colorModes.begin(), ReadbackHelper::colorModes.end(),
- mode) != ReadbackHelper::colorModes.end()) {
- mTestColorModes.push_back(mode);
- }
- }
- });
- }
-};
-
-TEST_F(GraphicsCompositionComparisonTest, SingleSolidColorLayer) {
- for (ColorMode mode : mTestColorModes) {
- std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
- << std::endl;
- mWriter->selectDisplay(mPrimaryDisplay);
- ASSERT_NO_FATAL_FAILURE(
- mComposerClient->setColorMode(mPrimaryDisplay, mode, RenderIntent::COLORIMETRIC));
-
- mComposerClient->getRaw()->getReadbackBufferAttributes(
- mPrimaryDisplay,
- [&](const auto& tmpError, const auto& tmpPixelFormat, const auto& tmpDataspace) {
- mHasReadbackBuffer = ReadbackHelper::readbackSupported(tmpPixelFormat,
- tmpDataspace, tmpError);
- mPixelFormat = tmpPixelFormat;
- mDataspace = tmpDataspace;
- });
-
- if (!mHasReadbackBuffer) {
- std::cout << "Readback not supported or unsupported pixelFormat/dataspace" << std::endl;
- GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace";
- return;
- }
-
- auto layer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay);
- IComposerClient::Rect coloredSquare({0, 0, mDisplayWidth, mDisplayHeight});
- layer->setColor(BLUE);
- layer->setDisplayFrame(coloredSquare);
- layer->setZOrder(10);
-
- std::vector<std::shared_ptr<TestLayer>> layers = {layer};
-
- // expected color for each pixel
- std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight);
- ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, coloredSquare, BLUE);
-
- ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth,
- mDisplayHeight, mPixelFormat, mDataspace);
- ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
-
- writeLayers(layers);
- ASSERT_EQ(0, mReader->mErrors.size());
- mWriter->validateDisplay();
- execute();
- // if hwc cannot handle and asks for composition change,
- // just succeed the test
- if (mReader->mCompositionChanges.size() != 0) {
- clearCommandReaderState();
- GTEST_SUCCEED();
- return;
- }
- ASSERT_EQ(0, mReader->mErrors.size());
- mWriter->presentDisplay();
- execute();
- ASSERT_EQ(0, mReader->mErrors.size());
-
- ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
- mTestRenderEngine->setRenderLayers(layers);
- ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
- ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
- }
-}
-
-} // namespace
-} // namespace vts
-} // namespace V2_2
-} // namespace composer
-} // namespace graphics
-} // namespace hardware
-} // namespace android
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
index 92c03f0..ade7a38 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
@@ -21,7 +21,14 @@
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <composer-vts/2.1/GraphicsComposerCallback.h>
#include <composer-vts/2.1/TestCommandReader.h>
+#include <composer-vts/2.2/ComposerVts.h>
#include <composer-vts/2.2/ReadbackVts.h>
+#include <composer-vts/2.2/RenderEngineVts.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
namespace android {
namespace hardware {
@@ -31,14 +38,17 @@
namespace vts {
namespace {
+using android::GraphicBuffer;
+using android::Rect;
using android::hardware::hidl_handle;
using common::V1_1::BufferUsage;
using common::V1_1::Dataspace;
using common::V1_1::PixelFormat;
using mapper::V2_1::IMapper;
+using V2_1::Config;
using V2_1::Display;
-using V2_1::Layer;
using V2_1::vts::TestCommandReader;
+using vts::Gralloc;
// Test environment for graphics.composer
class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
@@ -55,8 +65,8 @@
GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment);
};
-class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase {
- protected:
+class GraphicsCompositionTest : public ::testing::VtsHalHidlTargetTestBase {
+ protected:
using PowerMode = V2_1::IComposerClient::PowerMode;
void SetUp() override {
VtsHalHidlTargetTestBase::SetUp();
@@ -90,6 +100,22 @@
mGralloc = std::make_shared<Gralloc>();
ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, PowerMode::ON));
+
+ ASSERT_NO_FATAL_FAILURE(
+ mTestRenderEngine = std::unique_ptr<TestRenderEngine>(new TestRenderEngine(
+ PixelFormat::RGBA_8888,
+ renderengine::RenderEngine::USE_COLOR_MANAGEMENT |
+ renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT)));
+
+ renderengine::DisplaySettings clientCompositionDisplay;
+ clientCompositionDisplay.physicalDisplay = Rect(mDisplayWidth, mDisplayHeight);
+ clientCompositionDisplay.clip = clientCompositionDisplay.physicalDisplay;
+ clientCompositionDisplay.clearRegion = Region(clientCompositionDisplay.physicalDisplay);
+
+ mTestRenderEngine->initGraphicBuffer(
+ static_cast<uint32_t>(mDisplayWidth), static_cast<uint32_t>(mDisplayHeight), 1,
+ static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN));
+ mTestRenderEngine->setDisplaySettings(clientCompositionDisplay);
}
void TearDown() override {
@@ -132,6 +158,7 @@
std::shared_ptr<CommandWriterBase> mWriter;
std::unique_ptr<TestCommandReader> mReader;
std::shared_ptr<Gralloc> mGralloc;
+ std::unique_ptr<TestRenderEngine> mTestRenderEngine;
bool mHasReadbackBuffer;
PixelFormat mPixelFormat;
@@ -166,7 +193,7 @@
}
};
-TEST_F(GraphicsComposerReadbackTest, SingleSolidColorLayer) {
+TEST_F(GraphicsCompositionTest, SingleSolidColorLayer) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -222,10 +249,13 @@
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(layers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-TEST_F(GraphicsComposerReadbackTest, SetLayerBuffer) {
+TEST_F(GraphicsCompositionTest, SetLayerBuffer) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -291,10 +321,13 @@
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(layers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-TEST_F(GraphicsComposerReadbackTest, SetLayerBufferNoEffect) {
+TEST_F(GraphicsCompositionTest, SetLayerBufferNoEffect) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -356,7 +389,7 @@
}
}
-TEST_F(GraphicsComposerReadbackTest, ClientComposition) {
+TEST_F(GraphicsCompositionTest, ClientComposition) {
ASSERT_NO_FATAL_FAILURE(
mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kClientTargetSlotCount));
@@ -473,7 +506,7 @@
}
}
-TEST_F(GraphicsComposerReadbackTest, DeviceAndClientComposition) {
+TEST_F(GraphicsCompositionTest, DeviceAndClientComposition) {
ASSERT_NO_FATAL_FAILURE(
mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kClientTargetSlotCount));
@@ -599,7 +632,7 @@
}
}
-TEST_F(GraphicsComposerReadbackTest, SetLayerDamage) {
+TEST_F(GraphicsCompositionTest, SetLayerDamage) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -684,7 +717,7 @@
}
}
-TEST_F(GraphicsComposerReadbackTest, SetLayerPlaneAlpha) {
+TEST_F(GraphicsCompositionTest, SetLayerPlaneAlpha) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -739,10 +772,13 @@
std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight);
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(layers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-TEST_F(GraphicsComposerReadbackTest, SetLayerSourceCrop) {
+TEST_F(GraphicsCompositionTest, SetLayerSourceCrop) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -807,10 +843,13 @@
execute();
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(layers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-TEST_F(GraphicsComposerReadbackTest, SetLayerZOrder) {
+TEST_F(GraphicsCompositionTest, SetLayerZOrder) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -891,20 +930,23 @@
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(layers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-class GraphicsComposerBlendModeReadbackTest : public GraphicsComposerReadbackTest,
- public ::testing::WithParamInterface<float> {
- public:
+class GraphicsBlendModeCompositionTest : public GraphicsCompositionTest,
+ public ::testing::WithParamInterface<float> {
+ public:
void SetUp() override {
- GraphicsComposerReadbackTest::SetUp();
+ GraphicsCompositionTest::SetUp();
mTestColorModes = {ColorMode::SRGB}; // TODO: add more color mode support
mBackgroundColor = BLACK;
mTopLayerColor = RED;
}
- void TearDown() override { GraphicsComposerReadbackTest::TearDown(); }
+ void TearDown() override { GraphicsCompositionTest::TearDown(); }
void setBackgroundColor(IComposerClient::Color color) { mBackgroundColor = color; }
@@ -976,7 +1018,7 @@
IComposerClient::Color mTopLayerColor;
};
-TEST_P(GraphicsComposerBlendModeReadbackTest, None) {
+TEST_P(GraphicsBlendModeCompositionTest, None) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -1026,12 +1068,15 @@
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(mLayers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
// TODO: bug 116865056: Readback returns (245, 0, 0) for layer plane
// alpha of .2, expected 10.2
-TEST_P(GraphicsComposerBlendModeReadbackTest, DISABLED_Coverage) {
+TEST_P(GraphicsBlendModeCompositionTest, DISABLED_Coverage) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -1084,7 +1129,7 @@
}
}
-TEST_P(GraphicsComposerBlendModeReadbackTest, Premultiplied) {
+TEST_P(GraphicsBlendModeCompositionTest, Premultiplied) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -1132,16 +1177,19 @@
execute();
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(mLayers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-INSTANTIATE_TEST_CASE_P(BlendModeTest, GraphicsComposerBlendModeReadbackTest,
+INSTANTIATE_TEST_CASE_P(BlendModeTest, GraphicsBlendModeCompositionTest,
::testing::Values(.2, 1.0));
-class GraphicsComposerTransformReadbackTest : public GraphicsComposerReadbackTest {
- protected:
+class GraphicsTransformCompositionTest : public GraphicsCompositionTest {
+ protected:
void SetUp() override {
- GraphicsComposerReadbackTest::SetUp();
+ GraphicsCompositionTest::SetUp();
mWriter->selectDisplay(mPrimaryDisplay);
@@ -1176,7 +1224,7 @@
int mSideLength;
};
-TEST_F(GraphicsComposerTransformReadbackTest, FLIP_H) {
+TEST_F(GraphicsTransformCompositionTest, FLIP_H) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -1225,10 +1273,13 @@
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(mLayers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-TEST_F(GraphicsComposerTransformReadbackTest, FLIP_V) {
+TEST_F(GraphicsTransformCompositionTest, FLIP_V) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -1277,10 +1328,13 @@
execute();
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(mLayers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
-TEST_F(GraphicsComposerTransformReadbackTest, ROT_180) {
+TEST_F(GraphicsTransformCompositionTest, ROT_180) {
for (ColorMode mode : mTestColorModes) {
std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---"
<< std::endl;
@@ -1330,6 +1384,9 @@
execute();
ASSERT_EQ(0, mReader->mErrors.size());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
+ mTestRenderEngine->setRenderLayers(mLayers);
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+ ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors));
}
}
diff --git a/graphics/composer/2.3/default/Android.bp b/graphics/composer/2.3/default/Android.bp
index 07afd6c..8103b89 100644
--- a/graphics/composer/2.3/default/Android.bp
+++ b/graphics/composer/2.3/default/Android.bp
@@ -28,8 +28,8 @@
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.composer@2.1-resources",
+ "android.hardware.graphics.composer@2.2-resources",
"libbase",
"libbinder",
"libcutils",
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
index b289b6a..04530d3 100644
--- a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
@@ -21,9 +21,9 @@
#endif
#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
-#include <composer-hal/2.2/ComposerResources.h>
#include <composer-hal/2.3/ComposerCommandEngine.h>
#include <composer-hal/2.3/ComposerHal.h>
+#include <composer-resources/2.2/ComposerResources.h>
namespace android {
namespace hardware {
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h
index 1a40d96..329dbed 100644
--- a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h
@@ -23,8 +23,8 @@
#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
#include <composer-hal/2.1/ComposerCommandEngine.h>
#include <composer-hal/2.2/ComposerCommandEngine.h>
-#include <composer-hal/2.2/ComposerResources.h>
#include <composer-hal/2.3/ComposerHal.h>
+#include <composer-resources/2.2/ComposerResources.h>
namespace android {
namespace hardware {
diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp
index 2766638..965c8fe 100644
--- a/graphics/composer/2.3/vts/functional/Android.bp
+++ b/graphics/composer/2.3/vts/functional/Android.bp
@@ -29,6 +29,7 @@
static_libs: [
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.1-vts",
"android.hardware.graphics.composer@2.2",
@@ -41,6 +42,8 @@
"android.hardware.graphics.mapper@2.1-vts",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@3.0-vts",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hardware.graphics.mapper@4.0-vts",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/graphics/composer/2.4/default/Android.bp b/graphics/composer/2.4/default/Android.bp
index 1bf51e0..a44e687 100644
--- a/graphics/composer/2.4/default/Android.bp
+++ b/graphics/composer/2.4/default/Android.bp
@@ -29,8 +29,8 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.composer@2.1-resources",
+ "android.hardware.graphics.composer@2.2-resources",
"libbase",
"libbinder",
"libcutils",
diff --git a/graphics/composer/2.4/vts/functional/Android.bp b/graphics/composer/2.4/vts/functional/Android.bp
index d437f24..6ee7873 100644
--- a/graphics/composer/2.4/vts/functional/Android.bp
+++ b/graphics/composer/2.4/vts/functional/Android.bp
@@ -27,6 +27,8 @@
],
static_libs: [
"android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.1-vts",
"android.hardware.graphics.composer@2.2",
@@ -38,6 +40,11 @@
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.0-vts",
"android.hardware.graphics.mapper@2.1",
+ "android.hardware.graphics.mapper@2.1-vts",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@3.0-vts",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hardware.graphics.mapper@4.0-vts",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/health/storage/1.0/default/service.cpp b/health/storage/1.0/default/service.cpp
index f4296f1..6f6ebc8 100644
--- a/health/storage/1.0/default/service.cpp
+++ b/health/storage/1.0/default/service.cpp
@@ -32,7 +32,7 @@
configureRpcThreadpool(1, true);
sp<IStorage> service = new Storage();
- LazyServiceRegistrar registrar;
+ auto registrar = LazyServiceRegistrar::getInstance();
status_t result = registrar.registerService(service);
if (result != OK) {
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTests.h b/neuralnetworks/1.0/vts/functional/GeneratedTests.h
index 11ee5e8..5cabf68 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTests.h
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTests.h
@@ -32,7 +32,4 @@
using namespace android::hardware::neuralnetworks::V1_0::vts::functional;
-using ::android::hardware::neuralnetworks::V1_0::Model;
-using ::android::hardware::neuralnetworks::V1_0::Request;
-
} // namespace android::hardware::neuralnetworks::V1_0::generated_tests
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTests.h b/neuralnetworks/1.1/vts/functional/GeneratedTests.h
index 2343a4f..80442bf 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTests.h
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTests.h
@@ -34,6 +34,5 @@
using ::android::hardware::neuralnetworks::V1_0::OperandLifeTime;
using ::android::hardware::neuralnetworks::V1_0::Request;
-using ::android::hardware::neuralnetworks::V1_1::Model;
} // namespace android::hardware::neuralnetworks::V1_1::generated_tests
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 082d758..5907646 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -35,10 +35,11 @@
#include "Utils.h"
#include "VtsHalNeuralnetworks.h"
-namespace android::hardware::neuralnetworks::V1_2::generated_tests::
- mobilenet_224_gender_basic_fixed {
+namespace android::hardware::neuralnetworks::V1_2 {
+namespace generated_tests::mobilenet_224_gender_basic_fixed {
Model createTestModel();
-} // namespace android::hardware::neuralnetworks::V1_2::generated_tests::mobilenet_224_gender_basic_fixed
+} // namespace generated_tests::mobilenet_224_gender_basic_fixed
+} // namespace android::hardware::neuralnetworks::V1_2
namespace generated_tests::mobilenet_224_gender_basic_fixed {
std::vector<test_helper::MixedTypedExample>& get_examples();
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTests.h b/neuralnetworks/1.2/vts/functional/GeneratedTests.h
index f393eb2..9842036 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTests.h
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTests.h
@@ -35,6 +35,5 @@
using ::android::hardware::neuralnetworks::V1_0::OperandLifeTime;
using ::android::hardware::neuralnetworks::V1_0::Request;
-using ::android::hardware::neuralnetworks::V1_2::Model;
} // namespace android::hardware::neuralnetworks::V1_2::generated_tests
diff --git a/tv/tuner/1.0/Android.bp b/tv/tuner/1.0/Android.bp
new file mode 100644
index 0000000..b54413d
--- /dev/null
+++ b/tv/tuner/1.0/Android.bp
@@ -0,0 +1,20 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.tv.tuner@1.0",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IFrontend.hal",
+ "IFrontendCallback.hal",
+ "ITuner.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ ],
+ gen_java: false,
+ gen_java_constants: true,
+}
diff --git a/tv/tuner/1.0/IFrontend.hal b/tv/tuner/1.0/IFrontend.hal
new file mode 100644
index 0000000..05cee91
--- /dev/null
+++ b/tv/tuner/1.0/IFrontend.hal
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.tv.tuner@1.0;
+
+import IFrontendCallback;
+
+/**
+ * A Tuner Frontend is used to tune to a frequency and lock signal. It provide
+ * live data feed to Tuner Demux interface.
+ */
+interface IFrontend {
+ /**
+ * Set the callback
+ *
+ * It is used by the client to receive events from the Frontend.
+ * Only one callback for one Frontend instance is supported. The callback
+ * will be replaced if it's set again.
+ *
+ * @param callback Callback object to pass Frontend events to the system.
+ * The previously registered callback must be replaced with this one.
+ * It can be null.
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if callback can't be set at current stage,
+ * UNKNOWN_ERROR if callback setting failed for other reasons.
+ */
+ setCallback(IFrontendCallback callback) generates (Result result);
+
+ /**
+ * Tuning Frontend
+ *
+ * It is used by the client to lock a frequency by providing signal
+ * delivery information. If previous tuning isn't completed, this call must
+ * stop previous tuning, and start a new tuning. Tune is a async call.
+ * LOCKED or NO_SIGNAL eventi is sent back to caller through callback.
+ *
+ * @param settings Signal delivery information which frontend can use to
+ * search and lock the signal.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if tuning can't be applied at current stage,
+ * UNKNOWN_ERROR if tuning failed for other reasons.
+ */
+ tune(FrontendSettings settings) generates (Result result);
+
+ /**
+ * Stop the tuning
+ *
+ * It is used by the client to stop a previous tuning.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successfully stop tuning.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ stopTune() generates (Result result);
+
+ /**
+ * Release the Frontend instance
+ *
+ * It is used by the client to release the frontend instance. HAL clear
+ * underneath resource. client mustn't access the instance any more.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ close() generates (Result result);
+};
diff --git a/tv/tuner/1.0/IFrontendCallback.hal b/tv/tuner/1.0/IFrontendCallback.hal
new file mode 100644
index 0000000..e907049
--- /dev/null
+++ b/tv/tuner/1.0/IFrontendCallback.hal
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.tv.tuner@1.0;
+
+interface IFrontendCallback {
+ /**
+ * Notify the client that a new event happened on the frontend.
+ *
+ * @param frontendEventType the event type.
+ */
+ oneway onEvent(FrontendEventType frontendEventType);
+
+ /**
+ * The callback function that must be called by HAL implementation to notify
+ * the client of new DiSEqC message.
+ *
+ * @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite
+ * Equipment Control) message which is specified by EUTELSAT Bus Functional
+ * Specification Version 4.2.
+ */
+ oneway onDiseqcMessage(vec<uint8_t> diseqcMessage);
+};
+
diff --git a/tv/tuner/1.0/ITuner.hal b/tv/tuner/1.0/ITuner.hal
new file mode 100644
index 0000000..5a5f547
--- /dev/null
+++ b/tv/tuner/1.0/ITuner.hal
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.tv.tuner@1.0;
+
+import IFrontend;
+
+/**
+ * Top level interface to manage Frontend, Demux and Decrambler hardware
+ * resouces which are needed for Android TV.
+ */
+interface ITuner {
+ /**
+ * Get Frontend IDs
+ *
+ * It is used by the client to get all available frontends' IDs.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if tuning failed for other reasons.
+ * @return frontendIds an array of FrontendId for the available frontends.
+ */
+ getFrontendIds() generates (Result result, vec<FrontendId> frontendIds);
+
+ /**
+ * Create a new instance of Frontend given a frontendId.
+ *
+ * It is used by the client to create a frontend instance.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if creation failed for other reasons.
+ * @return frontend the newly created frontend interface.
+ */
+ openFrontendById(FrontendId frontendId)
+ generates (Result result, IFrontend frontend);
+
+};
+
diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp
new file mode 100644
index 0000000..cc8d1f5
--- /dev/null
+++ b/tv/tuner/1.0/default/Android.bp
@@ -0,0 +1,43 @@
+cc_defaults {
+ name: "tuner_service_defaults",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "Frontend.cpp",
+ "Tuner.cpp",
+ "service.cpp",
+ ],
+
+ compile_multilib: "first",
+
+ shared_libs: [
+ "android.hardware.tv.tuner@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "liblog",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+ header_libs: [
+ "media_plugin_headers",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.tv.tuner@1.0-service",
+ vintf_fragments: ["android.hardware.tv.tuner@1.0-service.xml"],
+ defaults: ["tuner_service_defaults"],
+ init_rc: ["android.hardware.tv.tuner@1.0-service.rc"],
+}
+
+cc_binary {
+ name: "android.hardware.tv.tuner@1.0-service-lazy",
+ vintf_fragments: ["android.hardware.tv.tuner@1.0-service-lazy.xml"],
+ overrides: ["android.hardware.tv.tuner@1.0-service"],
+ defaults: ["tuner_service_defaults"],
+ init_rc: ["android.hardware.tv.tuner@1.0-service-lazy.rc"],
+ cflags: ["-DLAZY_SERVICE"],
+}
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
new file mode 100644
index 0000000..3dcc2b1
--- /dev/null
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "android.hardware.tv.tuner@1.0-Frontend"
+
+#include "Frontend.h"
+#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+Frontend::Frontend() {
+ // Init callback to nullptr
+ mCallback = nullptr;
+}
+
+Frontend::Frontend(FrontendType type, FrontendId id) {
+ mType = type;
+ mId = id;
+ // Init callback to nullptr
+ mCallback = nullptr;
+}
+
+Frontend::~Frontend() {}
+
+Return<Result> Frontend::close() {
+ ALOGV("%s", __FUNCTION__);
+ // Reset callback
+ mCallback = nullptr;
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Frontend::setCallback(const sp<IFrontendCallback>& callback) {
+ ALOGV("%s", __FUNCTION__);
+ if (callback == nullptr) {
+ ALOGW("[ WARN ] Set Frontend callback with nullptr");
+ return Result::INVALID_ARGUMENT;
+ }
+
+ mCallback = callback;
+ return Result::SUCCESS;
+}
+
+Return<Result> Frontend::tune(const FrontendSettings& /* settings */) {
+ ALOGV("%s", __FUNCTION__);
+ if (mCallback == nullptr) {
+ ALOGW("[ WARN ] Frontend callback is not set when tune");
+ return Result::INVALID_STATE;
+ }
+
+ mCallback->onEvent(FrontendEventType::NO_SIGNAL);
+ return Result::SUCCESS;
+}
+
+Return<Result> Frontend::stopTune() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+FrontendType Frontend::getFrontendType() {
+ return mType;
+}
+
+FrontendId Frontend::getFrontendId() {
+ return mId;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
new file mode 100644
index 0000000..f77a0d8
--- /dev/null
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_FRONTEND_H_
+#define ANDROID_HARDWARE_TV_TUNER_V1_0_FRONTEND_H_
+
+#include <android/hardware/tv/tuner/1.0/IFrontend.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::android::hardware::tv::tuner::V1_0::IFrontend;
+using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+class Frontend : public IFrontend {
+ public:
+ Frontend();
+ Frontend(FrontendType type, FrontendId id);
+
+ virtual Return<Result> close() override;
+
+ virtual Return<Result> setCallback(const sp<IFrontendCallback>& callback) override;
+
+ virtual Return<Result> tune(const FrontendSettings& settings) override;
+
+ virtual Return<Result> stopTune() override;
+
+ FrontendType getFrontendType();
+
+ FrontendId getFrontendId();
+
+ private:
+ virtual ~Frontend();
+ sp<IFrontendCallback> mCallback;
+ FrontendType mType = FrontendType::UNDEFINED;
+ FrontendId mId = 0;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_FRONTEND_H_
diff --git a/tv/tuner/1.0/default/OWNERS b/tv/tuner/1.0/default/OWNERS
new file mode 100644
index 0000000..1b3d095
--- /dev/null
+++ b/tv/tuner/1.0/default/OWNERS
@@ -0,0 +1,4 @@
+nchalko@google.com
+amyjojo@google.com
+shubang@google.com
+quxiangfang@google.com
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
new file mode 100644
index 0000000..67ec754
--- /dev/null
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "android.hardware.tv.tuner@1.0-Tuner"
+
+#include "Tuner.h"
+#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
+#include <utils/Log.h>
+#include "Frontend.h"
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+Tuner::Tuner() {
+ // Static Frontends array to maintain local frontends information
+ // Array index matches their FrontendId in the default impl
+ mFrontendSize = 8;
+ mFrontends.resize(mFrontendSize);
+ mFrontends[0] = new Frontend();
+ mFrontends[1] = new Frontend(FrontendType::ATSC, 1);
+ mFrontends[2] = new Frontend(FrontendType::DVBC, 2);
+ mFrontends[3] = new Frontend(FrontendType::DVBS, 3);
+ mFrontends[4] = new Frontend(FrontendType::DVBT, 4);
+ mFrontends[5] = new Frontend(FrontendType::ISDBT, 5);
+ mFrontends[6] = new Frontend(FrontendType::ANALOG, 6);
+ mFrontends[7] = new Frontend(FrontendType::ATSC, 7);
+}
+
+Tuner::~Tuner() {}
+
+Return<void> Tuner::getFrontendIds(getFrontendIds_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ vector<FrontendId> frontendIds;
+ frontendIds.resize(mFrontendSize);
+ for (int i = 0; i < mFrontendSize; i++) {
+ frontendIds[i] = mFrontends[i]->getFrontendId();
+ }
+
+ _hidl_cb(Result::SUCCESS, frontendIds);
+ return Void();
+}
+
+Return<void> Tuner::openFrontendById(uint32_t frontendId, openFrontendById_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ if (frontendId >= mFrontendSize || frontendId < 0) {
+ ALOGW("[ WARN ] Frontend with id %d isn't available", frontendId);
+ _hidl_cb(Result::UNAVAILABLE, nullptr);
+ return Void();
+ }
+
+ _hidl_cb(Result::SUCCESS, mFrontends[frontendId]);
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/tuner/1.0/default/Tuner.h b/tv/tuner/1.0/default/Tuner.h
new file mode 100644
index 0000000..2152c89
--- /dev/null
+++ b/tv/tuner/1.0/default/Tuner.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_TUNER_H_
+#define ANDROID_HARDWARE_TV_TUNER_V1_0_TUNER_H_
+
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include "Frontend.h"
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+class Tuner : public ITuner {
+ public:
+ Tuner();
+ virtual Return<void> getFrontendIds(getFrontendIds_cb _hidl_cb) override;
+
+ virtual Return<void> openFrontendById(uint32_t frontendId,
+ openFrontendById_cb _hidl_cb) override;
+
+ private:
+ virtual ~Tuner();
+ // Static mFrontends array to maintain local frontends information
+ vector<sp<Frontend>> mFrontends;
+ // To maintain how many Frontends we have
+ int mFrontendSize;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_TUNER_H_
diff --git a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.rc b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.rc
new file mode 100644
index 0000000..ad72fae
--- /dev/null
+++ b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.rc
@@ -0,0 +1,9 @@
+service vendor.tuner-hal-1-0 /vendor/bin/hw/android.hardware.tv.tuner@1.0-service-lazy
+ interface android.hardware.tv.tuner@1.0::ITuner default
+ oneshot
+ disabled
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.xml b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.xml
new file mode 100644
index 0000000..4bcfe10
--- /dev/null
+++ b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.tv.tuner</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>ITuner</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.rc b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.rc
new file mode 100644
index 0000000..6d59ed7
--- /dev/null
+++ b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.rc
@@ -0,0 +1,6 @@
+service vendor.tuner-hal-1-0 /vendor/bin/hw/android.hardware.tv.tuner@1.0-service
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.xml b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.xml
new file mode 100644
index 0000000..4bcfe10
--- /dev/null
+++ b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.tv.tuner</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>ITuner</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/service.cpp b/tv/tuner/1.0/default/service.cpp
new file mode 100644
index 0000000..581d269
--- /dev/null
+++ b/tv/tuner/1.0/default/service.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 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.
+ */
+
+//#define LOG_NDEBUG 0
+#ifdef LAZY_SERVICE
+#define LOG_TAG "android.hardware.tv.tuner@1.0-service-lazy"
+#else
+#define LOG_TAG "android.hardware.tv.tuner@1.0-service"
+#endif
+
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+
+#include "Tuner.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::LazyServiceRegistrar;
+using android::hardware::tv::tuner::V1_0::ITuner;
+using android::hardware::tv::tuner::V1_0::implementation::Tuner;
+
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
+int main() {
+ configureRpcThreadpool(8, true /* callerWillJoin */);
+
+ // Setup hwbinder service
+ android::sp<ITuner> service = new Tuner();
+ android::status_t status;
+ if (kLazyService) {
+ auto serviceRegistrar = std::make_shared<LazyServiceRegistrar>();
+ status = serviceRegistrar->registerService(service);
+ } else {
+ status = service->registerAsService();
+ }
+ LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering tuner service: %d", status);
+
+ joinRpcThreadpool();
+ return 0;
+}
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
new file mode 100644
index 0000000..1ca7fda
--- /dev/null
+++ b/tv/tuner/1.0/types.hal
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 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 android.hardware.tv.tuner@1.0;
+
+import android.hidl.safe_union@1.0;
+
+@export
+enum Result : int32_t {
+ SUCCESS,
+ UNAVAILABLE,
+ NOT_INITIALIZED,
+ INVALID_STATE,
+ INVALID_ARGUMENT,
+ OUT_OF_MEMORY,
+ UNKNOWN_ERROR,
+};
+
+/**
+ * Frontend ID.
+ */
+typedef uint32_t FrontendId;
+
+/**
+ * Frontend Types.
+ */
+@export
+enum FrontendType : uint32_t {
+ UNDEFINED = 0,
+ ANALOG,
+ ATSC,
+ DVBC,
+ DVBS,
+ DVBT,
+ ISDBT,
+};
+
+/**
+ * Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
+ * It's a 4-bit field specifying the inner FEC scheme used according to the
+ * table 35 in the spec.
+ */
+@export
+enum FrontendInnerFec : uint32_t {
+ /* Not defined */
+ FEC_UNDEFINED = 0,
+ /* 1/2 conv. code rate */
+ FEC_1_2 = 1 << 0,
+ /* 2/3 conv. code rate */
+ FEC_2_3 = 1 << 1,
+ /* 3/4 conv. code rate */
+ FEC_3_4 = 1 << 2,
+ /* 5/6 conv. code rate */
+ FEC_5_6 = 1 << 3,
+ /* 7/8 conv. code rate */
+ FEC_7_8 = 1 << 4,
+ /* 8/9 conv. code rate */
+ FEC_8_9 = 1 << 5,
+ /* 3/5 conv. code rate */
+ FEC_3_5 = 1 << 6,
+ /* 4/5 conv. code rate */
+ FEC_4_5 = 1 << 7,
+ /* 9/10 conv. code rate */
+ FEC_9_10 = 1 << 8,
+ /* hardware is able to detect and set FEC automatically */
+ FEC_AUTO = 1 << 9,
+};
+
+/**
+ * Modulation Type for ATSC.
+ */
+@export
+enum FrontendAtscModulation : uint32_t {
+ UNDEFINED = 0,
+ MOD_8VSB = 1 << 0,
+ MOD_16VSB = 1 << 1,
+};
+
+/**
+ * Signal Setting for ATSC Frontend.
+ */
+struct FrontendAtscSettings {
+ /** Signal frequencey in Herhz */
+ uint32_t frequency;
+ FrontendAtscModulation modulation;
+};
+
+/**
+ * Signal Setting for DVBT Frontend.
+ */
+struct FrontendDvbtSettings {
+ /** Signal frequencey in Herhz */
+ uint32_t frequency;
+ FrontendAtscModulation modulation;
+ FrontendInnerFec fec;
+};
+
+/**
+ * Modulation Type for ATSC.
+ */
+safe_union FrontendSettings {
+ FrontendAtscSettings atsc;
+ FrontendDvbtSettings dvbt;
+};
+
+/**
+ * Frontend Event Type.
+ */
+@export
+enum FrontendEventType : uint32_t {
+ /**
+ * If frontend locked the signal which is specified by tune method, HAL sent
+ * Locked event.
+ */
+ LOCKED,
+ /**
+ * If frontend can't locked the signal which is specified by tune method,
+ * HAL sent NO_SIGNAL event.
+ */
+ NO_SIGNAL,
+ /**
+ * If frontend detect that the locked signal get lost, HAL sent LOST_LOCK
+ * event.
+ */
+ LOST_LOCK,
+};
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..faf566c
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_test {
+ name: "VtsHalTvTunerV1_0TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalTvTunerV1_0TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.tv.tuner@1.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlallocatorutils",
+ "libhidlmemory",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
new file mode 100644
index 0000000..4840a02
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "Tuner_hidl_hal_test"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/tv/tuner/1.0/IFrontend.h>
+#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <binder/MemoryDealer.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#define WAIT_TIMEOUT 3000000000
+
+using android::Condition;
+using android::IMemory;
+using android::IMemoryHeap;
+using android::MemoryDealer;
+using android::Mutex;
+using android::sp;
+using android::hardware::fromHeap;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::HidlMemory;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using android::hardware::tv::tuner::V1_0::FrontendAtscSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
+using android::hardware::tv::tuner::V1_0::FrontendEventType;
+using android::hardware::tv::tuner::V1_0::FrontendId;
+using android::hardware::tv::tuner::V1_0::FrontendInnerFec;
+using android::hardware::tv::tuner::V1_0::FrontendSettings;
+using android::hardware::tv::tuner::V1_0::IFrontend;
+using android::hardware::tv::tuner::V1_0::IFrontendCallback;
+using android::hardware::tv::tuner::V1_0::ITuner;
+using android::hardware::tv::tuner::V1_0::Result;
+
+namespace {
+
+class FrontendCallback : public IFrontendCallback {
+ public:
+ virtual Return<void> onEvent(FrontendEventType frontendEventType) override {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ mEventReceived = true;
+ mEventType = frontendEventType;
+ mMsgCondition.signal();
+ return Void();
+ }
+
+ virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ mDiseqcMessageReceived = true;
+ mEventMessage = diseqcMessage;
+ mMsgCondition.signal();
+ return Void();
+ }
+
+ void testOnEvent(sp<IFrontend>& frontend, FrontendSettings settings);
+ void testOnDiseqcMessage(sp<IFrontend>& frontend, FrontendSettings settings);
+
+ private:
+ bool mEventReceived = false;
+ bool mDiseqcMessageReceived = false;
+ FrontendEventType mEventType;
+ hidl_vec<uint8_t> mEventMessage;
+ android::Mutex mMsgLock;
+ android::Condition mMsgCondition;
+};
+
+void FrontendCallback::testOnEvent(sp<IFrontend>& frontend, FrontendSettings settings) {
+ Result result = frontend->tune(settings);
+
+ EXPECT_TRUE(result == Result::SUCCESS);
+
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mEventReceived) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "event not received within timeout";
+ return;
+ }
+ }
+}
+
+void FrontendCallback::testOnDiseqcMessage(sp<IFrontend>& frontend, FrontendSettings settings) {
+ Result result = frontend->tune(settings);
+
+ EXPECT_TRUE(result == Result::SUCCESS);
+
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mDiseqcMessageReceived) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "diseqc message not received within timeout";
+ return;
+ }
+ }
+}
+
+// Test environment for Tuner HIDL HAL.
+class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+ // get the test environment singleton
+ static TunerHidlEnvironment* Instance() {
+ static TunerHidlEnvironment* instance = new TunerHidlEnvironment;
+ return instance;
+ }
+
+ virtual void registerTestServices() override { registerTestService<ITuner>(); }
+};
+
+class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ mService = ::testing::VtsHalHidlTargetTestBase::getService<ITuner>(
+ TunerHidlEnvironment::Instance()->getServiceName<ITuner>());
+ ASSERT_NE(mService, nullptr);
+ }
+
+ sp<ITuner> mService;
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ sp<IFrontend> mFrontend;
+ sp<FrontendCallback> mFrontendCallback;
+
+ ::testing::AssertionResult createFrontend(int32_t frontendId);
+ ::testing::AssertionResult tuneFrontend(int32_t frontendId);
+ ::testing::AssertionResult stopTuneFrontend(int32_t frontendId);
+ ::testing::AssertionResult closeFrontend(int32_t frontendId);
+};
+
+::testing::AssertionResult TunerHidlTest::createFrontend(int32_t frontendId) {
+ Result status;
+
+ mService->openFrontendById(frontendId, [&](Result result, const sp<IFrontend>& frontend) {
+ mFrontend = frontend;
+ status = result;
+ });
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+
+ mFrontendCallback = new FrontendCallback();
+ auto callbackStatus = mFrontend->setCallback(mFrontendCallback);
+
+ return ::testing::AssertionResult(callbackStatus.isOk());
+}
+
+::testing::AssertionResult TunerHidlTest::tuneFrontend(int32_t frontendId) {
+ if (createFrontend(frontendId) == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+
+ // Frontend Settings for testing
+ FrontendSettings frontendSettings;
+ FrontendAtscSettings frontendAtscSettings{
+ .frequency = 0,
+ .modulation = FrontendAtscModulation::UNDEFINED,
+ };
+ frontendSettings.atsc() = frontendAtscSettings;
+ mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
+
+ FrontendDvbtSettings frontendDvbtSettings{
+ .frequency = 0,
+ .modulation = FrontendAtscModulation::UNDEFINED,
+ .fec = FrontendInnerFec::FEC_UNDEFINED,
+ };
+ frontendSettings.dvbt(frontendDvbtSettings);
+ mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
+
+ return ::testing::AssertionResult(true);
+}
+
+::testing::AssertionResult TunerHidlTest::stopTuneFrontend(int32_t frontendId) {
+ Result status;
+ if (createFrontend(frontendId) == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+
+ status = mFrontend->stopTune();
+ return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
+::testing::AssertionResult TunerHidlTest::closeFrontend(int32_t frontendId) {
+ Result status;
+ if (createFrontend(frontendId) == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+
+ status = mFrontend->close();
+ return ::testing::AssertionResult(status == Result::SUCCESS);
+}
+
+TEST_F(TunerHidlTest, CreateFrontend) {
+ Result status;
+ hidl_vec<FrontendId> feIds;
+
+ description("Create Frontends");
+ mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+ status = result;
+ feIds = frontendIds;
+ });
+
+ if (feIds.size() == 0) {
+ ALOGW("[ WARN ] Frontend isn't available");
+ return;
+ }
+
+ for (size_t i = 0; i < feIds.size(); i++) {
+ ASSERT_TRUE(createFrontend(feIds[i]));
+ }
+}
+
+TEST_F(TunerHidlTest, TuneFrontend) {
+ Result status;
+ hidl_vec<FrontendId> feIds;
+
+ description("Tune Frontends and check callback onEvent");
+ mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+ status = result;
+ feIds = frontendIds;
+ });
+
+ if (feIds.size() == 0) {
+ ALOGW("[ WARN ] Frontend isn't available");
+ return;
+ }
+
+ for (size_t i = 0; i < feIds.size(); i++) {
+ ASSERT_TRUE(tuneFrontend(feIds[i]));
+ }
+}
+
+TEST_F(TunerHidlTest, StopTuneFrontend) {
+ Result status;
+ hidl_vec<FrontendId> feIds;
+
+ description("stopTune Frontends");
+ mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+ status = result;
+ feIds = frontendIds;
+ });
+
+ if (feIds.size() == 0) {
+ ALOGW("[ WARN ] Frontend isn't available");
+ return;
+ }
+
+ for (size_t i = 0; i < feIds.size(); i++) {
+ ASSERT_TRUE(stopTuneFrontend(feIds[i]));
+ }
+}
+
+TEST_F(TunerHidlTest, CloseFrontend) {
+ Result status;
+ hidl_vec<FrontendId> feIds;
+
+ description("Close Frontends");
+ mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+ status = result;
+ feIds = frontendIds;
+ });
+
+ if (feIds.size() == 0) {
+ ALOGW("[ WARN ] Frontend isn't available");
+ return;
+ }
+
+ for (size_t i = 0; i < feIds.size(); i++) {
+ ASSERT_TRUE(closeFrontend(feIds[i]));
+ }
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ ::testing::AddGlobalTestEnvironment(TunerHidlEnvironment::Instance());
+ ::testing::InitGoogleTest(&argc, argv);
+ TunerHidlEnvironment::Instance()->init(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/wifi/1.3/default/service.cpp b/wifi/1.3/default/service.cpp
index fcbc37c..0b41d28 100644
--- a/wifi/1.3/default/service.cpp
+++ b/wifi/1.3/default/service.cpp
@@ -58,7 +58,7 @@
std::make_shared<WifiIfaceUtil>(iface_tool),
std::make_shared<WifiFeatureFlags>());
if (kLazyService) {
- LazyServiceRegistrar registrar;
+ auto registrar = LazyServiceRegistrar::getInstance();
CHECK_EQ(registrar.registerService(service), android::NO_ERROR)
<< "Failed to register wifi HAL";
} else {