diff options
372 files changed, 24811 insertions, 3840 deletions
diff --git a/audio/6.0/config/api/current.txt b/audio/6.0/config/api/current.txt index 431bc90561..ddd4d1ccc6 100644 --- a/audio/6.0/config/api/current.txt +++ b/audio/6.0/config/api/current.txt @@ -252,7 +252,9 @@ package audio.policy.configuration.V6_0 { public class GlobalConfiguration { ctor public GlobalConfiguration(); + method public boolean getCall_screen_mode_supported(); method public boolean getSpeaker_drc_enabled(); + method public void setCall_screen_mode_supported(boolean); method public void setSpeaker_drc_enabled(boolean); } diff --git a/audio/6.0/config/audio_policy_configuration.xsd b/audio/6.0/config/audio_policy_configuration.xsd index d0f80ea4b9..3fab7dc151 100644 --- a/audio/6.0/config/audio_policy_configuration.xsd +++ b/audio/6.0/config/audio_policy_configuration.xsd @@ -66,6 +66,7 @@ </xs:element> <xs:complexType name="globalConfiguration"> <xs:attribute name="speaker_drc_enabled" type="xs:boolean" use="required"/> + <xs:attribute name="call_screen_mode_supported" type="xs:boolean" use="optional"/> </xs:complexType> <xs:complexType name="modules"> <xs:annotation> diff --git a/audio/README b/audio/README index abe979c0fa..afafbe32d2 100644 --- a/audio/README +++ b/audio/README @@ -1,5 +1,8 @@ Directory structure of the audio HIDL related code. +Run `common/all-versions/copyHAL.sh` to create a new version of the audio HAL +based on an existing one. + audio |-- 2.0 <== core 2.0 HIDL API. .hal can not be moved into the core directory | because that would change its namespace and include path @@ -11,13 +14,13 @@ audio | |-- 2.0 <== HIDL API of V2 | |-- 4.0 | |-- ... -| `-- all_versions <== code common to all version of both core and effect API +| `-- all-versions <== code common to all version of both core and effect API | |-- default <== implementation shared code between core and effect impl | |-- test <== utilities used by tests | `-- util <== utilities used by both implementation and tests | |-- core <== VTS and default implementation of the core API (not HIDL, see /audio/2.0)) -| `-- all_versions <== Code is version independent through #if and separate files +| `-- all-versions <== Code is version independent through #if and separate files | |-- default <== code that wraps the legacy API | `-- vts <== vts of core API | |-- 2.0 <== 2.0 specific tests and helpers @@ -28,6 +31,6 @@ audio |-- 2.0 |-- 4.0 |-- ... - `-- all_versions + `-- all-versions |-- default `-- vts diff --git a/audio/common/6.0/types.hal b/audio/common/6.0/types.hal index 132f86dc4e..0c97c3686c 100644 --- a/audio/common/6.0/types.hal +++ b/audio/common/6.0/types.hal @@ -556,6 +556,8 @@ enum AudioMode : int32_t { IN_CALL = 2, /** Calls handled by apps (Eg: Hangout). */ IN_COMMUNICATION = 3, + /** Call screening in progress */ + CALL_SCREEN = 4, }; @export(name="", value_prefix="AUDIO_DEVICE_") diff --git a/audio/core/all-versions/default/Android.bp b/audio/core/all-versions/default/Android.bp index 007ad8594c..b8b7feeed0 100644 --- a/audio/core/all-versions/default/Android.bp +++ b/audio/core/all-versions/default/Android.bp @@ -19,12 +19,14 @@ cc_defaults { export_include_dirs: ["include"], shared_libs: [ + "libaudiofoundation", "libbase", "libcutils", "libfmq", "libhardware", "libhidlbase", "liblog", + "libmedia_helper", "libutils", "android.hardware.audio.common-util", ], @@ -36,10 +38,6 @@ cc_defaults { "libhardware_headers", "libmedia_headers", ], - - whole_static_libs: [ - "libmedia_helper", - ], } cc_library_shared { diff --git a/audio/core/all-versions/default/Stream.cpp b/audio/core/all-versions/default/Stream.cpp index 5f24a5d781..74e59450f0 100644 --- a/audio/core/all-versions/default/Stream.cpp +++ b/audio/core/all-versions/default/Stream.cpp @@ -26,9 +26,8 @@ #include <android/log.h> #include <hardware/audio.h> #include <hardware/audio_effect.h> +#include <media/AudioContainers.h> #include <media/TypeConverter.h> -#include <utils/SortedVector.h> -#include <utils/Vector.h> namespace android { namespace hardware { @@ -100,11 +99,11 @@ Return<void> Stream::getSupportedSampleRates(AudioFormat format, Result result = getParam(AudioParameter::keyStreamSupportedSamplingRates, &halListValue, context); hidl_vec<uint32_t> sampleRates; - SortedVector<uint32_t> halSampleRates; + SampleRateSet halSampleRates; if (result == Result::OK) { halSampleRates = samplingRatesFromString(halListValue.string(), AudioParameter::valueListSeparator); - sampleRates.setToExternal(halSampleRates.editArray(), halSampleRates.size()); + sampleRates = hidl_vec<uint32_t>(halSampleRates.begin(), halSampleRates.end()); // Legacy get_parameter does not return a status_t, thus can not advertise of failure. // Note that this method must succeed (non empty list) if the format is supported. if (sampleRates.size() == 0) { @@ -126,13 +125,14 @@ Return<void> Stream::getSupportedChannelMasks(AudioFormat format, String8 halListValue; Result result = getParam(AudioParameter::keyStreamSupportedChannels, &halListValue, context); hidl_vec<AudioChannelBitfield> channelMasks; - SortedVector<audio_channel_mask_t> halChannelMasks; + ChannelMaskSet halChannelMasks; if (result == Result::OK) { halChannelMasks = channelMasksFromString(halListValue.string(), AudioParameter::valueListSeparator); channelMasks.resize(halChannelMasks.size()); - for (size_t i = 0; i < halChannelMasks.size(); ++i) { - channelMasks[i] = AudioChannelBitfield(halChannelMasks[i]); + size_t i = 0; + for (auto channelMask : halChannelMasks) { + channelMasks[i++] = AudioChannelBitfield(channelMask); } // Legacy get_parameter does not return a status_t, thus can not advertise of failure. // Note that this method must succeed (non empty list) if the format is supported. @@ -168,7 +168,7 @@ Return<void> Stream::getSupportedFormats(getSupportedFormats_cb _hidl_cb) { String8 halListValue; Result result = getParam(AudioParameter::keyStreamSupportedFormats, &halListValue); hidl_vec<AudioFormat> formats; - Vector<audio_format_t> halFormats; + FormatVector halFormats; if (result == Result::OK) { halFormats = formatsFromString(halListValue.string(), AudioParameter::valueListSeparator); formats.resize(halFormats.size()); diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp index 3286ecb0a3..1818e36112 100644 --- a/audio/core/all-versions/vts/functional/Android.bp +++ b/audio/core/all-versions/vts/functional/Android.bp @@ -19,11 +19,13 @@ cc_defaults { defaults: ["VtsHalTargetTestDefaults"], static_libs: [ "android.hardware.audio.common.test.utility", + "libaudiofoundation", "libaudiopolicycomponents", "libmedia_helper", "libxml2", ], shared_libs: [ + "libbinder", "libfmq", ], header_libs: [ diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp index 0afa779f03..e11e123038 100644 --- a/audio/effect/all-versions/default/Effect.cpp +++ b/audio/effect/all-versions/default/Effect.cpp @@ -307,12 +307,11 @@ void Effect::getConfigImpl(int commandCode, const char* commandName, GetConfigCa Result Effect::getCurrentConfigImpl(uint32_t featureId, uint32_t configSize, GetCurrentConfigSuccessCallback onSuccess) { uint32_t halCmd = featureId; - uint32_t halResult[alignedSizeIn<uint32_t>(sizeof(uint32_t) + configSize)]; - memset(halResult, 0, sizeof(halResult)); + std::vector<uint32_t> halResult(alignedSizeIn<uint32_t>(sizeof(uint32_t) + configSize), 0); uint32_t halResultSize = 0; - return sendCommandReturningStatusAndData(EFFECT_CMD_GET_FEATURE_CONFIG, "GET_FEATURE_CONFIG", - sizeof(uint32_t), &halCmd, &halResultSize, halResult, - sizeof(uint32_t), [&] { onSuccess(&halResult[1]); }); + return sendCommandReturningStatusAndData( + EFFECT_CMD_GET_FEATURE_CONFIG, "GET_FEATURE_CONFIG", sizeof(uint32_t), &halCmd, + &halResultSize, &halResult[0], sizeof(uint32_t), [&] { onSuccess(&halResult[1]); }); } Result Effect::getParameterImpl(uint32_t paramSize, const void* paramData, @@ -339,8 +338,7 @@ Result Effect::getSupportedConfigsImpl(uint32_t featureId, uint32_t maxConfigs, GetSupportedConfigsSuccessCallback onSuccess) { uint32_t halCmd[2] = {featureId, maxConfigs}; uint32_t halResultSize = 2 * sizeof(uint32_t) + maxConfigs * sizeof(configSize); - uint8_t halResult[halResultSize]; - memset(&halResult[0], 0, halResultSize); + std::vector<uint8_t> halResult(static_cast<size_t>(halResultSize), 0); return sendCommandReturningStatusAndData( EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS, "GET_FEATURE_SUPPORTED_CONFIGS", sizeof(halCmd), halCmd, &halResultSize, &halResult[0], 2 * sizeof(uint32_t), [&] { @@ -519,9 +517,9 @@ Return<void> Effect::setAndGetVolume(const hidl_vec<uint32_t>& volumes, uint32_t halDataSize; std::unique_ptr<uint8_t[]> halData = hidlVecToHal(volumes, &halDataSize); uint32_t halResultSize = halDataSize; - uint32_t halResult[volumes.size()]; + std::vector<uint32_t> halResult(volumes.size(), 0); Result retval = sendCommandReturningData(EFFECT_CMD_SET_VOLUME, "SET_VOLUME", halDataSize, - &halData[0], &halResultSize, halResult); + &halData[0], &halResultSize, &halResult[0]); hidl_vec<uint32_t> result; if (retval == Result::OK) { result.setToExternal(&halResult[0], halResultSize); @@ -581,8 +579,6 @@ Return<void> Effect::getSupportedAuxChannelsConfigs(uint32_t maxConfigs, } Return<void> Effect::getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) { - uint32_t halResult[alignedSizeIn<uint32_t>(sizeof(uint32_t) + sizeof(channel_config_t))]; - memset(halResult, 0, sizeof(halResult)); EffectAuxChannelsConfig result; Result retval = getCurrentConfigImpl( EFFECT_FEATURE_AUX_CHANNELS, sizeof(channel_config_t), [&](void* configData) { @@ -594,11 +590,12 @@ Return<void> Effect::getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) { } Return<Result> Effect::setAuxChannelsConfig(const EffectAuxChannelsConfig& config) { - uint32_t halCmd[alignedSizeIn<uint32_t>(sizeof(uint32_t) + sizeof(channel_config_t))]; + std::vector<uint32_t> halCmd( + alignedSizeIn<uint32_t>(sizeof(uint32_t) + sizeof(channel_config_t)), 0); halCmd[0] = EFFECT_FEATURE_AUX_CHANNELS; effectAuxChannelsConfigToHal(config, reinterpret_cast<channel_config_t*>(&halCmd[1])); return sendCommandReturningStatus(EFFECT_CMD_SET_FEATURE_CONFIG, - "SET_FEATURE_CONFIG AUX_CHANNELS", sizeof(halCmd), halCmd); + "SET_FEATURE_CONFIG AUX_CHANNELS", halCmd.size(), &halCmd[0]); } Return<Result> Effect::setAudioSource(AudioSource source) { @@ -692,12 +689,11 @@ Return<void> Effect::getCurrentConfigForFeature(uint32_t featureId, uint32_t con Return<Result> Effect::setCurrentConfigForFeature(uint32_t featureId, const hidl_vec<uint8_t>& configData) { - uint32_t halCmd[alignedSizeIn<uint32_t>(sizeof(uint32_t) + configData.size())]; - memset(halCmd, 0, sizeof(halCmd)); + std::vector<uint32_t> halCmd(alignedSizeIn<uint32_t>(sizeof(uint32_t) + configData.size()), 0); halCmd[0] = featureId; memcpy(&halCmd[1], &configData[0], configData.size()); return sendCommandReturningStatus(EFFECT_CMD_SET_FEATURE_CONFIG, "SET_FEATURE_CONFIG", - sizeof(halCmd), halCmd); + halCmd.size(), &halCmd[0]); } Return<Result> Effect::close() { diff --git a/automotive/OWNERS b/automotive/OWNERS index 3cf4489e9a..83ee63c397 100644 --- a/automotive/OWNERS +++ b/automotive/OWNERS @@ -1,4 +1,5 @@ -randolphs@google.com pirozzoj@google.com twasilczyk@google.com pfg@google.com +gurunagarajan@google.com +keunyoung@google.com diff --git a/automotive/audiocontrol/1.0/IAudioControl.hal b/automotive/audiocontrol/1.0/IAudioControl.hal index 3c8b086bc6..2e7ef75380 100644 --- a/automotive/audiocontrol/1.0/IAudioControl.hal +++ b/automotive/audiocontrol/1.0/IAudioControl.hal @@ -29,6 +29,11 @@ interface IAudioControl { * * For every context, a valid bus number (0 - num busses-1) must be returned. If an * unrecognized contextNumber is encountered, then -1 shall be returned. + * + * Deprecated: usage of this API and car_volume_groups.xml has been replaced with + * car_audio_configuration.xml. If using car_audio_configuration.xml, then the framework + * will not call this method. If it doesn't, then it will load car_volume_groups.xml and + * call this method. */ getBusForContext(ContextNumber contextNumber) generates (int32_t busNumber); diff --git a/automotive/audiocontrol/1.0/default/Android.bp b/automotive/audiocontrol/1.0/default/Android.bp index 314830b92a..3d04c890bc 100644 --- a/automotive/audiocontrol/1.0/default/Android.bp +++ b/automotive/audiocontrol/1.0/default/Android.bp @@ -29,7 +29,7 @@ cc_binary { "liblog", "libutils", ], - + vintf_fragments: ["audiocontrol_manifest.xml"], cflags: [ "-DLOG_TAG=\"AudCntrlDrv\"", "-O0", diff --git a/automotive/audiocontrol/1.0/default/audiocontrol_manifest.xml b/automotive/audiocontrol/1.0/default/audiocontrol_manifest.xml new file mode 100644 index 0000000000..0981eb71ad --- /dev/null +++ b/automotive/audiocontrol/1.0/default/audiocontrol_manifest.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="device"> + <hal format="hidl"> + <name>android.hardware.automotive.audiocontrol</name> + <transport>hwbinder</transport> + <version>1.0</version> + <interface> + <name>IAudioControl</name> + <instance>default</instance> + </interface> + </hal> +</manifest>
\ No newline at end of file diff --git a/automotive/can/1.0/Android.bp b/automotive/can/1.0/Android.bp new file mode 100644 index 0000000000..2221e6623e --- /dev/null +++ b/automotive/can/1.0/Android.bp @@ -0,0 +1,21 @@ +// 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", + "ICanErrorListener.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 0000000000..e68f16c6fc --- /dev/null +++ b/automotive/can/1.0/ICanBus.hal @@ -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. + */ +package android.hardware.automotive.can@1.0; + +import ICanErrorListener; +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); + + /** + * Adds a new listener for CAN bus or interface errors. + * + * If the error is fatal, the client is supposed to drop any references to + * this specific ICanBus instance (see ICanErrorListener). + * + * @param listener The interface to receive the error events on + * @return close A handle to call in order to remove the listener + */ + listenForErrors(ICanErrorListener listener) generates (ICloseHandle close); +}; diff --git a/automotive/can/1.0/ICanController.hal b/automotive/can/1.0/ICanController.hal new file mode 100644 index 0000000000..2c7494a907 --- /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/ICanErrorListener.hal b/automotive/can/1.0/ICanErrorListener.hal new file mode 100644 index 0000000000..8a6ba054bc --- /dev/null +++ b/automotive/can/1.0/ICanErrorListener.hal @@ -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. + */ +package android.hardware.automotive.can@1.0; + +/** + * CAN error listener. + */ +interface ICanErrorListener { + /** + * Called on error event. + * + * If the error is fatal, the client is supposed to drop any references to + * this specific ICanBus instance. + * + * @param error Error type + * @param isFatal Whether an error would result with ICanBus instance being unusable. + */ + onError(ErrorEvent error, bool isFatal); +}; diff --git a/automotive/can/1.0/ICanMessageListener.hal b/automotive/can/1.0/ICanMessageListener.hal new file mode 100644 index 0000000000..28161fa747 --- /dev/null +++ b/automotive/can/1.0/ICanMessageListener.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; + +/** + * 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); +}; diff --git a/automotive/can/1.0/ICloseHandle.hal b/automotive/can/1.0/ICloseHandle.hal new file mode 100644 index 0000000000..924c58bfff --- /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 0000000000..ee2e92bdb1 --- /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", + "CanBusSlcan.cpp", + "CanController.cpp", + "CanSocket.cpp", + "CloseHandle.cpp", + "service.cpp", + ], + shared_libs: [ + "android.hardware.automotive.can@1.0", + "libhidlbase", + ], + 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 0000000000..42d2e3c277 --- /dev/null +++ b/automotive/can/1.0/default/CanBus.cpp @@ -0,0 +1,296 @@ +/* + * 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(mMsgListenersGuard); + + sp<CloseHandle> closeHandle = new CloseHandle([this, listenerCb]() { + std::lock_guard<std::mutex> lck(mMsgListenersGuard); + std::erase_if(mMsgListeners, [&](const auto& e) { return e.callback == listenerCb; }); + }); + mMsgListeners.emplace_back(CanMessageListener{listenerCb, filter, closeHandle}); + auto& listener = mMsgListeners.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() {} + +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(mMsgListenersGuard); + CHECK(mMsgListeners.empty()) << "Listener list is not empty while interface is being destroyed"; +} + +void CanBus::setErrorCallback(ErrorCallback errcb) { + CHECK(!mIsUp) << "Can't set error callback while interface is up"; + CHECK(mErrCb == nullptr) << "Error callback is already set"; + mErrCb = errcb; + CHECK(!mIsUp) << "Can't set error callback while interface is up"; +} + +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; + } + + if (!*isUp && !netdevice::up(mIfname)) { + LOG(ERROR) << "Can't bring " << mIfname << " up"; + return ICanController::Result::UNKNOWN_ERROR; + } + mDownAfterUse = !*isUp; + + using namespace std::placeholders; + CanSocket::ReadCallback rdcb = std::bind(&CanBus::onRead, this, _1, _2); + CanSocket::ErrorCallback errcb = std::bind(&CanBus::onError, this, _1); + mSocket = CanSocket::open(mIfname, rdcb, errcb); + if (!mSocket) { + if (mDownAfterUse) netdevice::down(mIfname); + return ICanController::Result::UNKNOWN_ERROR; + } + + mIsUp = true; + return ICanController::Result::OK; +} + +void CanBus::clearMsgListeners() { + std::vector<wp<ICloseHandle>> listenersToClose; + { + std::lock_guard<std::mutex> lck(mMsgListenersGuard); + std::transform(mMsgListeners.begin(), mMsgListeners.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 mMsgListeners 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(mMsgListenersGuard); + CHECK(mMsgListeners.empty()) << "Listeners list wasn't emptied"; +} + +void CanBus::clearErrListeners() { + std::lock_guard<std::mutex> lck(mErrListenersGuard); + mErrListeners.clear(); +} + +Return<sp<ICloseHandle>> CanBus::listenForErrors(const sp<ICanErrorListener>& listener) { + if (listener == nullptr) { + return new CloseHandle(); + } + + std::lock_guard<std::mutex> upLck(mIsUpGuard); + if (!mIsUp) { + listener->onError(ErrorEvent::INTERFACE_DOWN, true); + return new CloseHandle(); + } + + std::lock_guard<std::mutex> errLck(mErrListenersGuard); + mErrListeners.emplace_back(listener); + + return new CloseHandle([this, listener]() { + std::lock_guard<std::mutex> lck(mErrListenersGuard); + std::erase(mErrListeners, listener); + }); +} + +bool CanBus::down() { + std::lock_guard<std::mutex> lck(mIsUpGuard); + + if (!mIsUp) { + LOG(WARNING) << "Interface is already down"; + return false; + } + mIsUp = false; + + clearMsgListeners(); + clearErrListeners(); + mSocket.reset(); + + bool success = true; + + if (mDownAfterUse && !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(mMsgListenersGuard); + for (auto& listener : mMsgListeners) { + if (!match(listener.filter, message.id)) continue; + if (!listener.callback->onReceive(message).isOk() && !listener.failedOnce) { + listener.failedOnce = true; + LOG(WARNING) << "Failed to notify listener about message"; + } + } +} + +void CanBus::onError(int errnoVal) { + auto eventType = ErrorEvent::HARDWARE_ERROR; + + if (errnoVal == ENODEV || errnoVal == ENETDOWN) { + mDownAfterUse = false; + eventType = ErrorEvent::INTERFACE_DOWN; + } + + { + std::lock_guard<std::mutex> lck(mErrListenersGuard); + for (auto& listener : mErrListeners) { + if (!listener->onError(eventType, true).isOk()) { + LOG(WARNING) << "Failed to notify listener about error"; + } + } + } + + const auto errcb = mErrCb; + if (errcb != nullptr) errcb(); +} + +} // 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 0000000000..365e90c439 --- /dev/null +++ b/automotive/can/1.0/default/CanBus.h @@ -0,0 +1,120 @@ +/* + * 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 { + using ErrorCallback = std::function<void()>; + + 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; + Return<sp<ICloseHandle>> listenForErrors(const sp<ICanErrorListener>& listener) override; + + void setErrorCallback(ErrorCallback errcb); + ICanController::Result up(); + bool down(); + + protected: + /** + * Blank constructor, since some interface types (such as SLCAN) don't get a name until after + * being initialized. + * + * If using this constructor, you MUST initialize mIfname prior to the completion of preUp(). + */ + CanBus(); + + CanBus(const std::string& ifname); + + /** + * Prepare the SocketCAN interface. + * + * After calling this method, mIfname network interface is available and ready to be brought up. + * + * \return OK on success, or an error state on failure. See ICanController::Result + */ + virtual ICanController::Result preUp(); + + /** + * Cleanup after bringing the interface down. + * + * This is a counterpart to preUp(). + * + * \return true upon success and false upon failure + */ + virtual bool postDown(); + + /** Network interface name. */ + std::string mIfname; + + private: + struct CanMessageListener { + sp<ICanMessageListener> callback; + hidl_vec<CanMessageFilter> filter; + wp<ICloseHandle> closeHandle; + bool failedOnce = false; + }; + void clearMsgListeners(); + void clearErrListeners(); + + void onRead(const struct canfd_frame& frame, std::chrono::nanoseconds timestamp); + void onError(int errnoVal); + + std::mutex mMsgListenersGuard; + std::vector<CanMessageListener> mMsgListeners GUARDED_BY(mMsgListenersGuard); + + std::mutex mErrListenersGuard; + std::vector<sp<ICanErrorListener>> mErrListeners GUARDED_BY(mErrListenersGuard); + + std::unique_ptr<CanSocket> mSocket; + bool mDownAfterUse; + + /** + * 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; + + ErrorCallback mErrCb; +}; + +} // 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 0000000000..365b749cd7 --- /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 0000000000..126f1cb77c --- /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/CanBusSlcan.cpp b/automotive/can/1.0/default/CanBusSlcan.cpp new file mode 100644 index 0000000000..7dce838ff1 --- /dev/null +++ b/automotive/can/1.0/default/CanBusSlcan.cpp @@ -0,0 +1,166 @@ +/* + * 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 "CanBusSlcan.h" + +#include <android-base/logging.h> +#include <libnetdevice/can.h> +#include <libnetdevice/libnetdevice.h> + +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <termios.h> + +namespace android { +namespace hardware { +namespace automotive { +namespace can { +namespace V1_0 { +namespace implementation { + +namespace slcanprotocol { +static const std::string kOpenCommand = "O\r"; +static const std::string kCloseCommand = "C\r"; +static constexpr int kSlcanDiscipline = N_SLCAN; +static constexpr int kDefaultDiscipline = N_TTY; + +static const std::map<uint32_t, std::string> kBitrateCommands = { + {10000, "C\rS0\r"}, {20000, "C\rS1\r"}, {50000, "C\rS2\r"}, + {100000, "C\rS3\r"}, {125000, "C\rS4\r"}, {250000, "C\rS5\r"}, + {500000, "C\rS6\r"}, {800000, "C\rS7\r"}, {1000000, "C\rS8\r"}}; +} // namespace slcanprotocol + +/** + * Serial Line CAN constructor + * \param string uartName - name of slcan device (e.x. /dev/ttyUSB0) + * \param uint32_t bitrate - speed of the CAN bus (125k = MSCAN, 500k = HSCAN) + */ +CanBusSlcan::CanBusSlcan(const std::string& uartName, uint32_t bitrate) + : CanBus(), mUartName(uartName), kBitrate(bitrate) {} + +ICanController::Result CanBusSlcan::preUp() { + // verify valid bitrate and translate to serial command format + const auto lookupIt = slcanprotocol::kBitrateCommands.find(kBitrate); + if (lookupIt == slcanprotocol::kBitrateCommands.end()) { + return ICanController::Result::BAD_BAUDRATE; + } + const auto canBitrateCommand = lookupIt->second; + + /* Attempt to open the uart in r/w without blocking or becoming the + * controlling terminal */ + mFd = base::unique_fd(open(mUartName.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY)); + if (!mFd.ok()) { + LOG(ERROR) << "SLCAN Failed to open " << mUartName << ": " << strerror(errno); + return ICanController::Result::BAD_ADDRESS; + } + + // blank terminal settings and pull them from the device + struct termios terminalSettings = {}; + if (tcgetattr(mFd.get(), &terminalSettings) < 0) { + LOG(ERROR) << "Failed to read attrs of" << mUartName << ": " << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + + // change settings to raw mode + cfmakeraw(&terminalSettings); + + // disable software flow control + terminalSettings.c_iflag &= ~IXOFF; + // enable hardware flow control + terminalSettings.c_cflag |= CRTSCTS; + + struct serial_struct serialSettings; + // get serial settings + if (ioctl(mFd.get(), TIOCGSERIAL, &serialSettings) < 0) { + LOG(ERROR) << "Failed to read serial settings from " << mUartName << ": " + << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + // set low latency mode + serialSettings.flags |= ASYNC_LOW_LATENCY; + // apply serial settings + if (ioctl(mFd.get(), TIOCSSERIAL, &serialSettings) < 0) { + LOG(ERROR) << "Failed to set low latency mode on " << mUartName << ": " << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + + /* TCSADRAIN applies settings after we finish writing the rest of our + * changes (as opposed to TCSANOW, which changes immediately) */ + if (tcsetattr(mFd.get(), TCSADRAIN, &terminalSettings) < 0) { + LOG(ERROR) << "Failed to apply terminal settings to " << mUartName << ": " + << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + + // apply speed setting for CAN + if (write(mFd.get(), canBitrateCommand.c_str(), canBitrateCommand.length()) <= 0) { + LOG(ERROR) << "Failed to apply CAN bitrate: " << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + + // set open flag TODO: also support listen only + if (write(mFd.get(), slcanprotocol::kOpenCommand.c_str(), + slcanprotocol::kOpenCommand.length()) <= 0) { + LOG(ERROR) << "Failed to set open flag: " << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + + // set line discipline to slcan + if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kSlcanDiscipline) < 0) { + LOG(ERROR) << "Failed to set line discipline to slcan: " << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + + // get the name of the device we created + struct ifreq ifrequest = {}; + if (ioctl(mFd.get(), SIOCGIFNAME, ifrequest.ifr_name) < 0) { + LOG(ERROR) << "Failed to get the name of the created device: " << strerror(errno); + return ICanController::Result::UNKNOWN_ERROR; + } + + // Update the CanBus object with name that was assigned to it + mIfname = ifrequest.ifr_name; + + return ICanController::Result::OK; +} + +bool CanBusSlcan::postDown() { + // reset the line discipline to TTY mode + if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kDefaultDiscipline) < 0) { + LOG(ERROR) << "Failed to reset line discipline!"; + return false; + } + + // issue the close command + if (write(mFd.get(), slcanprotocol::kCloseCommand.c_str(), + slcanprotocol::kCloseCommand.length()) <= 0) { + LOG(ERROR) << "Failed to close tty!"; + return false; + } + + // close our unique_fd + mFd.reset(); + + return true; +} + +} // namespace implementation +} // namespace V1_0 +} // namespace can +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/can/1.0/default/CanBusSlcan.h b/automotive/can/1.0/default/CanBusSlcan.h new file mode 100644 index 0000000000..2713da86d9 --- /dev/null +++ b/automotive/can/1.0/default/CanBusSlcan.h @@ -0,0 +1,50 @@ +/* + * 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 <linux/serial.h> +#include <linux/tty.h> +#include <net/if.h> +#include <termios.h> +#include "CanBus.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace can { +namespace V1_0 { +namespace implementation { + +struct CanBusSlcan : public CanBus { + CanBusSlcan(const std::string& uartName, uint32_t bitrate); + + protected: + virtual ICanController::Result preUp() override; + virtual bool postDown() override; + + private: + const std::string mUartName; + const uint32_t kBitrate; + base::unique_fd mFd; +}; + +} // 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 0000000000..cc59fa97b8 --- /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 0000000000..c2d5794502 --- /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 0000000000..ffdc9125fc --- /dev/null +++ b/automotive/can/1.0/default/CanController.cpp @@ -0,0 +1,147 @@ +/* + * 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 "CanBusSlcan.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, + ICanController::InterfaceType::SLCAN}); + 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 if (config.iftype == ICanController::InterfaceType::SLCAN) { + if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) { + busService = new CanBusSlcan(config.interfaceId.address(), config.baudrate); + } else { + return ICanController::Result::BAD_ADDRESS; + } + } else { + return ICanController::Result::NOT_SUPPORTED; + } + + busService->setErrorCallback([this, name = config.name]() { downInterface(name); }); + + 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; +} + +static bool unregisterCanBusService(const hidl_string& name, sp<CanBus> busService) { + auto manager = hidl::manager::V1_2::IServiceManager::getService(); + if (!manager) return false; + const auto res = manager->tryUnregister(ICanBus::descriptor, name, busService); + if (!res.isOk()) return false; + return res; +} + +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; + + if (!unregisterCanBusService(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 0000000000..0674d0edf7 --- /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 0000000000..86e12d19b1 --- /dev/null +++ b/automotive/can/1.0/default/CanSocket.cpp @@ -0,0 +1,160 @@ +/* + * 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; + + /* CanSocket can be brought down as a result of read failure, from the same thread, + * so let's just detach and let it finish on its own. */ + if (mReaderThreadFinished) { + mReaderThread.detach(); + } else { + 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"; + int errnoCopy = 0; + + 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; + + errnoCopy = errno; + LOG(ERROR) << "Failed to read CAN packet: " << strerror(errno) << " (" << errno << ")"; + break; + } + + mReadCallback(frame, ts); + } + + bool failed = !mStopReaderThread; + auto errCb = mErrorCallback; + mReaderThreadFinished = true; + + // Don't access any fields from here, see CanSocket::~CanSocket comment about detached thread + if (failed) errCb(errnoCopy); + + 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 0000000000..c98330bfe0 --- /dev/null +++ b/automotive/can/1.0/default/CanSocket.h @@ -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. + */ + +#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(int errnoVal)>; + + /** + * 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; + std::atomic<bool> mReaderThreadFinished = 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 0000000000..aba2c49a48 --- /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 {}; + + if (mCallback != nullptr) 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 0000000000..eade1098a4 --- /dev/null +++ b/automotive/can/1.0/default/CloseHandle.h @@ -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. + */ + +#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 = nullptr); + virtual ~CloseHandle(); + + Return<void> close() override; + + private: + const 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 0000000000..a629bdae00 --- /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 0000000000..31e5ad0376 --- /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 0000000000..9845bc7dd6 --- /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 0000000000..ba9b65bdd3 --- /dev/null +++ b/automotive/can/1.0/default/libnetdevice/NetlinkRequest.h @@ -0,0 +1,155 @@ +/* + * 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; + } + + /** \return 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 0000000000..05147647e6 --- /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 0000000000..90e1f3f95d --- /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 0000000000..87617dde74 --- /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 0000000000..3deac3e7ff --- /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 0000000000..9bdff4de21 --- /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 0000000000..d75361eaa0 --- /dev/null +++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/can.h @@ -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. + */ + +#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. + * + * \param ifname Interface for which the bitrate is to be set + * \return true on success, false on failure + */ +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 0000000000..e22eafb723 --- /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 0000000000..fc2b193355 --- /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 0000000000..7ef44fc297 --- /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 0000000000..63b07c9391 --- /dev/null +++ b/automotive/can/1.0/hidl-utils/Android.bp @@ -0,0 +1,21 @@ +// +// 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"], + vendor_available: true, +} 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 0000000000..039f971c35 --- /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 0000000000..21f364b629 --- /dev/null +++ b/automotive/can/1.0/tools/Android.bp @@ -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. +// + +cc_binary { + name: "canhalctrl", + defaults: ["android.hardware.automotive.can@defaults"], + srcs: [ + "canhalctrl.cpp", + ], + shared_libs: [ + "android.hardware.automotive.can@1.0", + "libhidlbase", + ], + 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", + ], + 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", + ], +} diff --git a/automotive/can/1.0/tools/canhalctrl.cpp b/automotive/can/1.0/tools/canhalctrl.cpp new file mode 100644 index 0000000000..fa1048d6ac --- /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 0000000000..99fd14a7dc --- /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 << " " << std::setfill('0') << std::setw(2) << 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 0000000000..29330c9c75 --- /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 0000000000..6f690f7851 --- /dev/null +++ b/automotive/can/1.0/types.hal @@ -0,0 +1,115 @@ +/* + * 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, + + /** CAN interface is down. */ + INTERFACE_DOWN, + + /** 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 0000000000..b4d91325ee --- /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 0000000000..1a05716fd0 --- /dev/null +++ b/automotive/can/1.0/vts/functional/VtsHalCanBusV1_0TargetTest.cpp @@ -0,0 +1,199 @@ +/* + * 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&) override { return {}; } +}; + +struct CanErrorListener : public can::V1_0::ICanErrorListener { + virtual Return<void> onError(ErrorEvent, bool) override { 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<ICloseHandle> listenForErrors(const sp<ICanErrorListener>& 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}; +} + +sp<ICloseHandle> CanBusHalTest::listenForErrors(const sp<ICanErrorListener>& listener) { + const auto res = mCanBus->listenForErrors(listener); + res.assertOk(); + return res; +} + +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, DoubleCloseListener) { + const auto [result, closeHandle] = listen({}, new CanMessageListener()); + ASSERT_EQ(Result::OK, result); + + closeHandle->close().assertOk(); + closeHandle->close().assertOk(); +} + +TEST_F(CanBusHalTest, DontCloseListener) { + const auto [result, closeHandle] = listen({}, new CanMessageListener()); + ASSERT_EQ(Result::OK, result); +} + +TEST_F(CanBusHalTest, DoubleCloseErrorListener) { + auto closeHandle = listenForErrors(new CanErrorListener()); + ASSERT_NE(nullptr, closeHandle.get()); + + closeHandle->close().assertOk(); + closeHandle->close().assertOk(); +} + +TEST_F(CanBusHalTest, DoubleCloseNullErrorListener) { + auto closeHandle = listenForErrors(nullptr); + ASSERT_NE(nullptr, closeHandle.get()); + + closeHandle->close().assertOk(); + closeHandle->close().assertOk(); +} + +TEST_F(CanBusHalTest, DontCloseErrorListener) { + auto closeHandle = listenForErrors(new CanErrorListener()); + ASSERT_NE(nullptr, closeHandle.get()); +} + +} // 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 0000000000..225984dd93 --- /dev/null +++ b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp @@ -0,0 +1,316 @@ +/* + * 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) override { + std::unique_lock<std::mutex> lk(mMessagesGuard); + mMessages.push_back(msg); + mMessagesUpdated.notify_one(); + 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 0000000000..64e7a96823 --- /dev/null +++ b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp @@ -0,0 +1,259 @@ +/* + * 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 0000000000..e925c8fe34 --- /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 0000000000..09239989ef --- /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 0000000000..a722dd0783 --- /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.0/IEvsCamera.hal b/automotive/evs/1.0/IEvsCamera.hal index dbcaf9288c..464dafba75 100644 --- a/automotive/evs/1.0/IEvsCamera.hal +++ b/automotive/evs/1.0/IEvsCamera.hal @@ -16,7 +16,6 @@ package android.hardware.automotive.evs@1.0; -import types; import IEvsCameraStream; @@ -28,8 +27,8 @@ interface IEvsCamera { /** * Returns the ID of this camera. * - * Returns the description of this camera. This must be the same value as reported - * by EvsEnumerator::getCamerList(). + * @return info The description of this camera. This must be the same value as + * reported by EvsEnumerator::getCameraList(). */ getCameraInfo() generates (CameraDesc info); @@ -43,16 +42,20 @@ interface IEvsCamera { * in which case buffers should be added or removed from the chain as appropriate. * If no call is made to this entry point, the IEvsCamera must support at least one * frame by default. More is acceptable. - * BUFFER_NOT_AVAILABLE is returned if the implementation cannot support the - * requested number of concurrent frames. + * + * @param bufferCount Number of buffers the client of IEvsCamera may hold concurrently. + * @return result EvsResult::OK is returned if this call is successful. */ setMaxFramesInFlight(uint32_t bufferCount) generates (EvsResult result); /** - * Request delivery of EVS camera frames from this camera. + * Request to start EVS camera stream from this camera. + * + * The IEvsCameraStream must begin receiving calls with various events + * including new image frame ready until stopVideoStream() is called. * - * The IEvsCameraStream must begin receiving periodic calls with new image - * frames until stopVideoStream() is called. + * @param receiver IEvsCameraStream implementation. + * @return result EvsResult::OK is returned if this call is successful. */ startVideoStream(IEvsCameraStream receiver) generates (EvsResult result); @@ -64,6 +67,8 @@ interface IEvsCamera { * A small, finite number of buffers are available (possibly as small * as one), and if the supply is exhausted, no further frames may be * delivered until a buffer is returned. + * + * @param buffer A buffer to be returned. */ oneway doneWithFrame(BufferDesc buffer); @@ -83,6 +88,11 @@ interface IEvsCamera { * The values allowed for opaqueIdentifier are driver specific, * but no value passed in may crash the driver. The driver should * return 0 for any unrecognized opaqueIdentifier. + * + * @param opaqueIdentifier An unique identifier of the information to + * request. + * @return value Requested information. Zero is returned if the + * driver does not recognize a given identifier. */ getExtendedInfo(uint32_t opaqueIdentifier) generates (int32_t value); @@ -94,6 +104,11 @@ interface IEvsCamera { * in order to function in a default state. * INVALID_ARG is returned if the opaqueValue is not meaningful to * the driver implementation. + * + * @param opaqueIdentifier An unique identifier of the information to + * program. + * opaqueValue A value to program. + * @return result EvsResult::OK is returned if this call is successful. */ setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) generates (EvsResult result); }; diff --git a/automotive/evs/1.0/IEvsCameraStream.hal b/automotive/evs/1.0/IEvsCameraStream.hal index 4e743b2683..ec18f6a822 100644 --- a/automotive/evs/1.0/IEvsCameraStream.hal +++ b/automotive/evs/1.0/IEvsCameraStream.hal @@ -31,6 +31,8 @@ interface IEvsCameraStream { * When the last frame in the stream has been delivered, a NULL bufferHandle * must be delivered, signifying the end of the stream. No further frame * deliveries may happen thereafter. + * + * @param buffer a buffer descriptor of a delivered image frame. */ oneway deliverFrame(BufferDesc buffer); }; diff --git a/automotive/evs/1.0/IEvsDisplay.hal b/automotive/evs/1.0/IEvsDisplay.hal index 12541f3513..72f767e9c0 100644 --- a/automotive/evs/1.0/IEvsDisplay.hal +++ b/automotive/evs/1.0/IEvsDisplay.hal @@ -16,8 +16,6 @@ package android.hardware.automotive.evs@1.0; -import types; - /** * Represents a single camera and is the primary interface for capturing images. @@ -28,6 +26,9 @@ interface IEvsDisplay { * Returns basic information about the EVS display provided by the system. * * See the description of the DisplayDesc structure for details. + * + * @return info The description of this display. Please see the description + * of the DisplayDesc structure for details. */ getDisplayInfo() generates (DisplayDesc info); @@ -42,6 +43,9 @@ interface IEvsDisplay { * video. When the display is no longer required, the client is expected to request * the NOT_VISIBLE state after passing the last video frame. * Returns INVALID_ARG if the requested state is not a recognized value. + * + * @param state Desired new DisplayState. + * @return result EvsResult::OK is returned if this call is successful. */ setDisplayState(DisplayState state) generates (EvsResult result); @@ -54,6 +58,8 @@ interface IEvsDisplay { * the logic responsible for changing display states should generally live above * the device layer, making it undesirable for the HAL implementation to spontaneously * change display states. + * + * @return state Current DisplayState of this Display. */ getDisplayState() generates (DisplayState state); @@ -61,9 +67,11 @@ interface IEvsDisplay { /** * This call returns a handle to a frame buffer associated with the display. * - * The returned buffer may be locked and written to by software and/or GL. This buffer - * must be returned via a call to returnTargetBufferForDisplay() even if the - * display is no longer visible. + * @return buffer A handle to a frame buffer. The returned buffer may be + * locked and written to by software and/or GL. This buffer + * must be returned via a call to + * returnTargetBufferForDisplay() even if the display is no + * longer visible. */ getTargetBuffer() generates (BufferDesc buffer); @@ -75,6 +83,9 @@ interface IEvsDisplay { * There is no maximum time the caller may hold onto the buffer before making this * call. The buffer may be returned at any time and in any DisplayState, but all * buffers are expected to be returned before the IEvsDisplay interface is destroyed. + * + * @param buffer A buffer handle to the frame that is ready for display. + * @return result EvsResult::OK is returned if this call is successful. */ returnTargetBufferForDisplay(BufferDesc buffer) generates (EvsResult result); }; diff --git a/automotive/evs/1.0/IEvsEnumerator.hal b/automotive/evs/1.0/IEvsEnumerator.hal index ee51e7e994..e5633df973 100644 --- a/automotive/evs/1.0/IEvsEnumerator.hal +++ b/automotive/evs/1.0/IEvsEnumerator.hal @@ -16,7 +16,6 @@ package android.hardware.automotive.evs@1.0; -import types; import IEvsCamera; import IEvsDisplay; @@ -28,6 +27,8 @@ interface IEvsEnumerator { /** * Returns a list of all EVS cameras available to the system + * + * @return cameras A list of cameras availale for EVS service. */ getCameraList() generates (vec<CameraDesc> cameras); @@ -37,9 +38,9 @@ interface IEvsEnumerator { * Given a camera's unique cameraId from CameraDesc, returns the * IEvsCamera interface associated with the specified camera. When * done using the camera, the caller may release it by calling closeCamera(). - * Note: Reliance on the sp<> going out of scope is not recommended - * because the resources may not be released right away due to asynchronos - * behavior in the hardware binder (ref b/36122635). + * + * @param cameraId A unique identifier of the camera. + * @return carCamera EvsCamera object associated with a given cameraId. */ openCamera(string cameraId) generates (IEvsCamera carCamera); @@ -48,6 +49,8 @@ interface IEvsEnumerator { * * When the IEvsCamera object is no longer required, it must be released. * NOTE: Video streaming must be cleanly stopped before making this call. + * + * @param carCamera EvsCamera object to be closed. */ closeCamera(IEvsCamera carCamera); @@ -60,8 +63,8 @@ interface IEvsEnumerator { * the old instance shall be closed and give the new caller exclusive * access. * When done using the display, the caller may release it by calling closeDisplay(). - * TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the - * resources may not be released right away due to asynchronos behavior in the hardware binder. + * + * @return display EvsDisplay object to be used. */ openDisplay() generates (IEvsDisplay display); @@ -70,6 +73,8 @@ interface IEvsEnumerator { * * When the IEvsDisplay object is no longer required, it must be released. * NOTE: All buffers must have been returned to the display before making this call. + * + * @param display EvsDisplay object to be closed. */ closeDisplay(IEvsDisplay display); @@ -80,6 +85,8 @@ interface IEvsEnumerator { * the actual state of the active display. This call is replicated on the IEvsEnumerator * interface in order to allow secondary clients to monitor the state of the EVS display * without acquiring exclusive ownership of the display. + * + * @return state Current DisplayState of this Display. */ getDisplayState() generates (DisplayState state); }; diff --git a/automotive/evs/1.0/default/android.hardware.automotive.evs@1.0-service.rc b/automotive/evs/1.0/default/android.hardware.automotive.evs@1.0-service.rc index 117c249a51..8dcd9694b0 100644 --- a/automotive/evs/1.0/default/android.hardware.automotive.evs@1.0-service.rc +++ b/automotive/evs/1.0/default/android.hardware.automotive.evs@1.0-service.rc @@ -2,3 +2,4 @@ service vendor.evs-hal-mock /vendor/bin/hw/android.hardware.automotive.evs@1.0-s class hal user automotive_evs group automotive_evs + disabled # do not start automatically diff --git a/automotive/evs/1.0/types.hal b/automotive/evs/1.0/types.hal index 7cebf6d179..1efd5ebf3d 100644 --- a/automotive/evs/1.0/types.hal +++ b/automotive/evs/1.0/types.hal @@ -24,8 +24,15 @@ package android.hardware.automotive.evs@1.0; * EVS camera in the system. */ struct CameraDesc { + /* Unique identifier for camera devices. This may be a path to detected + * camera device; for example, "/dev/video0". + */ string cameraId; - uint32_t vendorFlags; // Opaque value from driver + + /* Opaque value from driver. Vendor may use this field to store additional + * information; for example, sensor and bridge chip id. + */ + uint32_t vendorFlags; }; @@ -38,8 +45,11 @@ struct CameraDesc { * presentation device. */ struct DisplayDesc { + /* Unique identifier for the display */ string displayId; - uint32_t vendorFlags; // Opaque value from driver + + /* Opaque value from driver */ + uint32_t vendorFlags; }; @@ -56,14 +66,31 @@ struct DisplayDesc { * Specifically consider if format and/or usage should become enumerated types. */ struct BufferDesc { - uint32_t width; // Units of pixels - uint32_t height; // Units of pixels - uint32_t stride; // Units of pixels to match gralloc - uint32_t pixelSize; // Units of bytes - uint32_t format; // May contain values from android_pixel_format_t - uint32_t usage; // May contain values from from Gralloc.h - uint32_t bufferId; // Opaque value from driver - handle memHandle; // gralloc memory buffer handle + /* A frame width in the units of pixels */ + uint32_t width; + + /* A frame height in the units of pixels */ + uint32_t height; + + /* A frame stride in the units of pixels, to match gralloc */ + uint32_t stride; + + /* The size of a pixel in the units of bytes */ + uint32_t pixelSize; + + /* The image format of the frame; may contain values from + * android_pixel_format_t + */ + uint32_t format; + + /* May contain values from Gralloc.h */ + uint32_t usage; + + /* Opaque value from driver */ + uint32_t bufferId; + + /* Gralloc memory buffer handle */ + handle memHandle; }; @@ -77,12 +104,23 @@ struct BufferDesc { * presentation device. */ enum DisplayState : uint32_t { - NOT_OPEN = 0, // Display has not been requested by any application - NOT_VISIBLE, // Display is inhibited - VISIBLE_ON_NEXT_FRAME, // Will become visible with next frame - VISIBLE, // Display is currently active - DEAD, // Driver is in an undefined state. Interface should be closed. - NUM_STATES // Must be last + /* Display has not been requested by any application yet */ + NOT_OPEN = 0, + + /* Display is inhibited */ + NOT_VISIBLE, + + /* Will become visible with next frame */ + VISIBLE_ON_NEXT_FRAME, + + /* Display is currently active */ + VISIBLE, + + /* Driver is in an undefined state. Interface should be closed. */ + DEAD, + + /* Must be the last */ + NUM_STATES }; diff --git a/automotive/evs/1.0/vts/functional/Android.bp b/automotive/evs/1.0/vts/functional/Android.bp index 2ef33fdd11..8988bfd8b1 100644 --- a/automotive/evs/1.0/vts/functional/Android.bp +++ b/automotive/evs/1.0/vts/functional/Android.bp @@ -19,13 +19,15 @@ cc_test { srcs: [ "VtsHalEvsV1_0TargetTest.cpp", "FrameHandler.cpp", - "FormatConvert.cpp" ], defaults: ["VtsHalTargetTestDefaults"], shared_libs: [ "libui", ], - static_libs: ["android.hardware.automotive.evs@1.0"], + static_libs: [ + "android.hardware.automotive.evs@1.0", + "android.hardware.automotive.evs@common-default-lib", + ], test_suites: ["general-tests"], cflags: [ "-O0", diff --git a/automotive/evs/1.0/vts/functional/FormatConvert.h b/automotive/evs/1.0/vts/functional/FormatConvert.h deleted file mode 100644 index 4a94f996d0..0000000000 --- a/automotive/evs/1.0/vts/functional/FormatConvert.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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. - */ - -#ifndef EVS_VTS_FORMATCONVERT_H -#define EVS_VTS_FORMATCONVERT_H - -#include <queue> -#include <stdint.h> - - -// Given an image buffer in NV21 format (HAL_PIXEL_FORMAT_YCRCB_420_SP), output 32bit RGBx/BGRx -// values. The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved -// U/V array. It assumes an even width and height for the overall image, and a horizontal -// stride that is an even multiple of 16 bytes for both the Y and UV arrays. -void copyNV21toRGB32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels, - bool bgrxFormat = false); - -void copyNV21toBGR32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels); - - -// Given an image buffer in YV12 format (HAL_PIXEL_FORMAT_YV12), output 32bit RGBx/BGRx values. -// The YV12 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 U array, followed -// by another 1/2 x 1/2 V array. It assumes an even width and height for the overall image, -// and a horizontal stride that is an even multiple of 16 bytes for each of the Y, U, -// and V arrays. -void copyYV12toRGB32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels, - bool bgrxFormat = false); - -void copyYV12toBGR32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels); - -// Given an image buffer in YUYV format (HAL_PIXEL_FORMAT_YCBCR_422_I), output 32bit RGBx/BGRx -// values. The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved -// U/V array. It assumes an even width and height for the overall image, and a horizontal -// stride that is an even multiple of 16 bytes for both the Y and UV arrays. -void copyYUYVtoRGB32(unsigned width, unsigned height, - uint8_t* src, unsigned srcStrideBytes, - uint32_t* dst, unsigned dstStrideBytes, - bool bgrxFormat = false); - -void copyYUYVtoBGR32(unsigned width, unsigned height, - uint8_t* src, unsigned srcStrideBytes, - uint32_t* dst, unsigned dstStrideBytes); - - -// Given an simple rectangular image buffer with an integer number of bytes per pixel, -// copy the pixel values into a new rectangular buffer (potentially with a different stride). -// This is typically used to copy RGBx data into an RGBx output buffer. -void copyMatchedInterleavedFormats(unsigned width, unsigned height, - void* src, unsigned srcStridePixels, - void* dst, unsigned dstStridePixels, - unsigned pixelSize); - -#endif // EVS_VTS_FORMATCONVERT_H diff --git a/automotive/evs/1.0/vts/functional/FrameHandler.cpp b/automotive/evs/1.0/vts/functional/FrameHandler.cpp index bc3790f385..6a01a44dfe 100644 --- a/automotive/evs/1.0/vts/functional/FrameHandler.cpp +++ b/automotive/evs/1.0/vts/functional/FrameHandler.cpp @@ -240,46 +240,47 @@ bool FrameHandler::copyBufferContents(const BufferDesc& tgtBuffer, tgt->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)&tgtPixels); if (srcPixels && tgtPixels) { + using namespace ::android::hardware::automotive::evs::common; if (tgtBuffer.format == HAL_PIXEL_FORMAT_RGBA_8888) { if (srcBuffer.format == HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21 - copyNV21toRGB32(width, height, - srcPixels, - tgtPixels, tgtBuffer.stride); + Utils::copyNV21toRGB32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12 - copyYV12toRGB32(width, height, - srcPixels, - tgtPixels, tgtBuffer.stride); + Utils::copyYV12toRGB32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV - copyYUYVtoRGB32(width, height, - srcPixels, srcBuffer.stride, - tgtPixels, tgtBuffer.stride); + Utils::copyYUYVtoRGB32(width, height, + srcPixels, srcBuffer.stride, + tgtPixels, tgtBuffer.stride); } else if (srcBuffer.format == tgtBuffer.format) { // 32bit RGBA - copyMatchedInterleavedFormats(width, height, - srcPixels, srcBuffer.stride, - tgtPixels, tgtBuffer.stride, - tgtBuffer.pixelSize); + Utils::copyMatchedInterleavedFormats(width, height, + srcPixels, srcBuffer.stride, + tgtPixels, tgtBuffer.stride, + tgtBuffer.pixelSize); } else { ALOGE("Camera buffer format is not supported"); success = false; } } else if (tgtBuffer.format == HAL_PIXEL_FORMAT_BGRA_8888) { if (srcBuffer.format == HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21 - copyNV21toBGR32(width, height, - srcPixels, - tgtPixels, tgtBuffer.stride); + Utils::copyNV21toBGR32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12 - copyYV12toBGR32(width, height, - srcPixels, - tgtPixels, tgtBuffer.stride); + Utils::copyYV12toBGR32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV - copyYUYVtoBGR32(width, height, - srcPixels, srcBuffer.stride, - tgtPixels, tgtBuffer.stride); + Utils::copyYUYVtoBGR32(width, height, + srcPixels, srcBuffer.stride, + tgtPixels, tgtBuffer.stride); } else if (srcBuffer.format == tgtBuffer.format) { // 32bit RGBA - copyMatchedInterleavedFormats(width, height, - srcPixels, srcBuffer.stride, - tgtPixels, tgtBuffer.stride, - tgtBuffer.pixelSize); + Utils::copyMatchedInterleavedFormats(width, height, + srcPixels, srcBuffer.stride, + tgtPixels, tgtBuffer.stride, + tgtBuffer.pixelSize); } else { ALOGE("Camera buffer format is not supported"); success = false; diff --git a/automotive/evs/1.1/Android.bp b/automotive/evs/1.1/Android.bp new file mode 100644 index 0000000000..c850c91b21 --- /dev/null +++ b/automotive/evs/1.1/Android.bp @@ -0,0 +1,24 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.automotive.evs@1.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IEvsCamera.hal", + "IEvsCameraStream.hal", + "IEvsEnumerator.hal", + ], + interfaces: [ + "android.hardware.automotive.evs@1.0", + "android.hardware.camera.device@3.2", + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hidl.base@1.0", + ], + gen_java: false, +} diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal new file mode 100644 index 0000000000..975b6c6cae --- /dev/null +++ b/automotive/evs/1.1/IEvsCamera.hal @@ -0,0 +1,158 @@ +/* + * 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.evs@1.1; + +import @1.0::IEvsCamera; +import @1.0::IEvsDisplay; +import @1.0::EvsResult; +import IEvsCameraStream; + +/** + * Represents a single camera and is the primary interface for capturing images. + */ +interface IEvsCamera extends @1.0::IEvsCamera { + /** + * Returns the description of this camera. + * + * @return info The description of this camera. This must be the same value as + * reported by EvsEnumerator::getCameraList_1_1(). + */ + getCameraInfo_1_1() generates (CameraDesc info); + + /** + * Requests to pause EVS camera stream events. + * + * Like stopVideoStream(), events may continue to arrive for some time + * after this call returns. Delivered frame buffers must be returned. + * + * @return result EvsResult::OK is returned if this call is successful. + */ + pauseVideoStream() generates (EvsResult result); + + /** + * Requests to resume EVS camera stream. + * + * @return result EvsResult::OK is returned if this call is successful. + */ + resumeVideoStream() generates (EvsResult result); + + /** + * Returns a frame that was delivered by to the IEvsCameraStream. + * + * When done consuming a frame delivered to the IEvsCameraStream + * interface, it must be returned to the IEvsCamera for reuse. + * A small, finite number of buffers are available (possibly as small + * as one), and if the supply is exhausted, no further frames may be + * delivered until a buffer is returned. + * + * @param buffer A buffer to be returned. + * @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); + + /** + * Retrieves a list of parameters this camera supports. + * + * @return params A list of CameraParam that this camera supports. + */ + getParameterList() generates (vec<CameraParam> params); + + /** + * Requests a valid value range of a camera parameter + * + * @param id The identifier of camera parameter, CameraParam enum. + * + * @return min The lower bound of valid parameter value range. + * @return max The upper bound of valid parameter value range. + * @return step The resolution of values in valid range. + */ + getIntParameterRange(CameraParam id) + generates (int32_t min, int32_t max, int32_t step); + + /** + * Requests to set a camera parameter. Only a request from the master + * client will be processed successfully. + * + * @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. + */ + setIntParameter(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. + */ + getIntParameter(CameraParam id) generates(EvsResult result, int32_t value); +}; diff --git a/automotive/evs/1.1/IEvsCameraStream.hal b/automotive/evs/1.1/IEvsCameraStream.hal new file mode 100644 index 0000000000..9e4ea19f1d --- /dev/null +++ b/automotive/evs/1.1/IEvsCameraStream.hal @@ -0,0 +1,48 @@ +/* + * 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.evs@1.1; + +import @1.0::IEvsCameraStream; +import @1.1::BufferDesc; +import @1.1::EvsEvent; + +/** + * Implemented on client side to receive asynchronous streaming event deliveries. + */ +interface IEvsCameraStream extends @1.0::IEvsCameraStream { + + /** + * Receives calls from the HAL each time a video frame is ready for inspection. + * Buffer handles received by this method must be returned via calls to + * IEvsCamera::doneWithFrame_1_1(). When the video stream is stopped via a call + * to IEvsCamera::stopVideoStream(), this callback may continue to happen for + * some time as the pipeline drains. Each frame must still be returned. + * When the last frame in the stream has been delivered, STREAM_STOPPED + * event must be delivered. No further frame deliveries may happen + * thereafter. + * + * @param buffer a buffer descriptor of a delivered image frame. + */ + oneway deliverFrame_1_1(BufferDesc buffer); + + /** + * Receives calls from the HAL each time an event happens. + * + * @param event EVS event with possible event information. + */ + oneway notify(EvsEvent event); +}; diff --git a/automotive/evs/1.1/IEvsEnumerator.hal b/automotive/evs/1.1/IEvsEnumerator.hal new file mode 100644 index 0000000000..1695821baa --- /dev/null +++ b/automotive/evs/1.1/IEvsEnumerator.hal @@ -0,0 +1,50 @@ +/* + * 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.evs@1.1; + +import IEvsCamera; +import @1.0::IEvsEnumerator; +import @1.0::EvsResult; +import android.hardware.camera.device@3.2::Stream; + +/** + * Provides the mechanism for EVS camera discovery + */ +interface IEvsEnumerator extends @1.0::IEvsEnumerator { + /** + * Returns a list of all EVS cameras available to the system + * + * @return cameras A list of cameras availale for EVS service. + */ + getCameraList_1_1() generates (vec<CameraDesc> cameras); + + /** + * Gets the IEvsCamera associated with a cameraId from a CameraDesc + * + * Given a camera's unique cameraId from CameraDesc, returns the + * IEvsCamera interface associated with the specified camera. When + * done using the camera, the caller may release it by calling closeCamera(). + * + * @param cameraId A unique identifier of the camera. + * @param streamCfg A stream configuration the client wants to use. + * @return evsCamera EvsCamera object associated with a given cameraId. + * Returned object would be null if a camera device does + * not support a given stream configuration or is already + * configured differently by another client. + */ + openCamera_1_1(string cameraId, Stream streamCfg) generates (IEvsCamera evsCamera); +}; diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp new file mode 100644 index 0000000000..41cb4265e5 --- /dev/null +++ b/automotive/evs/1.1/default/Android.bp @@ -0,0 +1,47 @@ +cc_binary { + name: "android.hardware.automotive.evs@1.1-service", + defaults: ["hidl_defaults"], + proprietary: true, + relative_install_path: "hw", + srcs: [ + "service.cpp", + "EvsCamera.cpp", + "EvsEnumerator.cpp", + "EvsDisplay.cpp", + "ConfigManager.cpp", + "ConfigManagerUtil.cpp", + ], + init_rc: ["android.hardware.automotive.evs@1.1-service.rc"], + + shared_libs: [ + "android.hardware.automotive.evs@1.0", + "android.hardware.automotive.evs@1.1", + "android.hardware.camera.device@3.2", + "libbase", + "libbinder", + "liblog", + "libhardware", + "libhidlbase", + "liblog", + "libui", + "libutils", + "libcamera_metadata", + "libtinyxml2", + ], + + cflags: [ + "-O0", + "-g", + ], + + required: [ + "evs_default_configuration.xml", + ], +} + +prebuilt_etc { + name: "evs_default_configuration.xml", + + src: "resources/evs_default_configuration.xml", + sub_dir: "automotive/evs", +} diff --git a/automotive/evs/1.1/default/ConfigManager.cpp b/automotive/evs/1.1/default/ConfigManager.cpp new file mode 100644 index 0000000000..96a2f98576 --- /dev/null +++ b/automotive/evs/1.1/default/ConfigManager.cpp @@ -0,0 +1,487 @@ +/* + * 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 <sstream> +#include <fstream> +#include <thread> + +#include <hardware/gralloc.h> +#include <utils/SystemClock.h> +#include <android/hardware/camera/device/3.2/ICameraDevice.h> + +#include "ConfigManager.h" + +using ::android::hardware::camera::device::V3_2::StreamRotation; + + +ConfigManager::~ConfigManager() { + /* Nothing to do */ +} + + +void ConfigManager::readCameraInfo(const XMLElement * const aCameraElem) { + if (aCameraElem == nullptr) { + ALOGW("XML file does not have required camera element"); + return; + } + + const XMLElement *curElem = aCameraElem->FirstChildElement(); + while (curElem != nullptr) { + if (!strcmp(curElem->Name(), "group")) { + /* camera group identifier */ + const char *group_id = curElem->FindAttribute("group_id")->Value(); + + /* create CameraGroup */ + unique_ptr<ConfigManager::CameraGroup> aCameraGroup(new ConfigManager::CameraGroup()); + + /* add a camera device to its group */ + addCameraDevices(curElem->FindAttribute("device_id")->Value(), aCameraGroup); + + /* a list of camera stream configurations */ + const XMLElement *childElem = + curElem->FirstChildElement("caps")->FirstChildElement("stream"); + while (childElem != nullptr) { + /* read 5 attributes */ + const XMLAttribute *idAttr = childElem->FindAttribute("id"); + const XMLAttribute *widthAttr = childElem->FindAttribute("width"); + const XMLAttribute *heightAttr = childElem->FindAttribute("height"); + const XMLAttribute *fmtAttr = childElem->FindAttribute("format"); + const XMLAttribute *fpsAttr = childElem->FindAttribute("framerate"); + + const int32_t id = stoi(idAttr->Value()); + int32_t framerate = 0; + if (fpsAttr != nullptr) { + framerate = stoi(fpsAttr->Value()); + } + + int32_t pixFormat; + if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), + pixFormat)) { + RawStreamConfiguration cfg = { + id, + stoi(widthAttr->Value()), + stoi(heightAttr->Value()), + pixFormat, + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT, + framerate + }; + aCameraGroup->streamConfigurations[id] = cfg; + } + + childElem = childElem->NextSiblingElement("stream"); + } + + /* camera group synchronization */ + const char *sync = curElem->FindAttribute("synchronized")->Value(); + aCameraGroup->synchronized = + static_cast<bool>(strcmp(sync, "false")); + + /* add a group to hash map */ + mCameraGroups[group_id] = std::move(aCameraGroup); + } else if (!strcmp(curElem->Name(), "device")) { + /* camera unique identifier */ + const char *id = curElem->FindAttribute("id")->Value(); + + /* camera mount location */ + const char *pos = curElem->FindAttribute("position")->Value(); + + /* store read camera module information */ + mCameraInfo[id] = readCameraDeviceInfo(curElem); + + /* assign a camera device to a position group */ + mCameraPosition[pos].emplace(id); + } else { + /* ignore other device types */ + ALOGD("Unknown element %s is ignored", curElem->Name()); + } + + curElem = curElem->NextSiblingElement(); + } +} + + +unique_ptr<ConfigManager::CameraInfo> +ConfigManager::readCameraDeviceInfo(const XMLElement *aDeviceElem) { + if (aDeviceElem == nullptr) { + return nullptr; + } + + /* create a CameraInfo to be filled */ + unique_ptr<ConfigManager::CameraInfo> aCamera(new ConfigManager::CameraInfo()); + + /* size information to allocate camera_metadata_t */ + size_t totalEntries = 0; + size_t totalDataSize = 0; + + /* read device capabilities */ + totalEntries += + readCameraCapabilities(aDeviceElem->FirstChildElement("caps"), + aCamera, + totalDataSize); + + + /* read camera metadata */ + totalEntries += + readCameraMetadata(aDeviceElem->FirstChildElement("characteristics"), + aCamera, + totalDataSize); + + /* construct camera_metadata_t */ + if (!constructCameraMetadata(aCamera, totalEntries, totalDataSize)) { + ALOGW("Either failed to allocate memory or " + "allocated memory was not large enough"); + } + + return aCamera; +} + + +size_t ConfigManager::readCameraCapabilities(const XMLElement * const aCapElem, + unique_ptr<ConfigManager::CameraInfo> &aCamera, + size_t &dataSize) { + if (aCapElem == nullptr) { + return 0; + } + + string token; + const XMLElement *curElem = nullptr; + + /* a list of supported camera parameters/controls */ + curElem = aCapElem->FirstChildElement("supported_controls"); + if (curElem != nullptr) { + const XMLElement *ctrlElem = curElem->FirstChildElement("control"); + while (ctrlElem != nullptr) { + const char *nameAttr = ctrlElem->FindAttribute("name")->Value();; + const int32_t minVal = stoi(ctrlElem->FindAttribute("min")->Value()); + const int32_t maxVal = stoi(ctrlElem->FindAttribute("max")->Value()); + + int32_t stepVal = 1; + const XMLAttribute *stepAttr = ctrlElem->FindAttribute("step"); + if (stepAttr != nullptr) { + stepVal = stoi(stepAttr->Value()); + } + + CameraParam aParam; + if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr, + aParam)) { + aCamera->controls.emplace( + aParam, + make_tuple(minVal, maxVal, stepVal) + ); + } + + ctrlElem = ctrlElem->NextSiblingElement("control"); + } + } + + /* a list of camera stream configurations */ + curElem = aCapElem->FirstChildElement("stream"); + while (curElem != nullptr) { + /* read 5 attributes */ + const XMLAttribute *idAttr = curElem->FindAttribute("id"); + const XMLAttribute *widthAttr = curElem->FindAttribute("width"); + const XMLAttribute *heightAttr = curElem->FindAttribute("height"); + const XMLAttribute *fmtAttr = curElem->FindAttribute("format"); + const XMLAttribute *fpsAttr = curElem->FindAttribute("framerate"); + + const int32_t id = stoi(idAttr->Value()); + int32_t framerate = 0; + if (fpsAttr != nullptr) { + framerate = stoi(fpsAttr->Value()); + } + + int32_t pixFormat; + if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), + pixFormat)) { + RawStreamConfiguration cfg = { + id, + stoi(widthAttr->Value()), + stoi(heightAttr->Value()), + pixFormat, + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT, + framerate + }; + aCamera->streamConfigurations[id] = cfg; + } + + curElem = curElem->NextSiblingElement("stream"); + } + + dataSize = calculate_camera_metadata_entry_data_size( + get_camera_metadata_tag_type( + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS + ), + aCamera->streamConfigurations.size() * kStreamCfgSz + ); + + /* a single camera metadata entry contains multiple stream configurations */ + return dataSize > 0 ? 1 : 0; +} + + +size_t ConfigManager::readCameraMetadata(const XMLElement * const aParamElem, + unique_ptr<ConfigManager::CameraInfo> &aCamera, + size_t &dataSize) { + if (aParamElem == nullptr) { + return 0; + } + + const XMLElement *curElem = aParamElem->FirstChildElement("parameter"); + size_t numEntries = 0; + camera_metadata_tag_t tag; + while (curElem != nullptr) { + if (!ConfigManagerUtil::convertToMetadataTag(curElem->FindAttribute("name")->Value(), + tag)) { + switch(tag) { + case ANDROID_LENS_DISTORTION: + case ANDROID_LENS_POSE_ROTATION: + case ANDROID_LENS_POSE_TRANSLATION: + case ANDROID_LENS_INTRINSIC_CALIBRATION: { + /* float[] */ + size_t count = 0; + void *data = ConfigManagerUtil::convertFloatArray( + curElem->FindAttribute("size")->Value(), + curElem->FindAttribute("value")->Value(), + count + ); + + aCamera->cameraMetadata[tag] = + make_pair(make_unique<void *>(data), count); + + ++numEntries; + dataSize += calculate_camera_metadata_entry_data_size( + get_camera_metadata_tag_type(tag), count + ); + + break; + } + + default: + ALOGW("Parameter %s is not supported", + curElem->FindAttribute("name")->Value()); + break; + } + } + + curElem = curElem->NextSiblingElement("parameter"); + } + + return numEntries; +} + + +bool ConfigManager::constructCameraMetadata(unique_ptr<CameraInfo> &aCamera, + const size_t totalEntries, + const size_t totalDataSize) { + if (!aCamera->allocate(totalEntries, totalDataSize)) { + ALOGE("Failed to allocate memory for camera metadata"); + return false; + } + + const size_t numStreamConfigs = aCamera->streamConfigurations.size(); + unique_ptr<int32_t[]> data(new int32_t[kStreamCfgSz * numStreamConfigs]); + int32_t *ptr = data.get(); + for (auto &cfg : aCamera->streamConfigurations) { + for (auto i = 0; i < kStreamCfgSz; ++i) { + *ptr++ = cfg.second[i]; + } + } + int32_t err = add_camera_metadata_entry(aCamera->characteristics, + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + data.get(), + numStreamConfigs * kStreamCfgSz); + + if (err) { + ALOGE("Failed to add stream configurations to metadata, ignored"); + return false; + } + + bool success = true; + for (auto &[tag, entry] : aCamera->cameraMetadata) { + /* try to add new camera metadata entry */ + int32_t err = add_camera_metadata_entry(aCamera->characteristics, + tag, + entry.first.get(), + entry.second); + if (err) { + ALOGE("Failed to add an entry with a tag 0x%X", tag); + + /* may exceed preallocated capacity */ + ALOGE("Camera metadata has %ld / %ld entries and %ld / %ld bytes are filled", + (long)get_camera_metadata_entry_count(aCamera->characteristics), + (long)get_camera_metadata_entry_capacity(aCamera->characteristics), + (long)get_camera_metadata_data_count(aCamera->characteristics), + (long)get_camera_metadata_data_capacity(aCamera->characteristics)); + ALOGE("\tCurrent metadata entry requires %ld bytes", + (long)calculate_camera_metadata_entry_data_size(tag, entry.second)); + + success = false; + } + } + + ALOGV("Camera metadata has %ld / %ld entries and %ld / %ld bytes are filled", + (long)get_camera_metadata_entry_count(aCamera->characteristics), + (long)get_camera_metadata_entry_capacity(aCamera->characteristics), + (long)get_camera_metadata_data_count(aCamera->characteristics), + (long)get_camera_metadata_data_capacity(aCamera->characteristics)); + + return success; +} + + +void ConfigManager::readSystemInfo(const XMLElement * const aSysElem) { + if (aSysElem == nullptr) { + return; + } + + /* + * Please note that this function assumes that a given system XML element + * and its child elements follow DTD. If it does not, it will cause a + * segmentation fault due to the failure of finding expected attributes. + */ + + /* read number of cameras available in the system */ + const XMLElement *xmlElem = aSysElem->FirstChildElement("num_cameras"); + if (xmlElem != nullptr) { + mSystemInfo.numCameras = + stoi(xmlElem->FindAttribute("value")->Value()); + } +} + + +void ConfigManager::readDisplayInfo(const XMLElement * const aDisplayElem) { + if (aDisplayElem == nullptr) { + ALOGW("XML file does not have required camera element"); + return; + } + + const XMLElement *curDev = aDisplayElem->FirstChildElement("device"); + while (curDev != nullptr) { + const char *id = curDev->FindAttribute("id")->Value(); + //const char *pos = curDev->FirstAttribute("position")->Value(); + + unique_ptr<DisplayInfo> dpy(new DisplayInfo()); + if (dpy == nullptr) { + ALOGE("Failed to allocate memory for DisplayInfo"); + return; + } + + const XMLElement *cap = curDev->FirstChildElement("caps"); + if (cap != nullptr) { + const XMLElement *curStream = cap->FirstChildElement("stream"); + while (curStream != nullptr) { + /* read 4 attributes */ + const XMLAttribute *idAttr = curStream->FindAttribute("id"); + const XMLAttribute *widthAttr = curStream->FindAttribute("width"); + const XMLAttribute *heightAttr = curStream->FindAttribute("height"); + const XMLAttribute *fmtAttr = curStream->FindAttribute("format"); + + const int32_t id = stoi(idAttr->Value()); + int32_t pixFormat; + if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), + pixFormat)) { + RawStreamConfiguration cfg = { + id, + stoi(widthAttr->Value()), + stoi(heightAttr->Value()), + pixFormat, + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT, + 0 // unused + }; + dpy->streamConfigurations[id] = cfg; + } + + curStream = curStream->NextSiblingElement("stream"); + } + } + + mDisplayInfo[id] = std::move(dpy); + curDev = curDev->NextSiblingElement("device"); + } + + return; +} + + +bool ConfigManager::readConfigDataFromXML() noexcept { + XMLDocument xmlDoc; + + const int64_t parsingStart = android::elapsedRealtimeNano(); + + /* load and parse a configuration file */ + xmlDoc.LoadFile(mConfigFilePath); + if (xmlDoc.ErrorID() != XML_SUCCESS) { + ALOGE("Failed to load and/or parse a configuration file, %s", xmlDoc.ErrorStr()); + return false; + } + + /* retrieve the root element */ + const XMLElement *rootElem = xmlDoc.RootElement(); + if (strcmp(rootElem->Name(), "configuration")) { + ALOGE("A configuration file is not in the required format. " + "See /etc/automotive/evs/evs_configuration.dtd"); + return false; + } + + /* + * parse camera information; this needs to be done before reading system + * information + */ + readCameraInfo(rootElem->FirstChildElement("camera")); + + /* parse system information */ + readSystemInfo(rootElem->FirstChildElement("system")); + + /* parse display information */ + readDisplayInfo(rootElem->FirstChildElement("display")); + + const int64_t parsingEnd = android::elapsedRealtimeNano(); + ALOGI("Parsing configuration file takes %lf (ms)", + (double)(parsingEnd - parsingStart) / 1000000.0); + + + return true; +} + + +void ConfigManager::addCameraDevices(const char *devices, + unique_ptr<CameraGroup> &aGroup) { + stringstream device_list(devices); + string token; + while (getline(device_list, token, ',')) { + aGroup->devices.emplace(token); + } +} + + +std::unique_ptr<ConfigManager> ConfigManager::Create(const char *path) { + unique_ptr<ConfigManager> cfgMgr(new ConfigManager(path)); + + /* + * Read a configuration from XML file + * + * If this is too slow, ConfigManager::readConfigDataFromBinary() and + * ConfigManager::writeConfigDataToBinary()can serialize CameraInfo object + * to the filesystem and construct CameraInfo instead; this was + * evaluated as 10x faster. + */ + if (!cfgMgr->readConfigDataFromXML()) { + return nullptr; + } else { + return cfgMgr; + } +} + diff --git a/automotive/evs/1.1/default/ConfigManager.h b/automotive/evs/1.1/default/ConfigManager.h new file mode 100644 index 0000000000..0275f904e5 --- /dev/null +++ b/automotive/evs/1.1/default/ConfigManager.h @@ -0,0 +1,336 @@ +/* + * 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 CONFIG_MANAGER_H +#define CONFIG_MANAGER_H + +#include <vector> +#include <string> +#include <unordered_map> +#include <unordered_set> + +#include <tinyxml2.h> + +#include <system/camera_metadata.h> +#include <log/log.h> +#include <android/hardware/automotive/evs/1.1/types.h> + +#include "ConfigManagerUtil.h" + +using namespace std; +using namespace tinyxml2; + +using ::android::hardware::hidl_vec; +using ::android::hardware::camera::device::V3_2::Stream; +using ::android::hardware::automotive::evs::V1_1::CameraParam; + +/* + * Plese note that this is different from what is defined in + * libhardware/modules/camera/3_4/metadata/types.h; this has one additional + * field to store a framerate. + */ +const size_t kStreamCfgSz = 6; +typedef std::array<int32_t, kStreamCfgSz> RawStreamConfiguration; + +class ConfigManager { +public: + static std::unique_ptr<ConfigManager> Create(const char *path = ""); + ConfigManager(const ConfigManager&) = delete; + ConfigManager& operator=(const ConfigManager&) = delete; + + virtual ~ConfigManager(); + + /* Camera device's capabilities and metadata */ + class CameraInfo { + public: + CameraInfo() : + characteristics(nullptr) { + /* Nothing to do */ + } + + virtual ~CameraInfo() { + free_camera_metadata(characteristics); + } + + /* Allocate memory for camera_metadata_t */ + bool allocate(size_t entry_cap, size_t data_cap) { + if (characteristics != nullptr) { + ALOGE("Camera metadata is already allocated"); + return false; + } + + characteristics = allocate_camera_metadata(entry_cap, data_cap); + return characteristics != nullptr; + } + + /* + * List of supported controls that the master client can program. + * Paraemters are stored with its valid range + */ + unordered_map<CameraParam, + tuple<int32_t, int32_t, int32_t>> controls; + + /* List of supported frame rates */ + unordered_set<int32_t> frameRates; + + /* + * List of supported output stream configurations; each array stores + * format, width, height, and direction values in the order. + */ + unordered_map<int32_t, RawStreamConfiguration> streamConfigurations; + + /* + * Internal storage for camera metadata. Each entry holds a pointer to + * data and number of elements + */ + unordered_map<camera_metadata_tag_t, + pair<unique_ptr<void *>, size_t>> cameraMetadata; + + /* Camera module characteristics */ + camera_metadata_t *characteristics; + }; + + class CameraGroup { + public: + CameraGroup() {} + + /* ID of member camera devices */ + unordered_set<string> devices; + + /* The capture operation of member camera devices are synchronized */ + bool synchronized = false; + + /* + * List of stream configurations that are supposed by all camera devices + * in this group. + */ + unordered_map<int32_t, RawStreamConfiguration> streamConfigurations; + }; + + class SystemInfo { + public: + /* number of available cameras */ + int32_t numCameras = 0; + }; + + class DisplayInfo { + public: + /* + * List of supported input stream configurations; each array stores + * format, width, height, and direction values in the order. + */ + unordered_map<int32_t, RawStreamConfiguration> streamConfigurations; + }; + + /* + * Return system information + * + * @return SystemInfo + * Constant reference of SystemInfo. + */ + const SystemInfo &getSystemInfo() { + return mSystemInfo; + } + + /* + * Return a list of cameras + * + * This function assumes that it is not being called frequently. + * + * @return vector<string> + * A vector that contains unique camera device identifiers. + */ + vector<string> getCameraList() { + vector<string> aList; + for (auto &v : mCameraInfo) { + aList.emplace_back(v.first); + } + + return aList; + } + + + /* + * Return a list of cameras + * + * @return CameraGroup + * A pointer to a camera group identified by a given id. + */ + unique_ptr<CameraGroup>& getCameraGroup(const string& gid) { + return mCameraGroups[gid]; + } + + + /* + * Return a camera metadata + * + * @param cameraId + * Unique camera node identifier in string + * + * @return unique_ptr<CameraInfo> + * A pointer to CameraInfo that is associated with a given camera + * ID. This returns a null pointer if this does not recognize a + * given camera identifier. + */ + unique_ptr<CameraInfo>& getCameraInfo(const string cameraId) noexcept { + return mCameraInfo[cameraId]; + } + +private: + /* Constructors */ + ConfigManager(const char *xmlPath) : + mConfigFilePath(xmlPath) { + } + + /* System configuration */ + SystemInfo mSystemInfo; + + /* Internal data structure for camera device information */ + unordered_map<string, unique_ptr<CameraInfo>> mCameraInfo; + + /* Internal data structure for camera device information */ + unordered_map<string, unique_ptr<DisplayInfo>> mDisplayInfo; + + /* Camera groups are stored in <groud id, CameraGroup> hash map */ + unordered_map<string, unique_ptr<CameraGroup>> mCameraGroups; + + /* + * Camera positions are stored in <position, camera id set> hash map. + * The position must be one of front, rear, left, and right. + */ + unordered_map<string, unordered_set<string>> mCameraPosition; + + /* A path to XML configuration file */ + const char *mConfigFilePath; + + /* + * Parse a given EVS configuration file and store the information + * internally. + * + * @return bool + * True if it completes parsing a file successfully. + */ + bool readConfigDataFromXML() noexcept; + + /* + * read the information of the vehicle + * + * @param aSysElem + * A pointer to "system" XML element. + */ + void readSystemInfo(const XMLElement * const aSysElem); + + /* + * read the information of camera devices + * + * @param aCameraElem + * A pointer to "camera" XML element that may contain multiple + * "device" elements. + */ + void readCameraInfo(const XMLElement * const aCameraElem); + + /* + * read display device information + * + * @param aDisplayElem + * A pointer to "display" XML element that may contain multiple + * "device" elements. + */ + void readDisplayInfo(const XMLElement * const aDisplayElem); + + /* + * read camera device information + * + * @param aDeviceElem + * A pointer to "device" XML element that contains camera module + * capability info and its characteristics. + * + * @return unique_ptr<CameraInfo> + * A pointer to CameraInfo class that contains camera module + * capability and characteristics. Please note that this transfers + * the ownership of created CameraInfo to the caller. + */ + unique_ptr<CameraInfo> readCameraDeviceInfo(const XMLElement *aDeviceElem); + + /* + * read camera metadata + * + * @param aCapElem + * A pointer to "cap" XML element. + * @param aCamera + * A pointer to CameraInfo that is being filled by this method. + * @param dataSize + * Required size of memory to store camera metadata found in this + * method. This is calculated in this method and returned to the + * caller for camera_metadata allocation. + * + * @return size_t + * Number of camera metadata entries + */ + size_t readCameraCapabilities(const XMLElement * const aCapElem, + unique_ptr<CameraInfo> &aCamera, + size_t &dataSize); + + /* + * read camera metadata + * + * @param aParamElem + * A pointer to "characteristics" XML element. + * @param aCamera + * A pointer to CameraInfo that is being filled by this method. + * @param dataSize + * Required size of memory to store camera metadata found in this + * method. + * + * @return size_t + * Number of camera metadata entries + */ + size_t readCameraMetadata(const XMLElement * const aParamElem, + unique_ptr<CameraInfo> &aCamera, + size_t &dataSize); + + /* + * construct camera_metadata_t from camera capabilities and metadata + * + * @param aCamera + * A pointer to CameraInfo that is being filled by this method. + * @param totalEntries + * Number of camera metadata entries to be added. + * @param totalDataSize + * Sum of sizes of camera metadata entries to be added. + * + * @return bool + * False if either it fails to allocate memory for camera metadata + * or its size is not large enough to add all found camera metadata + * entries. + */ + bool constructCameraMetadata(unique_ptr<CameraInfo> &aCamera, + const size_t totalEntries, + const size_t totalDataSize); + + /* + * parse a comma-separated list of camera devices and add them to + * CameraGroup. + * + * @param devices + * A comma-separated list of camera device identifiers. + * @param aGroup + * Camera group which cameras will be added to. + */ + void addCameraDevices(const char *devices, + unique_ptr<CameraGroup> &aGroup); +}; +#endif // CONFIG_MANAGER_H + diff --git a/automotive/evs/1.1/default/ConfigManagerUtil.cpp b/automotive/evs/1.1/default/ConfigManagerUtil.cpp new file mode 100644 index 0000000000..8206daa6d7 --- /dev/null +++ b/automotive/evs/1.1/default/ConfigManagerUtil.cpp @@ -0,0 +1,131 @@ +/* + * 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 "ConfigManagerUtil.h" + +#include <string> +#include <sstream> +#include <linux/videodev2.h> + +#include <log/log.h> +#include <system/graphics-base-v1.0.h> + + +bool ConfigManagerUtil::convertToEvsCameraParam(const string &id, + CameraParam &camParam) { + string trimmed = ConfigManagerUtil::trimString(id); + bool success = true; + + if (!trimmed.compare("BRIGHTNESS")) { + camParam = CameraParam::BRIGHTNESS; + } else if (!trimmed.compare("CONTRAST")) { + camParam = CameraParam::CONTRAST; + } else if (!trimmed.compare("AUTOGAIN")) { + camParam = CameraParam::AUTOGAIN; + } else if (!trimmed.compare("GAIN")) { + camParam = CameraParam::GAIN; + } else if (!trimmed.compare("AUTO_WHITE_BALANCE")) { + camParam = CameraParam::AUTO_WHITE_BALANCE; + } else if (!trimmed.compare("WHITE_BALANCE_TEMPERATURE")) { + camParam = CameraParam::WHITE_BALANCE_TEMPERATURE; + } else if (!trimmed.compare("SHARPNESS")) { + camParam = CameraParam::SHARPNESS; + } else if (!trimmed.compare("AUTO_EXPOSURE")) { + camParam = CameraParam::AUTO_EXPOSURE; + } else if (!trimmed.compare("ABSOLUTE_EXPOSURE")) { + camParam = CameraParam::ABSOLUTE_EXPOSURE; + } else if (!trimmed.compare("ABSOLUTE_FOCUS")) { + camParam = CameraParam::ABSOLUTE_FOCUS; + } else if (!trimmed.compare("AUTO_FOCUS")) { + camParam = CameraParam::AUTO_FOCUS; + } else if (!trimmed.compare("ABSOLUTE_ZOOM")) { + camParam = CameraParam::ABSOLUTE_ZOOM; + } else { + success = false; + } + + return success; +} + + +bool ConfigManagerUtil::convertToPixelFormat(const string &format, + int32_t &pixFormat) { + string trimmed = ConfigManagerUtil::trimString(format); + bool success = true; + + if (!trimmed.compare("RGBA_8888")) { + pixFormat = HAL_PIXEL_FORMAT_RGBA_8888; + } else if (!trimmed.compare("YCRCB_420_SP")) { + pixFormat = HAL_PIXEL_FORMAT_YCRCB_420_SP; + } else if (!trimmed.compare("YCBCR_422_I")) { + pixFormat = HAL_PIXEL_FORMAT_YCBCR_422_I; + } else { + success = false; + } + + return success; +} + + +bool ConfigManagerUtil::convertToMetadataTag(const char *name, + camera_metadata_tag &aTag) { + if (!strcmp(name, "LENS_DISTORTION")) { + aTag = ANDROID_LENS_DISTORTION; + } else if (!strcmp(name, "LENS_INTRINSIC_CALIBRATION")) { + aTag = ANDROID_LENS_INTRINSIC_CALIBRATION; + } else if (!strcmp(name, "LENS_POSE_ROTATION")) { + aTag = ANDROID_LENS_POSE_ROTATION; + } else if (!strcmp(name, "LENS_POSE_TRANSLATION")) { + aTag = ANDROID_LENS_POSE_TRANSLATION; + } else { + return false; + } + + return true; +} + + +float *ConfigManagerUtil::convertFloatArray(const char *sz, const char *vals, + size_t &count, const char delimiter) { + string size_string(sz); + string value_string(vals); + + count = stoi(size_string); + float *result = new float[count]; + stringstream values(value_string); + + int32_t idx = 0; + string token; + while (getline(values, token, delimiter)) { + result[idx++] = stof(token); + } + + return result; +} + + +string ConfigManagerUtil::trimString(const string &src, const string &ws) { + const auto s = src.find_first_not_of(ws); + if (s == string::npos) { + return ""; + } + + const auto e = src.find_last_not_of(ws); + const auto r = e - s + 1; + + return src.substr(s, r); +} + diff --git a/automotive/evs/1.1/default/ConfigManagerUtil.h b/automotive/evs/1.1/default/ConfigManagerUtil.h new file mode 100644 index 0000000000..8c89ae7745 --- /dev/null +++ b/automotive/evs/1.1/default/ConfigManagerUtil.h @@ -0,0 +1,61 @@ +/* + * 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 CONFIG_MANAGER_UTIL_H +#define CONFIG_MANAGER_UTIL_H + +#include <utility> +#include <string> +#include <system/camera_metadata.h> +#include <android/hardware/automotive/evs/1.1/types.h> + +using namespace std; +using ::android::hardware::automotive::evs::V1_1::CameraParam; + + +class ConfigManagerUtil { +public: + /** + * Convert a given string into V4L2_CID_* + */ + static bool convertToEvsCameraParam(const string &id, + CameraParam &camParam); + /** + * Convert a given string into android.hardware.graphics.common.PixelFormat + */ + static bool convertToPixelFormat(const string &format, + int32_t &pixelFormat); + /** + * Convert a given string into corresponding camera metadata data tag defined in + * system/media/camera/include/system/camera_metadta_tags.h + */ + static bool convertToMetadataTag(const char *name, + camera_metadata_tag &aTag); + /** + * Convert a given string into a floating value array + */ + static float *convertFloatArray(const char *sz, + const char *vals, + size_t &count, + const char delimiter = ','); + /** + * Trim a string + */ + static string trimString(const string &src, + const string &ws = " \n\r\t\f\v"); +}; + +#endif // CONFIG_MANAGER_UTIL_H + diff --git a/automotive/evs/1.1/default/EvsCamera.cpp b/automotive/evs/1.1/default/EvsCamera.cpp new file mode 100644 index 0000000000..5ba753da2e --- /dev/null +++ b/automotive/evs/1.1/default/EvsCamera.cpp @@ -0,0 +1,676 @@ +/* + * 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.automotive.evs@1.1-service" + +#include "EvsCamera.h" +#include "EvsEnumerator.h" + +#include <ui/GraphicBufferAllocator.h> +#include <ui/GraphicBufferMapper.h> + + +namespace android { +namespace hardware { +namespace automotive { +namespace evs { +namespace V1_1 { +namespace implementation { + + +// Special camera names for which we'll initialize alternate test data +const char EvsCamera::kCameraName_Backup[] = "backup"; + + +// Arbitrary limit on number of graphics buffers allowed to be allocated +// Safeguards against unreasonable resource consumption and provides a testable limit +const unsigned MAX_BUFFERS_IN_FLIGHT = 100; + + +EvsCamera::EvsCamera(const char *id, + unique_ptr<ConfigManager::CameraInfo> &camInfo) : + mFramesAllowed(0), + mFramesInUse(0), + mStreamState(STOPPED), + mCameraInfo(camInfo) { + + ALOGD("EvsCamera instantiated"); + + /* set a camera id */ + mDescription.v1.cameraId = id; + + /* set camera metadata */ + mDescription.metadata.setToExternal((uint8_t *)camInfo->characteristics, + get_camera_metadata_size(camInfo->characteristics)); +} + + +EvsCamera::~EvsCamera() { + ALOGD("EvsCamera being destroyed"); + forceShutdown(); +} + + +// +// This gets called if another caller "steals" ownership of the camera +// +void EvsCamera::forceShutdown() +{ + ALOGD("EvsCamera forceShutdown"); + + // Make sure our output stream is cleaned up + // (It really should be already) + stopVideoStream(); + + // Claim the lock while we work on internal state + std::lock_guard <std::mutex> lock(mAccessLock); + + // Drop all the graphics buffers we've been using + if (mBuffers.size() > 0) { + GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); + for (auto&& rec : mBuffers) { + if (rec.inUse) { + ALOGE("Error - releasing buffer despite remote ownership"); + } + alloc.free(rec.handle); + rec.handle = nullptr; + } + mBuffers.clear(); + } + + // Put this object into an unrecoverable error state since somebody else + // is going to own the underlying camera now + mStreamState = DEAD; +} + + +// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow. +Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) { + ALOGD("getCameraInfo"); + + // Send back our self description + _hidl_cb(mDescription.v1); + return Void(); +} + + +Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) { + ALOGD("setMaxFramesInFlight"); + std::lock_guard<std::mutex> lock(mAccessLock); + + // If we've been displaced by another owner of the camera, then we can't do anything else + if (mStreamState == DEAD) { + ALOGE("ignoring setMaxFramesInFlight call when camera has been lost."); + return EvsResult::OWNERSHIP_LOST; + } + + // We cannot function without at least one video buffer to send data + if (bufferCount < 1) { + ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested"); + return EvsResult::INVALID_ARG; + } + + // Update our internal state + if (setAvailableFrames_Locked(bufferCount)) { + return EvsResult::OK; + } else { + return EvsResult::BUFFER_NOT_AVAILABLE; + } +} + + +Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream_1_0>& stream) { + ALOGD("startVideoStream"); + std::lock_guard<std::mutex> lock(mAccessLock); + + // If we've been displaced by another owner of the camera, then we can't do anything else + if (mStreamState == DEAD) { + ALOGE("ignoring startVideoStream call when camera has been lost."); + return EvsResult::OWNERSHIP_LOST; + } + if (mStreamState != STOPPED) { + ALOGE("ignoring startVideoStream call when a stream is already running."); + return EvsResult::STREAM_ALREADY_RUNNING; + } + + // If the client never indicated otherwise, configure ourselves for a single streaming buffer + if (mFramesAllowed < 1) { + if (!setAvailableFrames_Locked(1)) { + ALOGE("Failed to start stream because we couldn't get a graphics buffer"); + return EvsResult::BUFFER_NOT_AVAILABLE; + } + } + + // Record the user's callback for use when we have a frame ready + mStream = IEvsCameraStream_1_1::castFrom(stream).withDefault(nullptr); + if (mStream == nullptr) { + ALOGE("Default implementation does not support v1.0 IEvsCameraStream"); + return EvsResult::INVALID_ARG; + } + + // Start the frame generation thread + mStreamState = RUNNING; + mCaptureThread = std::thread([this](){ generateFrames(); }); + + return EvsResult::OK; +} + + +Return<void> EvsCamera::doneWithFrame(const BufferDesc_1_0& buffer) { + std::lock_guard <std::mutex> lock(mAccessLock); + returnBuffer(buffer.bufferId, buffer.memHandle); + + return Void(); +} + + +Return<void> EvsCamera::stopVideoStream() { + ALOGD("stopVideoStream"); + std::unique_lock <std::mutex> lock(mAccessLock); + + if (mStreamState == RUNNING) { + // Tell the GenerateFrames loop we want it to stop + mStreamState = STOPPING; + + // Block outside the mutex until the "stop" flag has been acknowledged + // We won't send any more frames, but the client might still get some already in flight + ALOGD("Waiting for stream thread to end..."); + lock.unlock(); + mCaptureThread.join(); + lock.lock(); + + mStreamState = STOPPED; + mStream = nullptr; + ALOGD("Stream marked STOPPED."); + } + + return Void(); +} + + +Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) { + ALOGD("getExtendedInfo"); + std::lock_guard<std::mutex> lock(mAccessLock); + + // For any single digit value, return the index itself as a test value + if (opaqueIdentifier <= 9) { + return opaqueIdentifier; + } + + // Return zero by default as required by the spec + return 0; +} + + +Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) { + ALOGD("setExtendedInfo"); + std::lock_guard<std::mutex> lock(mAccessLock); + + // If we've been displaced by another owner of the camera, then we can't do anything else + if (mStreamState == DEAD) { + ALOGE("ignoring setExtendedInfo call when camera has been lost."); + return EvsResult::OWNERSHIP_LOST; + } + + // We don't store any device specific information in this implementation + return EvsResult::INVALID_ARG; +} + + +// Methods from ::android::hardware::automotive::evs::V1_1::IEvsCamera follow. +Return<void> EvsCamera::getCameraInfo_1_1(getCameraInfo_1_1_cb _hidl_cb) { + ALOGD("getCameraInfo_1_1"); + + // Send back our self description + _hidl_cb(mDescription); + return Void(); +} + + +Return<EvsResult> EvsCamera::doneWithFrame_1_1(const BufferDesc_1_1& bufDesc) { + std::lock_guard <std::mutex> lock(mAccessLock); + returnBuffer(bufDesc.bufferId, bufDesc.buffer.nativeHandle); + + return EvsResult::OK; +} + + +Return<EvsResult> EvsCamera::pauseVideoStream() { + // Default implementation does not support this. + return EvsResult::UNDERLYING_SERVICE_ERROR; +} + + +Return<EvsResult> EvsCamera::resumeVideoStream() { + // Default implementation does not support this. + return EvsResult::UNDERLYING_SERVICE_ERROR; +} + + +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::getParameterList(getParameterList_cb _hidl_cb) { + hidl_vec<CameraParam> hidlCtrls; + hidlCtrls.resize(mCameraInfo->controls.size()); + unsigned idx = 0; + for (auto& [cid, cfg] : mCameraInfo->controls) { + hidlCtrls[idx++] = cid; + } + + _hidl_cb(hidlCtrls); + return Void(); +} + + +Return<void> EvsCamera::getIntParameterRange(CameraParam id, + getIntParameterRange_cb _hidl_cb) { + auto range = mCameraInfo->controls[id]; + _hidl_cb(get<0>(range), get<1>(range), get<2>(range)); + return Void(); +} + + +Return<void> EvsCamera::setIntParameter(CameraParam id, int32_t value, + setIntParameter_cb _hidl_cb) { + // Default implementation does not support this. + (void)id; + (void)value; + _hidl_cb(EvsResult::INVALID_ARG, 0); + return Void(); +} + + +Return<void> EvsCamera::getIntParameter(CameraParam id, + getIntParameter_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"); + return false; + } + if (bufferCount > MAX_BUFFERS_IN_FLIGHT) { + ALOGE("Rejecting buffer request in excess of internal limit"); + return false; + } + + // Is an increase required? + if (mFramesAllowed < bufferCount) { + // An increase is required + unsigned needed = bufferCount - mFramesAllowed; + ALOGI("Allocating %d buffers for camera frames", needed); + + unsigned added = increaseAvailableFrames_Locked(needed); + if (added != needed) { + // If we didn't add all the frames we needed, then roll back to the previous state + ALOGE("Rolling back to previous frame queue size"); + decreaseAvailableFrames_Locked(added); + return false; + } + } else if (mFramesAllowed > bufferCount) { + // A decrease is required + unsigned framesToRelease = mFramesAllowed - bufferCount; + ALOGI("Returning %d camera frame buffers", framesToRelease); + + unsigned released = decreaseAvailableFrames_Locked(framesToRelease); + if (released != framesToRelease) { + // This shouldn't happen with a properly behaving client because the client + // should only make this call after returning sufficient outstanding buffers + // to allow a clean resize. + ALOGE("Buffer queue shrink failed -- too many buffers currently in use?"); + } + } + + return true; +} + + +unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) { + // Acquire the graphics buffer allocator + GraphicBufferAllocator &alloc(GraphicBufferAllocator::get()); + + unsigned added = 0; + + while (added < numToAdd) { + buffer_handle_t memHandle = nullptr; + status_t result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, + &memHandle, &mStride, 0, "EvsCamera"); + if (result != NO_ERROR) { + ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight); + break; + } + if (!memHandle) { + ALOGE("We didn't get a buffer handle back from the allocator"); + break; + } + + // Find a place to store the new buffer + bool stored = false; + for (auto&& rec : mBuffers) { + if (rec.handle == nullptr) { + // Use this existing entry + rec.handle = memHandle; + rec.inUse = false; + stored = true; + break; + } + } + if (!stored) { + // Add a BufferRecord wrapping this handle to our set of available buffers + mBuffers.emplace_back(memHandle); + } + + mFramesAllowed++; + added++; + } + + return added; +} + + +unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) { + // Acquire the graphics buffer allocator + GraphicBufferAllocator &alloc(GraphicBufferAllocator::get()); + + unsigned removed = 0; + + for (auto&& rec : mBuffers) { + // Is this record not in use, but holding a buffer that we can free? + if ((rec.inUse == false) && (rec.handle != nullptr)) { + // Release buffer and update the record so we can recognize it as "empty" + alloc.free(rec.handle); + rec.handle = nullptr; + + mFramesAllowed--; + removed++; + + if (removed == numToRemove) { + break; + } + } + } + + return removed; +} + + +// This is the asynchronous frame generation thread that runs in parallel with the +// main serving thread. There is one for each active camera instance. +void EvsCamera::generateFrames() { + ALOGD("Frame generation loop started"); + + unsigned idx; + + while (true) { + bool timeForFrame = false; + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + + // Lock scope for updating shared state + { + std::lock_guard<std::mutex> lock(mAccessLock); + + if (mStreamState != RUNNING) { + // Break out of our main thread loop + break; + } + + // Are we allowed to issue another buffer? + if (mFramesInUse >= mFramesAllowed) { + // Can't do anything right now -- skip this frame + ALOGW("Skipped a frame because too many are in flight\n"); + } else { + // Identify an available buffer to fill + for (idx = 0; idx < mBuffers.size(); idx++) { + if (!mBuffers[idx].inUse) { + if (mBuffers[idx].handle != nullptr) { + // Found an available record, so stop looking + break; + } + } + } + if (idx >= mBuffers.size()) { + // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed + ALOGE("Failed to find an available buffer slot\n"); + } else { + // We're going to make the frame busy + mBuffers[idx].inUse = true; + mFramesInUse++; + timeForFrame = true; + } + } + } + + if (timeForFrame) { + // Assemble the buffer description we'll transmit below + BufferDesc_1_1 newBuffer = {}; + AHardwareBuffer_Desc* pDesc = + reinterpret_cast<AHardwareBuffer_Desc *>(&newBuffer.buffer.description); + pDesc->width = mWidth; + pDesc->height = mHeight; + pDesc->layers = 1; + pDesc->format = mFormat; + pDesc->usage = mUsage; + pDesc->stride = mStride; + newBuffer.buffer.nativeHandle = mBuffers[idx].handle; + newBuffer.pixelSize = sizeof(uint32_t); + newBuffer.bufferId = idx; + + // Write test data into the image buffer + fillTestFrame(newBuffer); + + // Issue the (asynchronous) callback to the client -- can't be holding the lock + auto result = mStream->deliverFrame_1_1(newBuffer); + if (result.isOk()) { + ALOGD("Delivered %p as id %d", + newBuffer.buffer.nativeHandle.getNativeHandle(), newBuffer.bufferId); + } else { + // This can happen if the client dies and is likely unrecoverable. + // To avoid consuming resources generating failing calls, we stop sending + // frames. Note, however, that the stream remains in the "STREAMING" state + // until cleaned up on the main thread. + ALOGE("Frame delivery call failed in the transport layer."); + + // Since we didn't actually deliver it, mark the frame as available + std::lock_guard<std::mutex> lock(mAccessLock); + mBuffers[idx].inUse = false; + mFramesInUse--; + + break; + } + } + + // We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement + static const int kTargetFrameRate = 12; + static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate; + const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + const nsecs_t workTimeUs = (now - startTime) / 1000; + const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs; + if (sleepDurationUs > 0) { + usleep(sleepDurationUs); + } + } + + // If we've been asked to stop, send an event to signal the actual end of stream + EvsEvent event; + event.aType = EvsEventType::STREAM_STOPPED; + auto result = mStream->notify(event); + if (!result.isOk()) { + ALOGE("Error delivering end of stream marker"); + } + + return; +} + + +void EvsCamera::fillTestFrame(const BufferDesc_1_1& buff) { + // Lock our output buffer for writing + uint32_t *pixels = nullptr; + const AHardwareBuffer_Desc* pDesc = + reinterpret_cast<const AHardwareBuffer_Desc *>(&buff.buffer.description); + GraphicBufferMapper &mapper = GraphicBufferMapper::get(); + mapper.lock(buff.buffer.nativeHandle, + GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER, + android::Rect(pDesc->width, pDesc->height), + (void **) &pixels); + + // If we failed to lock the pixel buffer, we're about to crash, but log it first + if (!pixels) { + ALOGE("Camera failed to gain access to image buffer for writing"); + } + + // Fill in the test pixels + for (unsigned row = 0; row < pDesc->height; row++) { + for (unsigned col = 0; col < pDesc->width; col++) { + // Index into the row to check the pixel at this column. + // We expect 0xFF in the LSB channel, a vertical gradient in the + // second channel, a horitzontal gradient in the third channel, and + // 0xFF in the MSB. + // The exception is the very first 32 bits which is used for the + // time varying frame signature to avoid getting fooled by a static image. + uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB + ((row & 0xFF) << 8) | // vertical gradient + ((col & 0xFF) << 16); // horizontal gradient + if ((row | col) == 0) { + static uint32_t sFrameTicker = 0; + expectedPixel = (sFrameTicker) & 0xFF; + sFrameTicker++; + } + pixels[col] = expectedPixel; + } + // Point to the next row + // NOTE: stride retrieved from gralloc is in units of pixels + pixels = pixels + pDesc->stride; + } + + // Release our output buffer + mapper.unlock(buff.buffer.nativeHandle); +} + + +void EvsCamera::fillTestFrame(const BufferDesc_1_0& buff) { + BufferDesc_1_1 newBufDesc = {}; + AHardwareBuffer_Desc desc = { + buff.width, // width + buff.height, // height + 1, // layers, always 1 for EVS + buff.format, // One of AHardwareBuffer_Format + buff.usage, // Combination of AHardwareBuffer_UsageFlags + buff.stride, // Row stride in pixels + 0, // Reserved + 0 // Reserved + }; + memcpy(&desc, &newBufDesc.buffer.description, sizeof(desc)); + newBufDesc.buffer.nativeHandle = buff.memHandle; + newBufDesc.pixelSize = buff.pixelSize; + newBufDesc.bufferId = buff.bufferId; + + return fillTestFrame(newBufDesc); +} + + +void EvsCamera::returnBuffer(const uint32_t bufferId, const buffer_handle_t memHandle) { + std::lock_guard <std::mutex> lock(mAccessLock); + + if (memHandle == nullptr) { + ALOGE("ignoring doneWithFrame called with null handle"); + } else if (bufferId >= mBuffers.size()) { + ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", + bufferId, mBuffers.size()-1); + } else if (!mBuffers[bufferId].inUse) { + ALOGE("ignoring doneWithFrame called on frame %d which is already free", + bufferId); + } else { + // Mark the frame as available + mBuffers[bufferId].inUse = false; + mFramesInUse--; + + // If this frame's index is high in the array, try to move it down + // to improve locality after mFramesAllowed has been reduced. + if (bufferId >= mFramesAllowed) { + // Find an empty slot lower in the array (which should always exist in this case) + for (auto&& rec : mBuffers) { + if (rec.handle == nullptr) { + rec.handle = mBuffers[bufferId].handle; + mBuffers[bufferId].handle = nullptr; + break; + } + } + } + } +} + + +sp<EvsCamera> EvsCamera::Create(const char *deviceName) { + unique_ptr<ConfigManager::CameraInfo> nullCamInfo = nullptr; + + return Create(deviceName, nullCamInfo); +} + + +sp<EvsCamera> EvsCamera::Create(const char *deviceName, + unique_ptr<ConfigManager::CameraInfo> &camInfo, + const Stream *streamCfg) { + sp<EvsCamera> evsCamera = new EvsCamera(deviceName, camInfo); + if (evsCamera == nullptr) { + return nullptr; + } + + /* default implementation does not use a given configuration */ + (void)streamCfg; + + /* Use the first resolution from the list for the testing */ + auto it = camInfo->streamConfigurations.begin(); + evsCamera->mWidth = it->second[1]; + evsCamera->mHeight = it->second[2]; + evsCamera->mDescription.v1.vendorFlags = 0xFFFFFFFF; // Arbitrary test value + + evsCamera->mFormat = HAL_PIXEL_FORMAT_RGBA_8888; + evsCamera->mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE | + GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY; + + return evsCamera; +} + + +} // namespace implementation +} // namespace V1_0 +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/evs/1.1/default/EvsCamera.h b/automotive/evs/1.1/default/EvsCamera.h new file mode 100644 index 0000000000..c15b4b117b --- /dev/null +++ b/automotive/evs/1.1/default/EvsCamera.h @@ -0,0 +1,154 @@ +/* + * 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_AUTOMOTIVE_EVS_V1_1_EVSCAMERA_H +#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERA_H + +#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> + +#include "ConfigManager.h" + +using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc; +using BufferDesc_1_1 = ::android::hardware::automotive::evs::V1_1::BufferDesc; +using IEvsCameraStream_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCameraStream; +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 { +namespace hardware { +namespace automotive { +namespace evs { +namespace V1_1 { +namespace implementation { + + +// From EvsEnumerator.h +class EvsEnumerator; + + +class EvsCamera : public IEvsCamera { +public: + // Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow. + Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override; + Return<EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override; + Return<EvsResult> startVideoStream(const ::android::sp<IEvsCameraStream_1_0>& stream) override; + Return<void> stopVideoStream() override; + Return<void> doneWithFrame(const BufferDesc_1_0& buffer) override; + + Return<int32_t> getExtendedInfo(uint32_t opaqueIdentifier) override; + Return<EvsResult> setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) override; + + // Methods from ::android::hardware::automotive::evs::V1_1::IEvsCamera follow. + Return<void> getCameraInfo_1_1(getCameraInfo_1_1_cb _hidl_cb) override; + 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> getParameterList(getParameterList_cb _hidl_cb) override; + Return<void> getIntParameterRange(CameraParam id, + getIntParameterRange_cb _hidl_cb) override; + Return<void> setIntParameter(CameraParam id, int32_t value, + setIntParameter_cb _hidl_cb) override; + Return<void> getIntParameter(CameraParam id, + getIntParameter_cb _hidl_cb) override; + + static sp<EvsCamera> Create(const char *deviceName); + static sp<EvsCamera> Create(const char *deviceName, + unique_ptr<ConfigManager::CameraInfo> &camInfo, + const Stream *streamCfg = nullptr); + EvsCamera(const EvsCamera&) = delete; + EvsCamera& operator=(const EvsCamera&) = delete; + + virtual ~EvsCamera() override; + void forceShutdown(); // This gets called if another caller "steals" ownership of the camera + + const CameraDesc& getDesc() { return mDescription; }; + + static const char kCameraName_Backup[]; + +private: + EvsCamera(const char *id, + unique_ptr<ConfigManager::CameraInfo> &camInfo); + // These three functions are expected to be called while mAccessLock is held + // + bool setAvailableFrames_Locked(unsigned bufferCount); + unsigned increaseAvailableFrames_Locked(unsigned numToAdd); + unsigned decreaseAvailableFrames_Locked(unsigned numToRemove); + + void generateFrames(); + void fillTestFrame(const BufferDesc_1_0& buff); + void fillTestFrame(const BufferDesc_1_1& buff); + void returnBuffer(const uint32_t bufferId, const buffer_handle_t memHandle); + + sp<EvsEnumerator> mEnumerator; // The enumerator object that created this camera + + CameraDesc mDescription = {}; // The properties of this camera + + std::thread mCaptureThread; // The thread we'll use to synthesize frames + + uint32_t mWidth = 0; // Horizontal pixel count in the buffers + uint32_t mHeight = 0; // Vertical pixel count in the buffers + uint32_t mFormat = 0; // Values from android_pixel_format_t + uint64_t mUsage = 0; // Values from from Gralloc.h + uint32_t mStride = 0; // Bytes per line in the buffers + + sp<IEvsCameraStream_1_1> mStream = nullptr; // The callback used to deliver each frame + + struct BufferRecord { + buffer_handle_t handle; + bool inUse; + + explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false) {}; + }; + + std::vector <BufferRecord> mBuffers; // Graphics buffers to transfer images + unsigned mFramesAllowed; // How many buffers are we currently using + unsigned mFramesInUse; // How many buffers are currently outstanding + + enum StreamStateValues { + STOPPED, + RUNNING, + STOPPING, + DEAD, + }; + StreamStateValues mStreamState; + + // Synchronization necessary to deconflict mCaptureThread from the main service thread + std::mutex mAccessLock; + + // Static camera module information + unique_ptr<ConfigManager::CameraInfo> &mCameraInfo; +}; + +} // namespace implementation +} // namespace V1_1 +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERA_H diff --git a/automotive/evs/1.1/default/EvsDisplay.cpp b/automotive/evs/1.1/default/EvsDisplay.cpp new file mode 100644 index 0000000000..74c099ac30 --- /dev/null +++ b/automotive/evs/1.1/default/EvsDisplay.cpp @@ -0,0 +1,335 @@ +/* + * 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.automotive.evs@1.1-service" + +#include "EvsDisplay.h" + +#include <ui/GraphicBufferAllocator.h> +#include <ui/GraphicBufferMapper.h> + + +namespace android { +namespace hardware { +namespace automotive { +namespace evs { +namespace V1_1 { +namespace implementation { + + +EvsDisplay::EvsDisplay() { + ALOGD("EvsDisplay instantiated"); + + // Set up our self description + // NOTE: These are arbitrary values chosen for testing + mInfo.displayId = "Mock Display"; + mInfo.vendorFlags = 3870; + + // Assemble the buffer description we'll use for our render target + mBuffer.width = 320; + mBuffer.height = 240; + mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888; + mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER; + mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition + mBuffer.pixelSize = 4; +} + + +EvsDisplay::~EvsDisplay() { + ALOGD("EvsDisplay being destroyed"); + forceShutdown(); +} + + +/** + * This gets called if another caller "steals" ownership of the display + */ +void EvsDisplay::forceShutdown() +{ + ALOGD("EvsDisplay forceShutdown"); + std::lock_guard<std::mutex> lock(mAccessLock); + + // If the buffer isn't being held by a remote client, release it now as an + // optimization to release the resources more quickly than the destructor might + // get called. + if (mBuffer.memHandle) { + // Report if we're going away while a buffer is outstanding + if (mFrameBusy) { + ALOGE("EvsDisplay going down while client is holding a buffer"); + } + + // Drop the graphics buffer we've been using + GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); + alloc.free(mBuffer.memHandle); + mBuffer.memHandle = nullptr; + } + + // Put this object into an unrecoverable error state since somebody else + // is going to own the display now. + mRequestedState = DisplayState::DEAD; +} + + +/** + * Returns basic information about the EVS display provided by the system. + * See the description of the DisplayDesc structure for details. + */ +Return<void> EvsDisplay::getDisplayInfo(getDisplayInfo_cb _hidl_cb) { + ALOGD("getDisplayInfo"); + + // Send back our self description + _hidl_cb(mInfo); + return Void(); +} + + +/** + * Clients may set the display state to express their desired state. + * The HAL implementation must gracefully accept a request for any state + * while in any other state, although the response may be to ignore the request. + * The display is defined to start in the NOT_VISIBLE state upon initialization. + * The client is then expected to request the VISIBLE_ON_NEXT_FRAME state, and + * then begin providing video. When the display is no longer required, the client + * is expected to request the NOT_VISIBLE state after passing the last video frame. + */ +Return<EvsResult> EvsDisplay::setDisplayState(DisplayState state) { + ALOGD("setDisplayState"); + std::lock_guard<std::mutex> lock(mAccessLock); + + if (mRequestedState == DisplayState::DEAD) { + // This object no longer owns the display -- it's been superceeded! + return EvsResult::OWNERSHIP_LOST; + } + + // Ensure we recognize the requested state so we don't go off the rails + if (state < DisplayState::NUM_STATES) { + // Record the requested state + mRequestedState = state; + return EvsResult::OK; + } + else { + // Turn off the display if asked for an unrecognized state + mRequestedState = DisplayState::NOT_VISIBLE; + return EvsResult::INVALID_ARG; + } +} + + +/** + * The HAL implementation should report the actual current state, which might + * transiently differ from the most recently requested state. Note, however, that + * the logic responsible for changing display states should generally live above + * the device layer, making it undesirable for the HAL implementation to + * spontaneously change display states. + */ +Return<DisplayState> EvsDisplay::getDisplayState() { + ALOGD("getDisplayState"); + std::lock_guard<std::mutex> lock(mAccessLock); + + return mRequestedState; +} + + +/** + * This call returns a handle to a frame buffer associated with the display. + * This buffer may be locked and written to by software and/or GL. This buffer + * must be returned via a call to returnTargetBufferForDisplay() even if the + * display is no longer visible. + */ +// TODO: We need to know if/when our client dies so we can get the buffer back! (blocked b/31632518) +Return<void> EvsDisplay::getTargetBuffer(getTargetBuffer_cb _hidl_cb) { + ALOGD("getTargetBuffer"); + std::lock_guard<std::mutex> lock(mAccessLock); + + if (mRequestedState == DisplayState::DEAD) { + ALOGE("Rejecting buffer request from object that lost ownership of the display."); + BufferDesc_1_0 nullBuff = {}; + _hidl_cb(nullBuff); + return Void(); + } + + // If we don't already have a buffer, allocate one now + if (!mBuffer.memHandle) { + // Allocate the buffer that will hold our displayable image + buffer_handle_t handle = nullptr; + GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); + status_t result = alloc.allocate( + mBuffer.width, mBuffer.height, mBuffer.format, 1, mBuffer.usage, + &handle, &mBuffer.stride, 0, "EvsDisplay"); + if (result != NO_ERROR) { + ALOGE("Error %d allocating %d x %d graphics buffer", + result, mBuffer.width, mBuffer.height); + BufferDesc_1_0 nullBuff = {}; + _hidl_cb(nullBuff); + return Void(); + } + if (!handle) { + ALOGE("We didn't get a buffer handle back from the allocator"); + BufferDesc_1_0 nullBuff = {}; + _hidl_cb(nullBuff); + return Void(); + } + + mBuffer.memHandle = handle; + mFrameBusy = false; + ALOGD("Allocated new buffer %p with stride %u", + mBuffer.memHandle.getNativeHandle(), mBuffer.stride); + } + + // Do we have a frame available? + if (mFrameBusy) { + // This means either we have a 2nd client trying to compete for buffers + // (an unsupported mode of operation) or else the client hasn't returned + // a previously issued buffer yet (they're behaving badly). + // NOTE: We have to make the callback even if we have nothing to provide + ALOGE("getTargetBuffer called while no buffers available."); + BufferDesc_1_0 nullBuff = {}; + _hidl_cb(nullBuff); + return Void(); + } else { + // Mark our buffer as busy + mFrameBusy = true; + + // Send the buffer to the client + ALOGD("Providing display buffer handle %p as id %d", + mBuffer.memHandle.getNativeHandle(), mBuffer.bufferId); + _hidl_cb(mBuffer); + return Void(); + } +} + + +/** + * This call tells the display that the buffer is ready for display. + * The buffer is no longer valid for use by the client after this call. + */ +Return<EvsResult> EvsDisplay::returnTargetBufferForDisplayImpl(const uint32_t bufferId, const buffer_handle_t memHandle) { + ALOGD("returnTargetBufferForDisplay %p", memHandle); + std::lock_guard<std::mutex> lock(mAccessLock); + + // Nobody should call us with a null handle + if (!memHandle) { + ALOGE ("returnTargetBufferForDisplay called without a valid buffer handle.\n"); + return EvsResult::INVALID_ARG; + } + if (bufferId != mBuffer.bufferId) { + ALOGE ("Got an unrecognized frame returned.\n"); + return EvsResult::INVALID_ARG; + } + if (!mFrameBusy) { + ALOGE ("A frame was returned with no outstanding frames.\n"); + return EvsResult::BUFFER_NOT_AVAILABLE; + } + + mFrameBusy = false; + + // If we've been displaced by another owner of the display, then we can't do anything else + if (mRequestedState == DisplayState::DEAD) { + return EvsResult::OWNERSHIP_LOST; + } + + // If we were waiting for a new frame, this is it! + if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) { + mRequestedState = DisplayState::VISIBLE; + } + + // Validate we're in an expected state + if (mRequestedState != DisplayState::VISIBLE) { + // We shouldn't get frames back when we're not visible. + ALOGE ("Got an unexpected frame returned while not visible - ignoring.\n"); + } else { + // This is where the buffer would be made visible. + // For now we simply validate it has the data we expect in it by reading it back + + // Lock our display buffer for reading + uint32_t* pixels = nullptr; + GraphicBufferMapper &mapper = GraphicBufferMapper::get(); + mapper.lock(mBuffer.memHandle, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, + android::Rect(mBuffer.width, mBuffer.height), + (void **)&pixels); + + // If we failed to lock the pixel buffer, we're about to crash, but log it first + if (!pixels) { + ALOGE("Display failed to gain access to image buffer for reading"); + } + + // Check the test pixels + bool frameLooksGood = true; + for (unsigned row = 0; row < mBuffer.height; row++) { + for (unsigned col = 0; col < mBuffer.width; col++) { + // Index into the row to check the pixel at this column. + // We expect 0xFF in the LSB channel, a vertical gradient in the + // second channel, a horitzontal gradient in the third channel, and + // 0xFF in the MSB. + // The exception is the very first 32 bits which is used for the + // time varying frame signature to avoid getting fooled by a static image. + uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB + ((row & 0xFF) << 8) | // vertical gradient + ((col & 0xFF) << 16); // horizontal gradient + if ((row | col) == 0) { + // we'll check the "uniqueness" of the frame signature below + continue; + } + // Walk across this row (we'll step rows below) + uint32_t receivedPixel = pixels[col]; + if (receivedPixel != expectedPixel) { + ALOGE("Pixel check mismatch in frame buffer"); + frameLooksGood = false; + break; + } + } + + if (!frameLooksGood) { + break; + } + + // Point to the next row (NOTE: gralloc reports stride in units of pixels) + pixels = pixels + mBuffer.stride; + } + + // Ensure we don't see the same buffer twice without it being rewritten + static uint32_t prevSignature = ~0; + uint32_t signature = pixels[0] & 0xFF; + if (prevSignature == signature) { + frameLooksGood = false; + ALOGE("Duplicate, likely stale frame buffer detected"); + } + + + // Release our output buffer + mapper.unlock(mBuffer.memHandle); + + if (!frameLooksGood) { + return EvsResult::UNDERLYING_SERVICE_ERROR; + } + } + + return EvsResult::OK; +} + + +Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const BufferDesc_1_0& buffer) { + return returnTargetBufferForDisplayImpl(buffer.bufferId, buffer.memHandle); +} + + +} // namespace implementation +} // namespace V1_1 +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/evs/1.1/default/EvsDisplay.h b/automotive/evs/1.1/default/EvsDisplay.h new file mode 100644 index 0000000000..2a5653500f --- /dev/null +++ b/automotive/evs/1.1/default/EvsDisplay.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H +#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H + +#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h> +#include <ui/GraphicBuffer.h> + +using ::android::hardware::automotive::evs::V1_0::IEvsDisplay; +using ::android::hardware::automotive::evs::V1_0::DisplayDesc; +using ::android::hardware::automotive::evs::V1_0::DisplayState; +using ::android::hardware::automotive::evs::V1_0::EvsResult; +using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc; + +namespace android { +namespace hardware { +namespace automotive { +namespace evs { +namespace V1_1 { +namespace implementation { + + +class EvsDisplay : public IEvsDisplay { +public: + // Methods from ::android::hardware::automotive::evs::V1_0::IEvsDisplay follow. + Return<void> getDisplayInfo(getDisplayInfo_cb _hidl_cb) override; + Return<EvsResult> setDisplayState(DisplayState state) override; + Return<DisplayState> getDisplayState() override; + Return<void> getTargetBuffer(getTargetBuffer_cb _hidl_cb) override; + Return<EvsResult> returnTargetBufferForDisplay(const BufferDesc_1_0& buffer) override; + + + // Implementation details + EvsDisplay(); + virtual ~EvsDisplay() override; + + void forceShutdown(); // This gets called if another caller "steals" ownership of the display + Return<EvsResult> returnTargetBufferForDisplayImpl(const uint32_t bufferId, + const buffer_handle_t memHandle); + +private: + DisplayDesc mInfo = {}; + BufferDesc_1_0 mBuffer = {}; // A graphics buffer into which we'll store images + + bool mFrameBusy = false; // A flag telling us our buffer is in use + DisplayState mRequestedState = DisplayState::NOT_VISIBLE; + + std::mutex mAccessLock; +}; + +} // namespace implementation +} // namespace V1_1 +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H diff --git a/automotive/evs/1.1/default/EvsEnumerator.cpp b/automotive/evs/1.1/default/EvsEnumerator.cpp new file mode 100644 index 0000000000..a010729ce6 --- /dev/null +++ b/automotive/evs/1.1/default/EvsEnumerator.cpp @@ -0,0 +1,308 @@ +/* + * 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.automotive.evs@1.1-service" + +#include "EvsEnumerator.h" +#include "EvsCamera.h" +#include "EvsDisplay.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace evs { +namespace V1_1 { +namespace implementation { + + +// NOTE: All members values are static so that all clients operate on the same state +// That is to say, this is effectively a singleton despite the fact that HIDL +// constructs a new instance for each client. +std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList; +wp<EvsDisplay> EvsEnumerator::sActiveDisplay; +unique_ptr<ConfigManager> EvsEnumerator::sConfigManager; + + +EvsEnumerator::EvsEnumerator() { + ALOGD("EvsEnumerator created"); + + // Add sample camera data to our list of cameras + // In a real driver, this would be expected to can the available hardware + sConfigManager = + ConfigManager::Create("/etc/automotive/evs/evs_sample_configuration.xml"); + for (auto v : sConfigManager->getCameraList()) { + sCameraList.emplace_back(v.c_str()); + } +} + + +// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow. +Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) { + ALOGD("getCameraList"); + + const unsigned numCameras = sCameraList.size(); + + // Build up a packed array of CameraDesc for return + // NOTE: Only has to live until the callback returns + std::vector<CameraDesc_1_0> descriptions; + descriptions.reserve(numCameras); + for (const auto& cam : sCameraList) { + descriptions.push_back( cam.desc.v1 ); + } + + // Encapsulate our camera descriptions in the HIDL vec type + hidl_vec<CameraDesc_1_0> hidlCameras(descriptions); + + // Send back the results + ALOGD("reporting %zu cameras available", hidlCameras.size()); + _hidl_cb(hidlCameras); + + // HIDL convention says we return Void if we sent our result back via callback + return Void(); +} + + +Return<sp<IEvsCamera_1_0>> EvsEnumerator::openCamera(const hidl_string& cameraId) { + ALOGD("openCamera"); + + // Find the named camera + CameraRecord *pRecord = nullptr; + for (auto &&cam : sCameraList) { + if (cam.desc.v1.cameraId == cameraId) { + // Found a match! + pRecord = &cam; + break; + } + } + + // Is this a recognized camera id? + if (!pRecord) { + ALOGE("Requested camera %s not found", cameraId.c_str()); + return nullptr; + } + + // Has this camera already been instantiated by another caller? + sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote(); + if (pActiveCamera != nullptr) { + ALOGW("Killing previous camera because of new caller"); + closeCamera(pActiveCamera); + } + + // Construct a camera instance for the caller + if (sConfigManager == nullptr) { + pActiveCamera = EvsCamera::Create(cameraId.c_str()); + } else { + pActiveCamera = EvsCamera::Create(cameraId.c_str(), + sConfigManager->getCameraInfo(cameraId)); + } + pRecord->activeInstance = pActiveCamera; + if (pActiveCamera == nullptr) { + ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str()); + } + + return pActiveCamera; +} + + +Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera_1_0>& pCamera) { + ALOGD("closeCamera"); + + auto pCamera_1_1 = IEvsCamera_1_1::castFrom(pCamera).withDefault(nullptr); + if (pCamera_1_1 == nullptr) { + ALOGE("Ignoring call to closeCamera with null camera ptr"); + return Void(); + } + + // Get the camera id so we can find it in our list + std::string cameraId; + pCamera_1_1->getCameraInfo_1_1([&cameraId](CameraDesc desc) { + cameraId = desc.v1.cameraId; + } + ); + + // Find the named camera + CameraRecord *pRecord = nullptr; + for (auto &&cam : sCameraList) { + if (cam.desc.v1.cameraId == cameraId) { + // Found a match! + pRecord = &cam; + break; + } + } + + // Is the display being destroyed actually the one we think is active? + if (!pRecord) { + ALOGE("Asked to close a camera who's name isn't recognized"); + } else { + sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote(); + + if (pActiveCamera == nullptr) { + ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed"); + } else if (pActiveCamera != pCamera_1_1) { + // This can happen if the camera was aggressively reopened, orphaning this previous instance + ALOGW("Ignoring close of previously orphaned camera - why did a client steal?"); + } else { + // Drop the active camera + pActiveCamera->forceShutdown(); + pRecord->activeInstance = nullptr; + } + } + + return Void(); +} + + +Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() { + ALOGD("openDisplay"); + + // If we already have a display active, then we need to shut it down so we can + // give exclusive access to the new caller. + sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote(); + if (pActiveDisplay != nullptr) { + ALOGW("Killing previous display because of new caller"); + closeDisplay(pActiveDisplay); + } + + // Create a new display interface and return it + pActiveDisplay = new EvsDisplay(); + sActiveDisplay = pActiveDisplay; + + ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get()); + return pActiveDisplay; +} + + +Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) { + ALOGD("closeDisplay"); + + // Do we still have a display object we think should be active? + sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote(); + if (pActiveDisplay == nullptr) { + ALOGE("Somehow a display is being destroyed when the enumerator didn't know one existed"); + } else if (sActiveDisplay != pDisplay) { + ALOGW("Ignoring close of previously orphaned display - why did a client steal?"); + } else { + // Drop the active display + pActiveDisplay->forceShutdown(); + sActiveDisplay = nullptr; + } + + return Void(); +} + + +Return<DisplayState> EvsEnumerator::getDisplayState() { + ALOGD("getDisplayState"); + + // Do we still have a display object we think should be active? + sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote(); + if (pActiveDisplay != nullptr) { + return pActiveDisplay->getDisplayState(); + } else { + return DisplayState::NOT_OPEN; + } +} + + +// Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow. +Return<void> EvsEnumerator::getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) { + ALOGD("getCameraList"); + + const unsigned numCameras = sCameraList.size(); + + // Build up a packed array of CameraDesc for return + // NOTE: Only has to live until the callback returns + std::vector<CameraDesc_1_1> descriptions; + descriptions.reserve(numCameras); + for (const auto& cam : sCameraList) { + descriptions.push_back( cam.desc ); + } + + // Encapsulate our camera descriptions in the HIDL vec type + hidl_vec<CameraDesc_1_1> hidlCameras(descriptions); + + // Send back the results + ALOGD("reporting %zu cameras available", hidlCameras.size()); + _hidl_cb(hidlCameras); + + // HIDL convention says we return Void if we sent our result back via callback + return Void(); +} + +Return<sp<IEvsCamera_1_1>> +EvsEnumerator::openCamera_1_1(const hidl_string& cameraId, + const Stream& streamCfg) { + // Find the named camera + CameraRecord *pRecord = nullptr; + for (auto &&cam : sCameraList) { + if (cam.desc.v1.cameraId == cameraId) { + // Found a match! + pRecord = &cam; + break; + } + } + + // Is this a recognized camera id? + if (!pRecord) { + ALOGE("Requested camera %s not found", cameraId.c_str()); + return nullptr; + } + + // Has this camera already been instantiated by another caller? + sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote(); + if (pActiveCamera != nullptr) { + ALOGW("Killing previous camera because of new caller"); + closeCamera(pActiveCamera); + } + + // Construct a camera instance for the caller + if (sConfigManager == nullptr) { + pActiveCamera = EvsCamera::Create(cameraId.c_str()); + } else { + pActiveCamera = EvsCamera::Create(cameraId.c_str(), + sConfigManager->getCameraInfo(cameraId), + &streamCfg); + } + + pRecord->activeInstance = pActiveCamera; + if (pActiveCamera == nullptr) { + ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str()); + } + + return pActiveCamera; +} + + +EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) { + // Find the named camera + CameraRecord *pRecord = nullptr; + for (auto &&cam : sCameraList) { + if (cam.desc.v1.cameraId == cameraId) { + // Found a match! + pRecord = &cam; + break; + } + } + + return pRecord; +} + +} // namespace implementation +} // namespace V1_1 +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/evs/1.1/default/EvsEnumerator.h b/automotive/evs/1.1/default/EvsEnumerator.h new file mode 100644 index 0000000000..475ec76b93 --- /dev/null +++ b/automotive/evs/1.1/default/EvsEnumerator.h @@ -0,0 +1,94 @@ +/* + * 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_AUTOMOTIVE_EVS_V1_1_EVSCAMERAENUMERATOR_H +#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERAENUMERATOR_H + +#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h> +#include <android/hardware/automotive/evs/1.1/IEvsCamera.h> + +#include <list> + +#include "ConfigManager.h" + +using ::android::hardware::automotive::evs::V1_0::EvsResult; +using ::android::hardware::automotive::evs::V1_0::IEvsDisplay; +using ::android::hardware::automotive::evs::V1_0::DisplayState; +using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera; +using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera; +using CameraDesc_1_0 = ::android::hardware::automotive::evs::V1_0::CameraDesc; +using CameraDesc_1_1 = ::android::hardware::automotive::evs::V1_1::CameraDesc; + + +namespace android { +namespace hardware { +namespace automotive { +namespace evs { +namespace V1_1 { +namespace implementation { + + +class EvsCamera; // from EvsCamera.h +class EvsDisplay; // from EvsDisplay.h + + +class EvsEnumerator : public IEvsEnumerator { +public: + // Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow. + Return<void> getCameraList(getCameraList_cb _hidl_cb) override; + Return<sp<IEvsCamera_1_0>> openCamera(const hidl_string& cameraId) override; + Return<void> closeCamera(const ::android::sp<IEvsCamera_1_0>& carCamera) override; + Return<sp<IEvsDisplay>> openDisplay() override; + Return<void> closeDisplay(const ::android::sp<IEvsDisplay>& display) override; + Return<DisplayState> getDisplayState() override; + + // Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow. + Return<void> getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) override; + Return<sp<IEvsCamera_1_1>> openCamera_1_1(const hidl_string& cameraId, + const Stream& streamCfg) override; + + // Implementation details + EvsEnumerator(); + +private: + // NOTE: All members values are static so that all clients operate on the same state + // That is to say, this is effectively a singleton despite the fact that HIDL + // constructs a new instance for each client. + struct CameraRecord { + CameraDesc_1_1 desc; + wp<EvsCamera> activeInstance; + + CameraRecord(const char *cameraId) : desc() { desc.v1.cameraId = cameraId; } + }; + + static CameraRecord* findCameraById(const std::string& cameraId); + + static std::list<CameraRecord> sCameraList; + + // Weak pointer. Object destructs if client dies. + static wp<EvsDisplay> sActiveDisplay; + + static unique_ptr<ConfigManager> sConfigManager; +}; + +} // namespace implementation +} // namespace V1_1 +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERAENUMERATOR_H diff --git a/automotive/evs/1.1/default/ServiceNames.h b/automotive/evs/1.1/default/ServiceNames.h new file mode 100644 index 0000000000..1178da5a9c --- /dev/null +++ b/automotive/evs/1.1/default/ServiceNames.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const static char kEnumeratorServiceName[] = "EvsEnumeratorHw"; diff --git a/automotive/evs/1.1/default/android.hardware.automotive.evs@1.1-service.rc b/automotive/evs/1.1/default/android.hardware.automotive.evs@1.1-service.rc new file mode 100644 index 0000000000..284b3fda4e --- /dev/null +++ b/automotive/evs/1.1/default/android.hardware.automotive.evs@1.1-service.rc @@ -0,0 +1,5 @@ +service vendor.evs-hal-mock /vendor/bin/hw/android.hardware.automotive.evs@1.1-service + class hal + user automotive_evs + group automotive_evs + disabled diff --git a/automotive/evs/1.1/default/resources/evs_default_configuration.xml b/automotive/evs/1.1/default/resources/evs_default_configuration.xml new file mode 100644 index 0000000000..692102ed38 --- /dev/null +++ b/automotive/evs/1.1/default/resources/evs_default_configuration.xml @@ -0,0 +1,68 @@ +<?xml version='1.0' encoding='utf-8'?> +<!-- 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. +--> + +<!-- Exterior View System Example Configuration + + Android Automotive axes are used to define coordinates. + See https://source.android.com/devices/sensors/sensor-types#auto_axes + + Use evs_configuration.dtd with xmllint tool, to validate XML configuration file +--> + +<configuration> + <!-- system configuration --> + <system> + <!-- number of cameras available to EVS --> + <num_cameras value='1'/> + </system> + + <!-- camera device information --> + <camera> + <!-- camera device starts --> + <device id='/dev/video1' position='rear'> + <caps> + <!-- list of supported controls --> + <supported_controls> + <control name='BRIGHTNESS' min='0' max='255'/> + <control name='CONTRAST' min='0' max='255'/> + </supported_controls> + + <stream id='0' width='640' height='360' format='RGBA_8888' framerate='30'/> + </caps> + + <!-- list of parameters --> + <characteristics> + <!-- Camera intrinsic calibration matrix. See + https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#LENS_INTRINSIC_CALIBRATION + --> + <parameter + name='LENS_INTRINSIC_CALIBRATION' + type='float' + size='5' + value='0.0,0.0,0.0,0.0,0.0' + /> + </characteristics> + </device> + </camera> + <display> + <device id='display0' position='driver'> + <caps> + <!-- list of supported inpu stream configurations --> + <stream id='0' width='1280' height='720' format='RGBA_8888' framerate='30'/> + </caps> + </device> + </display> +</configuration> + diff --git a/automotive/evs/1.1/default/service.cpp b/automotive/evs/1.1/default/service.cpp new file mode 100644 index 0000000000..5135864e88 --- /dev/null +++ b/automotive/evs/1.1/default/service.cpp @@ -0,0 +1,63 @@ +/* + * 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.automotive.evs@1.1-service" + +#include <unistd.h> + +#include <hidl/HidlTransportSupport.h> +#include <log/log.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include "ServiceNames.h" +#include "EvsEnumerator.h" +#include "EvsDisplay.h" + + +// libhidl: +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; + +// Generated HIDL files +using android::hardware::automotive::evs::V1_1::IEvsEnumerator; +using android::hardware::automotive::evs::V1_0::IEvsDisplay; + +// The namespace in which all our implementation code lives +using namespace android::hardware::automotive::evs::V1_1::implementation; +using namespace android; + + +int main() { + ALOGI("EVS Hardware Enumerator service is starting"); + android::sp<IEvsEnumerator> service = new EvsEnumerator(); + + configureRpcThreadpool(1, true /* callerWillJoin */); + + // Register our service -- if somebody is already registered by our name, + // they will be killed (their thread pool will throw an exception). + status_t status = service->registerAsService(kEnumeratorServiceName); + if (status == OK) { + ALOGD("%s is ready.", kEnumeratorServiceName); + joinRpcThreadpool(); + } else { + ALOGE("Could not register service %s (%d).", kEnumeratorServiceName, status); + } + + // In normal operation, we don't expect the thread pool to exit + ALOGE("EVS Hardware Enumerator is shutting down"); + return 1; +} diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal new file mode 100644 index 0000000000..dcb2abb0e9 --- /dev/null +++ b/automotive/evs/1.1/types.hal @@ -0,0 +1,165 @@ +/* + * 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.evs@1.1; + +import @1.0::CameraDesc; +import @1.0::DisplayDesc; +import @1.0::DisplayState; +import @1.0::EvsResult; +import android.hardware.graphics.common@1.2::HardwareBuffer; +import android.hardware.camera.device@3.2::CameraMetadata; + +/** + * Structure describing the basic properties of an EVS camera, extended from its + * v1.0 declaration. + * + * The HAL is responsible for filling out this structure for each + * EVS camera in the system. + */ +struct CameraDesc { + @1.0::CameraDesc v1; + /** + * Store camera metadata such as lens characteristics. + */ + CameraMetadata metadata; +}; + +/** + * Structure representing an image buffer through our APIs + * + * In addition to the handle to the graphics memory, we need to retain + * the properties of the buffer for easy reference and reconstruction of + * an ANativeWindowBuffer object on the remote side of API calls. + * (Not least because OpenGL expect an ANativeWindowBuffer* for us as a + * texture via eglCreateImageKHR(). + */ +struct BufferDesc { + /** + * HIDL counterpart of `AHardwareBuffer_Desc`. Please see + * hardware/interfaces/graphics/common/1.2/types.hal for more details. + */ + HardwareBuffer buffer; + /** + * The size of a pixel in the units of bytes + */ + uint32_t pixelSize; + /** + * Opaque value from driver + */ + uint32_t bufferId; +}; + +/** + * Types of informative streaming events + */ +enum EvsEventType : uint32_t { + /** + * Video stream is started + */ + STREAM_STARTED = 0, + /** + * Video stream is stopped + */ + STREAM_STOPPED, + /** + * Video frame is dropped + */ + FRAME_DROPPED, + /** + * 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 EvsEvent { + /** + * Type of an informative event + */ + EvsEventType aType; + /** + * Possible additional information + */ + uint32_t[4] payload; +}; + +/** + * 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, + /** + * 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, + /** + * 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/Android.bp b/automotive/evs/1.1/vts/functional/Android.bp new file mode 100644 index 0000000000..4753933f7f --- /dev/null +++ b/automotive/evs/1.1/vts/functional/Android.bp @@ -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. +// + +cc_test { + name: "VtsHalEvsV1_1TargetTest", + srcs: [ + "FrameHandler.cpp", + "VtsHalEvsV1_1TargetTest.cpp", + ], + defaults: ["VtsHalTargetTestDefaults"], + shared_libs: [ + "libui", + "libcamera_metadata", + ], + static_libs: [ + "android.hardware.automotive.evs@1.0", + "android.hardware.automotive.evs@1.1", + "android.hardware.automotive.evs@common-default-lib", + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hardware.camera.device@3.2", + ], + test_suites: ["general-tests"], + cflags: [ + "-O0", + "-g", + ], +} diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.cpp b/automotive/evs/1.1/vts/functional/FrameHandler.cpp new file mode 100644 index 0000000000..6d53652f86 --- /dev/null +++ b/automotive/evs/1.1/vts/functional/FrameHandler.cpp @@ -0,0 +1,384 @@ +/* + * 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 "VtsHalEvsTest" + +#include "FrameHandler.h" +#include "FormatConvert.h" + +#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) : + mCamera(pCamera), + mCameraInfo(cameraInfo), + mDisplay(pDisplay), + mReturnMode(mode) { + // Nothing but member initialization here... +} + + +void FrameHandler::shutdown() +{ + // Make sure we're not still streaming + blockingStopStream(); + + // At this point, the receiver thread is no longer running, so we can safely drop + // our remote object references so they can be freed + mCamera = nullptr; + mDisplay = nullptr; +} + + +bool FrameHandler::startStream() { + // Tell the camera to start streaming + Return<EvsResult> result = mCamera->startVideoStream(this); + if (result != EvsResult::OK) { + return false; + } + + // Mark ourselves as running + mLock.lock(); + mRunning = true; + mLock.unlock(); + + return true; +} + + +void FrameHandler::asyncStopStream() { + // Tell the camera to stop streaming. + // This will result in a null frame being delivered when the stream actually stops. + mCamera->stopVideoStream(); +} + + +void FrameHandler::blockingStopStream() { + // Tell the stream to stop + asyncStopStream(); + + // Wait until the stream has actually stopped + std::unique_lock<std::mutex> lock(mLock); + if (mRunning) { + mEventSignal.wait(lock, [this]() { return !mRunning; }); + } +} + + +bool FrameHandler::returnHeldBuffer() { + std::unique_lock<std::mutex> lock(mLock); + + // Return the oldest buffer we're holding + if (mHeldBuffers.empty()) { + // No buffers are currently held + return false; + } + + BufferDesc_1_1 buffer = mHeldBuffers.front(); + mHeldBuffers.pop(); + mCamera->doneWithFrame_1_1(buffer); + + return true; +} + + +bool FrameHandler::isRunning() { + std::unique_lock<std::mutex> lock(mLock); + return mRunning; +} + + +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); + mFrameSignal.wait(lock, [this, frameCount](){ + return mFramesReceived >= frameCount; + }); +} + + +void FrameHandler::getFramesCounters(unsigned* received, unsigned* displayed) { + std::unique_lock<std::mutex> lock(mLock); + + if (received) { + *received = mFramesReceived; + } + if (displayed) { + *displayed = mFramesDisplayed; + } +} + + +Return<void> FrameHandler::deliverFrame(const BufferDesc_1_0& bufferArg) { + ALOGW("A frame delivered via v1.0 method is rejected."); + mCamera->doneWithFrame(bufferArg); + return Void(); +} + + +Return<void> FrameHandler::deliverFrame_1_1(const BufferDesc_1_1& bufDesc) { + const AHardwareBuffer_Desc* pDesc = + reinterpret_cast<const AHardwareBuffer_Desc *>(&bufDesc.buffer.description); + ALOGD("Received a frame from the camera (%p)", + bufDesc.buffer.nativeHandle.getNativeHandle()); + + // Store a dimension of a received frame. + mFrameWidth = pDesc->width; + mFrameHeight = pDesc->height; + + // If we were given an opened display at construction time, then send the received + // image back down the camera. + if (mDisplay.get()) { + // Get the output buffer we'll use to display the imagery + BufferDesc_1_0 tgtBuffer = {}; + mDisplay->getTargetBuffer([&tgtBuffer](const BufferDesc_1_0& buff) { + tgtBuffer = buff; + } + ); + + if (tgtBuffer.memHandle == nullptr) { + printf("Didn't get target buffer - frame lost\n"); + ALOGE("Didn't get requested output buffer -- skipping this frame."); + } else { + // Copy the contents of the of buffer.memHandle into tgtBuffer + copyBufferContents(tgtBuffer, bufDesc); + + // Send the target buffer back for display + Return<EvsResult> result = mDisplay->returnTargetBufferForDisplay(tgtBuffer); + if (!result.isOk()) { + printf("HIDL error on display buffer (%s)- frame lost\n", + result.description().c_str()); + ALOGE("Error making the remote function call. HIDL said %s", + result.description().c_str()); + } else if (result != EvsResult::OK) { + printf("Display reported error - frame lost\n"); + ALOGE("We encountered error %d when returning a buffer to the display!", + (EvsResult) result); + } else { + // Everything looks good! + // Keep track so tests or watch dogs can monitor progress + mLock.lock(); + mFramesDisplayed++; + mLock.unlock(); + } + } + } + + + switch (mReturnMode) { + case eAutoReturn: + // Send the camera buffer back now that the client has seen it + ALOGD("Calling doneWithFrame"); + mCamera->doneWithFrame_1_1(bufDesc); + break; + case eNoAutoReturn: + // Hang onto the buffer handle for now -- the client will return it explicitly later + mHeldBuffers.push(bufDesc); + } + + mLock.lock(); + ++mFramesReceived; + mLock.unlock(); + mFrameSignal.notify_all(); + + ALOGD("Frame handling complete"); + + return Void(); +} + + +Return<void> FrameHandler::notify(const EvsEvent& event) { + // Local flag we use to keep track of when the stream is stopping + mLock.lock(); + mLatestEventDesc = event; + if (mLatestEventDesc.aType == EvsEventType::STREAM_STOPPED) { + // Signal that the last frame has been received and the stream is stopped + mRunning = false; + } else if (mLatestEventDesc.aType == EvsEventType::PARAMETER_CHANGED) { + ALOGD("Camera parameter 0x%X is changed to 0x%X", + mLatestEventDesc.payload[0], mLatestEventDesc.payload[1]); + } else { + ALOGD("Received an event %s", eventToString(mLatestEventDesc.aType)); + } + mLock.unlock(); + mEventSignal.notify_all(); + + return Void(); +} + + +bool FrameHandler::copyBufferContents(const BufferDesc_1_0& tgtBuffer, + const BufferDesc_1_1& srcBuffer) { + bool success = true; + const AHardwareBuffer_Desc* pSrcDesc = + reinterpret_cast<const AHardwareBuffer_Desc *>(&srcBuffer.buffer.description); + + // Make sure we don't run off the end of either buffer + const unsigned width = std::min(tgtBuffer.width, + pSrcDesc->width); + const unsigned height = std::min(tgtBuffer.height, + pSrcDesc->height); + + sp<android::GraphicBuffer> tgt = new android::GraphicBuffer(tgtBuffer.memHandle, + android::GraphicBuffer::CLONE_HANDLE, + tgtBuffer.width, + tgtBuffer.height, + tgtBuffer.format, + 1, + tgtBuffer.usage, + tgtBuffer.stride); + sp<android::GraphicBuffer> src = new android::GraphicBuffer(srcBuffer.buffer.nativeHandle, + android::GraphicBuffer::CLONE_HANDLE, + pSrcDesc->width, + pSrcDesc->height, + pSrcDesc->format, + pSrcDesc->layers, + pSrcDesc->usage, + pSrcDesc->stride); + + // Lock our source buffer for reading (current expectation are for this to be NV21 format) + uint8_t* srcPixels = nullptr; + src->lock(GRALLOC_USAGE_SW_READ_OFTEN, (void**)&srcPixels); + + // Lock our target buffer for writing (should be either RGBA8888 or BGRA8888 format) + uint32_t* tgtPixels = nullptr; + tgt->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)&tgtPixels); + + if (srcPixels && tgtPixels) { + using namespace ::android::hardware::automotive::evs::common; + if (tgtBuffer.format == HAL_PIXEL_FORMAT_RGBA_8888) { + if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21 + Utils::copyNV21toRGB32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); + } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12 + Utils::copyYV12toRGB32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); + } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV + Utils::copyYUYVtoRGB32(width, height, + srcPixels, pSrcDesc->stride, + tgtPixels, tgtBuffer.stride); + } else if (pSrcDesc->format == tgtBuffer.format) { // 32bit RGBA + Utils::copyMatchedInterleavedFormats(width, height, + srcPixels, pSrcDesc->stride, + tgtPixels, tgtBuffer.stride, + tgtBuffer.pixelSize); + } else { + ALOGE("Camera buffer format is not supported"); + success = false; + } + } else if (tgtBuffer.format == HAL_PIXEL_FORMAT_BGRA_8888) { + if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21 + Utils::copyNV21toBGR32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); + } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12 + Utils::copyYV12toBGR32(width, height, + srcPixels, + tgtPixels, tgtBuffer.stride); + } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV + Utils::copyYUYVtoBGR32(width, height, + srcPixels, pSrcDesc->stride, + tgtPixels, tgtBuffer.stride); + } else if (pSrcDesc->format == tgtBuffer.format) { // 32bit RGBA + Utils::copyMatchedInterleavedFormats(width, height, + srcPixels, pSrcDesc->stride, + tgtPixels, tgtBuffer.stride, + tgtBuffer.pixelSize); + } else { + ALOGE("Camera buffer format is not supported"); + success = false; + } + } else { + // We always expect 32 bit RGB for the display output for now. Is there a need for 565? + ALOGE("Diplay buffer is always expected to be 32bit RGBA"); + success = false; + } + } else { + ALOGE("Failed to lock buffer contents for contents transfer"); + success = false; + } + + if (srcPixels) { + src->unlock(); + } + if (tgtPixels) { + tgt->unlock(); + } + + return success; +} + +void FrameHandler::getFrameDimension(unsigned* width, unsigned* height) { + if (width) { + *width = mFrameWidth; + } + + if (height) { + *height = mFrameHeight; + } +} + +bool FrameHandler::waitForEvent(const EvsEventType aTargetEvent, + EvsEvent &event) { + // 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, &event](){ + bool flag = mLatestEventDesc.aType == aTargetEvent; + if (flag) { + event.aType = mLatestEventDesc.aType; + event.payload[0] = mLatestEventDesc.payload[0]; + event.payload[1] = mLatestEventDesc.payload[1]; + } + + return flag; + } + ); + + return !result; +} + +const char *FrameHandler::eventToString(const EvsEventType aType) { + switch (aType) { + case EvsEventType::STREAM_STARTED: + return "STREAM_STARTED"; + case EvsEventType::STREAM_STOPPED: + return "STREAM_STOPPED"; + case EvsEventType::FRAME_DROPPED: + return "FRAME_DROPPED"; + case EvsEventType::TIMEOUT: + return "TIMEOUT"; + case EvsEventType::PARAMETER_CHANGED: + return "PARAMETER_CHANGED"; + case EvsEventType::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 new file mode 100644 index 0000000000..e5f1b8f112 --- /dev/null +++ b/automotive/evs/1.1/vts/functional/FrameHandler.h @@ -0,0 +1,116 @@ +/* + * 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 EVS_VTS_FRAMEHANDLER_H +#define EVS_VTS_FRAMEHANDLER_H + +#include <queue> + +#include <FrameHandler.h> + +#include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h> +#include <android/hardware/automotive/evs/1.1/IEvsCamera.h> +#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h> + +using namespace ::android::hardware::automotive::evs::V1_1; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_handle; +using ::android::sp; +using ::android::hardware::automotive::evs::V1_0::IEvsDisplay; +using ::android::hardware::automotive::evs::V1_0::EvsResult; +using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc; +using BufferDesc_1_1 = ::android::hardware::automotive::evs::V1_1::BufferDesc; + + +/* + * FrameHandler: + * This class can be used to receive camera imagery from an IEvsCamera implementation. Given an + * IEvsDisplay instance at startup, it will forward the received imagery to the display, + * providing a trivial implementation of a rear vew camera type application. + * Note that the video frames are delivered on a background thread, while the control interface + * is actuated from the applications foreground thread. + */ +class FrameHandler : public IEvsCameraStream { +public: + enum BufferControlFlag { + eAutoReturn, + eNoAutoReturn, + }; + + FrameHandler(android::sp <IEvsCamera> pCamera, CameraDesc cameraInfo, + android::sp <IEvsDisplay> pDisplay = nullptr, + BufferControlFlag mode = eAutoReturn); + virtual ~FrameHandler() { + if (mCamera != nullptr) { + /* shutdown a camera explicitly */ + shutdown(); + } + } + + void shutdown(); + + bool startStream(); + void asyncStopStream(); + void blockingStopStream(); + + bool returnHeldBuffer(); + + bool isRunning(); + + void waitForFrameCount(unsigned frameCount); + bool waitForEvent(const EvsEventType aTargetEvent, + EvsEvent &eventDesc); + void getFramesCounters(unsigned* received, unsigned* displayed); + void getFrameDimension(unsigned* width, unsigned* height); + +private: + // Implementation for ::android::hardware::automotive::evs::V1_0::IEvsCameraStream + Return<void> deliverFrame(const BufferDesc_1_0& buffer) override; + + // Implementation for ::android::hardware::automotive::evs::V1_1::IEvsCameraStream + Return<void> deliverFrame_1_1(const BufferDesc_1_1& buffer) override; + Return<void> notify(const EvsEvent& event) override; + + // Local implementation details + bool copyBufferContents(const BufferDesc_1_0& tgtBuffer, const BufferDesc_1_1& srcBuffer); + const char *eventToString(const EvsEventType aType); + + // Values initialized as startup + android::sp <IEvsCamera> mCamera; + CameraDesc mCameraInfo; + android::sp <IEvsDisplay> mDisplay; + BufferControlFlag mReturnMode; + + // Since we get frames delivered to us asynchronously via the IEvsCameraStream interface, + // we need to protect all member variables that may be modified while we're streaming + // (ie: those below) + std::mutex mLock; + std::condition_variable mEventSignal; + std::condition_variable mFrameSignal; + + std::queue<BufferDesc_1_1> mHeldBuffers; + bool mRunning = false; + unsigned mFramesReceived = 0; // Simple counter -- rolls over eventually! + unsigned mFramesDisplayed = 0; // Simple counter -- rolls over eventually! + unsigned mFrameWidth = 0; + unsigned mFrameHeight = 0; + EvsEvent mLatestEventDesc; +}; + + +#endif //EVS_VTS_FRAMEHANDLER_H diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp new file mode 100644 index 0000000000..1d3fd87356 --- /dev/null +++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp @@ -0,0 +1,1516 @@ +/* + * 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 "VtsHalEvsTest" + + +// Note: We have't got a great way to indicate which target +// should be tested, so we'll leave the interface served by the +// default (mock) EVS driver here for easy reference. All +// actual EVS drivers should serve on the EvsEnumeratorHw name, +// however, so the code is checked in that way. +//const static char kEnumeratorName[] = "EvsEnumeratorHw-Mock"; +const static char kEnumeratorName[] = "EvsEnumeratorHw"; + + +// These values are called out in the EVS design doc (as of Mar 8, 2017) +static const int kMaxStreamStartMilliseconds = 500; +static const int kMinimumFramesPerSecond = 10; + +static const int kSecondsToMilliseconds = 1000; +static const int kMillisecondsToMicroseconds = 1000; +static const float kNanoToMilliseconds = 0.000001f; +static const float kNanoToSeconds = 0.000000001f; + + +#include "FrameHandler.h" + +#include <cstdio> +#include <cstring> +#include <cstdlib> + +#include <hidl/HidlTransportSupport.h> +#include <hwbinder/ProcessState.h> +#include <log/log.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include <android/log.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.1/IEvsEnumerator.h> +#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h> +#include <android/hardware/camera/device/3.2/ICameraDevice.h> +#include <system/camera_metadata.h> + +#include <VtsHalHidlTargetTestBase.h> +#include <VtsHalHidlTargetTestEnvBase.h> + +using namespace ::android::hardware::automotive::evs::V1_1; + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; +using ::android::sp; +using ::android::hardware::camera::device::V3_2::Stream; +using ::android::hardware::automotive::evs::V1_0::DisplayDesc; +using ::android::hardware::automotive::evs::V1_0::DisplayState; +using ::android::hardware::graphics::common::V1_0::PixelFormat; +using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera; +using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera; + +/* + * Plese note that this is different from what is defined in + * libhardware/modules/camera/3_4/metadata/types.h; this has one additional + * field to store a framerate. + */ +const size_t kStreamCfgSz = 5; +typedef struct { + int32_t width; + int32_t height; + int32_t format; + int32_t direction; + int32_t framerate; +} RawStreamConfig; + + +// Test environment for Evs HIDL HAL. +class EvsHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { + public: + // get the test environment singleton + static EvsHidlEnvironment* Instance() { + static EvsHidlEnvironment* instance = new EvsHidlEnvironment; + return instance; + } + + virtual void registerTestServices() override { registerTestService<IEvsEnumerator>(); } + + private: + EvsHidlEnvironment() {} +}; + +// The main test class for EVS +class EvsHidlTest : public ::testing::VtsHalHidlTargetTestBase { +public: + virtual void SetUp() override { + // Make sure we can connect to the enumerator + string service_name = + EvsHidlEnvironment::Instance()->getServiceName<IEvsEnumerator>(kEnumeratorName); + pEnumerator = getService<IEvsEnumerator>(service_name); + ASSERT_NE(pEnumerator.get(), nullptr); + + mIsHwModule = !service_name.compare(kEnumeratorName); + } + + virtual void TearDown() override {} + +protected: + void loadCameraList() { + // SetUp() must run first! + assert(pEnumerator != nullptr); + + // Get the camera list + pEnumerator->getCameraList_1_1( + [this](hidl_vec <CameraDesc> cameraList) { + ALOGI("Camera list callback received %zu cameras", + cameraList.size()); + cameraInfo.reserve(cameraList.size()); + for (auto&& cam: cameraList) { + ALOGI("Found camera %s", cam.v1.cameraId.c_str()); + cameraInfo.push_back(cam); + } + } + ); + + // We insist on at least one camera for EVS to pass any camera tests + ASSERT_GE(cameraInfo.size(), 1u); + } + + 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. +}; + + +// Test cases, their implementations, and corresponding requirements are +// documented at go/aae-evs-public-api-test. + +/* + * CameraOpenClean: + * Opens each camera reported by the enumerator and then explicitly closes it via a + * call to closeCamera. Then repeats the test to ensure all cameras can be reopened. + */ +TEST_F(EvsHidlTest, CameraOpenClean) { + ALOGI("Starting CameraOpenClean test"); + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Open and close each camera twice + for (auto&& cam: cameraInfo) { + for (int pass = 0; pass < 2; pass++) { + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Verify that this camera self-identifies correctly + pCam->getCameraInfo_1_1([&cam](CameraDesc desc) { + ALOGD("Found camera %s", desc.v1.cameraId.c_str()); + EXPECT_EQ(cam.v1.cameraId, desc.v1.cameraId); + } + ); + + // Explicitly close the camera so resources are released right away + pEnumerator->closeCamera(pCam); + } + } +} + + +/* + * CameraOpenAggressive: + * Opens each camera reported by the enumerator twice in a row without an intervening closeCamera + * call. This ensures that the intended "aggressive open" behavior works. This is necessary for + * the system to be tolerant of shutdown/restart race conditions. + */ +TEST_F(EvsHidlTest, CameraOpenAggressive) { + ALOGI("Starting CameraOpenAggressive test"); + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Open and close each camera twice + for (auto&& cam: cameraInfo) { + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Verify that this camera self-identifies correctly + pCam->getCameraInfo_1_1([&cam](CameraDesc desc) { + ALOGD("Found camera %s", desc.v1.cameraId.c_str()); + EXPECT_EQ(cam.v1.cameraId, desc.v1.cameraId); + } + ); + + sp<IEvsCamera_1_1> pCam2 = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, pCam2); + ASSERT_NE(pCam2, nullptr); + + Return<EvsResult> result = pCam->setMaxFramesInFlight(2); + if (mIsHwModule) { + // Verify that the old camera rejects calls via HW module. + EXPECT_EQ(EvsResult::OWNERSHIP_LOST, EvsResult(result)); + } else { + // default implementation supports multiple clients. + EXPECT_EQ(EvsResult::OK, EvsResult(result)); + } + + // Close the superceded camera + pEnumerator->closeCamera(pCam); + + // Verify that the second camera instance self-identifies correctly + pCam2->getCameraInfo_1_1([&cam](CameraDesc desc) { + ALOGD("Found camera %s", desc.v1.cameraId.c_str()); + EXPECT_EQ(cam.v1.cameraId, desc.v1.cameraId); + } + ); + + // Close the second camera instance + pEnumerator->closeCamera(pCam2); + } + + // Sleep here to ensure the destructor cleanup has time to run so we don't break follow on tests + sleep(1); // I hate that this is an arbitrary time to wait. :( b/36122635 +} + + +/* + * CameraStreamPerformance: + * Measure and qualify the stream start up time and streaming frame rate of each reported camera + */ +TEST_F(EvsHidlTest, CameraStreamPerformance) { + ALOGI("Starting CameraStreamPerformance test"); + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Test each reported camera + for (auto&& cam: cameraInfo) { + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Set up a frame receiver object which will fire up its own thread + sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam, + nullptr, + FrameHandler::eAutoReturn); + + // Start the camera's video stream + nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); + bool startResult = frameHandler->startStream(); + ASSERT_TRUE(startResult); + + // Ensure the first frame arrived within the expected time + frameHandler->waitForFrameCount(1); + nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t timeToFirstFrame = systemTime(SYSTEM_TIME_MONOTONIC) - start; + EXPECT_LE(nanoseconds_to_milliseconds(timeToFirstFrame), kMaxStreamStartMilliseconds); + printf("Measured time to first frame %0.2f ms\n", timeToFirstFrame * kNanoToMilliseconds); + ALOGI("Measured time to first frame %0.2f ms", timeToFirstFrame * kNanoToMilliseconds); + + // Check aspect ratio + unsigned width = 0, height = 0; + frameHandler->getFrameDimension(&width, &height); + EXPECT_GE(width, height); + + // Wait a bit, then ensure we get at least the required minimum number of frames + sleep(5); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); + unsigned framesReceived = 0; + frameHandler->getFramesCounters(&framesReceived, nullptr); + framesReceived = framesReceived - 1; // Back out the first frame we already waited for + nsecs_t runTime = end - firstFrame; + float framesPerSecond = framesReceived / (runTime * kNanoToSeconds); + printf("Measured camera rate %3.2f fps\n", framesPerSecond); + ALOGI("Measured camera rate %3.2f fps", framesPerSecond); + EXPECT_GE(framesPerSecond, kMinimumFramesPerSecond); + + // Even when the camera pointer goes out of scope, the FrameHandler object will + // keep the stream alive unless we tell it to shutdown. + // Also note that the FrameHandle and the Camera have a mutual circular reference, so + // we have to break that cycle in order for either of them to get cleaned up. + frameHandler->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCam); + } +} + + +/* + * CameraStreamBuffering: + * Ensure the camera implementation behaves properly when the client holds onto buffers for more + * than one frame time. The camera must cleanly skip frames until the client is ready again. + */ +TEST_F(EvsHidlTest, CameraStreamBuffering) { + ALOGI("Starting CameraStreamBuffering test"); + + // Arbitrary constant (should be > 1 and less than crazy) + static const unsigned int kBuffersToHold = 6; + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Test each reported camera + for (auto&& cam: cameraInfo) { + + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Ask for a crazy number of buffers in flight to ensure it errors correctly + Return<EvsResult> badResult = pCam->setMaxFramesInFlight(0xFFFFFFFF); + EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult); + + // Now ask for exactly two buffers in flight as we'll test behavior in that case + Return<EvsResult> goodResult = pCam->setMaxFramesInFlight(kBuffersToHold); + EXPECT_EQ(EvsResult::OK, goodResult); + + + // Set up a frame receiver object which will fire up its own thread. + sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam, + nullptr, + FrameHandler::eNoAutoReturn); + + // Start the camera's video stream + bool startResult = frameHandler->startStream(); + ASSERT_TRUE(startResult); + + // Check that the video stream stalls once we've gotten exactly the number of buffers + // we requested since we told the frameHandler not to return them. + sleep(2); // 1 second should be enough for at least 5 frames to be delivered worst case + unsigned framesReceived = 0; + frameHandler->getFramesCounters(&framesReceived, nullptr); + ASSERT_EQ(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit"; + + + // Give back one buffer + bool didReturnBuffer = frameHandler->returnHeldBuffer(); + EXPECT_TRUE(didReturnBuffer); + + // Once we return a buffer, it shouldn't take more than 1/10 second to get a new one + // filled since we require 10fps minimum -- but give a 10% allowance just in case. + usleep(110 * kMillisecondsToMicroseconds); + frameHandler->getFramesCounters(&framesReceived, nullptr); + EXPECT_EQ(kBuffersToHold+1, framesReceived) << "Stream should've resumed"; + + // Even when the camera pointer goes out of scope, the FrameHandler object will + // keep the stream alive unless we tell it to shutdown. + // Also note that the FrameHandle and the Camera have a mutual circular reference, so + // we have to break that cycle in order for either of them to get cleaned up. + frameHandler->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCam); + } +} + + +/* + * CameraToDisplayRoundTrip: + * End to end test of data flowing from the camera to the display. Each delivered frame of camera + * imagery is simply copied to the display buffer and presented on screen. This is the one test + * which a human could observe to see the operation of the system on the physical display. + */ +TEST_F(EvsHidlTest, CameraToDisplayRoundTrip) { + ALOGI("Starting CameraToDisplayRoundTrip test"); + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Request exclusive access to the EVS display + sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay(); + ASSERT_NE(pDisplay, nullptr); + + // Test each reported camera + for (auto&& cam: cameraInfo) { + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Set up a frame receiver object which will fire up its own thread. + sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam, + pDisplay, + FrameHandler::eAutoReturn); + + + // Activate the display + pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME); + + // Start the camera's video stream + bool startResult = frameHandler->startStream(); + ASSERT_TRUE(startResult); + + // Wait a while to let the data flow + static const int kSecondsToWait = 5; + const int streamTimeMs = kSecondsToWait * kSecondsToMilliseconds - + kMaxStreamStartMilliseconds; + const unsigned minimumFramesExpected = streamTimeMs * kMinimumFramesPerSecond / + kSecondsToMilliseconds; + sleep(kSecondsToWait); + unsigned framesReceived = 0; + unsigned framesDisplayed = 0; + frameHandler->getFramesCounters(&framesReceived, &framesDisplayed); + EXPECT_EQ(framesReceived, framesDisplayed); + EXPECT_GE(framesDisplayed, minimumFramesExpected); + + // Turn off the display (yes, before the stream stops -- it should be handled) + pDisplay->setDisplayState(DisplayState::NOT_VISIBLE); + + // Shut down the streamer + frameHandler->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCam); + } + + // Explicitly release the display + pEnumerator->closeDisplay(pDisplay); +} + + +/* + * MultiCameraStream: + * Verify that each client can start and stop video streams on the same + * underlying camera. + */ +TEST_F(EvsHidlTest, MultiCameraStream) { + ALOGI("Starting MultiCameraStream test"); + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Test each reported camera + for (auto&& cam: cameraInfo) { + // Create two camera clients. + sp<IEvsCamera_1_1> pCam0 = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam0, nullptr); + + sp<IEvsCamera_1_1> pCam1 = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam1, nullptr); + + // Set up per-client frame receiver objects which will fire up its own thread + sp<FrameHandler> frameHandler0 = new FrameHandler(pCam0, cam, + nullptr, + FrameHandler::eAutoReturn); + ASSERT_NE(frameHandler0, nullptr); + + sp<FrameHandler> frameHandler1 = new FrameHandler(pCam1, cam, + nullptr, + FrameHandler::eAutoReturn); + ASSERT_NE(frameHandler1, nullptr); + + // Start the camera's video stream via client 0 + bool startResult = false; + startResult = frameHandler0->startStream() && + frameHandler1->startStream(); + ASSERT_TRUE(startResult); + + // Ensure the stream starts + frameHandler0->waitForFrameCount(1); + frameHandler1->waitForFrameCount(1); + + nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC); + + // Wait a bit, then ensure both clients get at least the required minimum number of frames + sleep(5); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); + unsigned framesReceived0 = 0, framesReceived1 = 0; + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + framesReceived0 = framesReceived0 - 1; // Back out the first frame we already waited for + framesReceived1 = framesReceived1 - 1; // Back out the first frame we already waited for + nsecs_t runTime = end - firstFrame; + float framesPerSecond0 = framesReceived0 / (runTime * kNanoToSeconds); + float framesPerSecond1 = framesReceived1 / (runTime * kNanoToSeconds); + ALOGI("Measured camera rate %3.2f fps and %3.2f fps", framesPerSecond0, framesPerSecond1); + EXPECT_GE(framesPerSecond0, kMinimumFramesPerSecond); + EXPECT_GE(framesPerSecond1, kMinimumFramesPerSecond); + + // Shutdown one client + frameHandler0->shutdown(); + + // Read frame counters again + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + + // Wait a bit again + sleep(5); + unsigned framesReceivedAfterStop0 = 0, framesReceivedAfterStop1 = 0; + frameHandler0->getFramesCounters(&framesReceivedAfterStop0, nullptr); + frameHandler1->getFramesCounters(&framesReceivedAfterStop1, nullptr); + EXPECT_EQ(framesReceived0, framesReceivedAfterStop0); + EXPECT_LT(framesReceived1, framesReceivedAfterStop1); + + // Shutdown another + frameHandler1->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCam0); + pEnumerator->closeCamera(pCam1); + } +} + + +/* + * CameraParameter: + * Verify that a client can adjust a camera parameter. + */ +TEST_F(EvsHidlTest, CameraParameter) { + ALOGI("Starting CameraParameter test"); + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Test each reported camera + Return<EvsResult> result = EvsResult::OK; + for (auto&& cam: cameraInfo) { + // Create a camera client + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Get the parameter list + std::vector<CameraParam> cmds; + pCam->getParameterList([&cmds](hidl_vec<CameraParam> cmdList) { + cmds.reserve(cmdList.size()); + for (auto &&cmd : cmdList) { + cmds.push_back(cmd); + } + } + ); + + if (cmds.size() < 1) { + continue; + } + + // 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); + + result = pCam->setMaster(); + ASSERT_EQ(EvsResult::OK, result); + + for (auto &cmd : cmds) { + // Get a valid parameter value range + int32_t minVal, maxVal, step; + pCam->getIntParameterRange( + cmd, + [&minVal, &maxVal, &step](int32_t val0, int32_t val1, int32_t val2) { + minVal = val0; + maxVal = val1; + step = val2; + } + ); + + EvsResult result = EvsResult::OK; + if (cmd == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + int32_t val1 = 0; + pCam->getIntParameter(CameraParam::AUTO_FOCUS, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + if (val1 != 0) { + pCam->setIntParameter(CameraParam::AUTO_FOCUS, 0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val1, 0); + } + } + + // Try to program a parameter with a random value [minVal, maxVal] + int32_t val0 = minVal + (std::rand() % (maxVal - minVal)); + int32_t val1 = 0; + + // Rounding down + val0 = val0 - (val0 % step); + pCam->setIntParameter(cmd, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + + ASSERT_EQ(EvsResult::OK, result); + + pCam->getIntParameter(cmd, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val0, val1) << "Values are not matched."; + } + + result = pCam->unsetMaster(); + ASSERT_EQ(EvsResult::OK, result); + + // 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(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Test each reported camera + for (auto&& cam: cameraInfo) { + // Create two camera clients. + sp<IEvsCamera_1_1> pCamMaster = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCamMaster, nullptr); + sp<IEvsCamera_1_1> pCamNonMaster = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .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. + EvsEvent aNotification = {}; + + // Release a master role. + pCamMaster->unsetMaster(); + + // Verify a change notification. + frameHandlerNonMaster->waitForEvent(EvsEventType::MASTER_RELEASED, aNotification); + ASSERT_EQ(EvsEventType::MASTER_RELEASED, + static_cast<EvsEventType>(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(EvsEventType::MASTER_RELEASED, aNotification); + ASSERT_EQ(EvsEventType::MASTER_RELEASED, + static_cast<EvsEventType>(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(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // Test each reported camera + for (auto&& cam: cameraInfo) { + // Create two camera clients. + sp<IEvsCamera_1_1> pCamMaster = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCamMaster, nullptr); + sp<IEvsCamera_1_1> pCamNonMaster = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCamNonMaster, nullptr); + + // Get the parameter list + std::vector<CameraParam> camMasterCmds, camNonMasterCmds; + pCamMaster->getParameterList([&camMasterCmds](hidl_vec<CameraParam> cmdList) { + camMasterCmds.reserve(cmdList.size()); + for (auto &&cmd : cmdList) { + camMasterCmds.push_back(cmd); + } + } + ); + + pCamNonMaster->getParameterList([&camNonMasterCmds](hidl_vec<CameraParam> cmdList) { + camNonMasterCmds.reserve(cmdList.size()); + for (auto &&cmd : cmdList) { + camNonMasterCmds.push_back(cmd); + } + } + ); + + if (camMasterCmds.size() < 1 || + camNonMasterCmds.size() < 1) { + // Skip a camera device if it does not support any parameter. + continue; + } + + // 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_EQ(EvsResult::OK, result); + + // Try to set another client as the master. + result = pCamNonMaster->setMaster(); + ASSERT_EQ(EvsResult::OWNERSHIP_LOST, result); + + // 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); + + int32_t val0 = 0; + int32_t val1 = 0; + for (auto &cmd : camMasterCmds) { + // Get a valid parameter value range + int32_t minVal, maxVal, step; + pCamMaster->getIntParameterRange( + cmd, + [&minVal, &maxVal, &step](int32_t val0, int32_t val1, int32_t val2) { + minVal = val0; + maxVal = val1; + step = val2; + } + ); + + EvsResult result = EvsResult::OK; + if (cmd == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + int32_t val1 = 1; + pCamMaster->setIntParameter(CameraParam::AUTO_FOCUS, 0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val1, 0); + } + + // Try to program a parameter + val0 = minVal + (std::rand() % (maxVal - minVal)); + + // Rounding down + val0 = val0 - (val0 % step); + pCamMaster->setIntParameter(cmd, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + + // Wait a moment + sleep(1); + + // Non-master client expects to receive a parameter change notification + // whenever a master client adjusts it. + EvsEvent aNotification = {}; + + pCamMaster->getIntParameter(cmd, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val0, val1) << "Values are not matched."; + + // Verify a change notification + frameHandlerNonMaster->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification); + ASSERT_EQ(EvsEventType::PARAMETER_CHANGED, + static_cast<EvsEventType>(aNotification.aType)); + ASSERT_EQ(cmd, + 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->setIntParameter(camNonMasterCmds[0], val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::INVALID_ARG, result); + + // Non-master client attemps to be a master + result = pCamNonMaster->setMaster(); + ASSERT_EQ(EvsResult::OWNERSHIP_LOST, result); + + // Master client retires from a master role + result = pCamMaster->unsetMaster(); + ASSERT_EQ(EvsResult::OK, result); + + // Try to adjust a parameter after being retired + pCamMaster->setIntParameter(camMasterCmds[0], val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::INVALID_ARG, result); + + // Non-master client becomes a master + result = pCamNonMaster->setMaster(); + ASSERT_EQ(EvsResult::OK, result); + + // Try to adjust a parameter via new master client + for (auto &cmd : camNonMasterCmds) { + // Get a valid parameter value range + int32_t minVal, maxVal, step; + pCamNonMaster->getIntParameterRange( + cmd, + [&minVal, &maxVal, &step](int32_t val0, int32_t val1, int32_t val2) { + minVal = val0; + maxVal = val1; + step = val2; + } + ); + + EvsResult result = EvsResult::OK; + if (cmd == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + int32_t val1 = 1; + pCamNonMaster->setIntParameter(CameraParam::AUTO_FOCUS, 0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val1, 0); + } + + // Try to program a parameter + val0 = minVal + (std::rand() % (maxVal - minVal)); + + // Rounding down + val0 = val0 - (val0 % step); + pCamNonMaster->setIntParameter(cmd, val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + + // Wait a moment + sleep(1); + + // Non-master client expects to receive a parameter change notification + // whenever a master client adjusts it. + EvsEvent aNotification = {}; + + pCamNonMaster->getIntParameter(cmd, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val0, val1) << "Values are not matched."; + + // Verify a change notification + frameHandlerMaster->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification); + ASSERT_EQ(EvsEventType::PARAMETER_CHANGED, + static_cast<EvsEventType>(aNotification.aType)); + ASSERT_EQ(cmd, + static_cast<CameraParam>(aNotification.payload[0])); + ASSERT_EQ(val1, + static_cast<int32_t>(aNotification.payload[1])); + } + + // New master retires from a master role + result = pCamNonMaster->unsetMaster(); + ASSERT_EQ(EvsResult::OK, result); + + // 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"); + + if (mIsHwModule) { + // This test is not for HW module implementation. + return; + } + + // Get the camera list + loadCameraList(); + + // Using null stream configuration makes EVS uses the default resolution and + // output format. + Stream nullCfg = {}; + + // 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_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam0, nullptr); + + sp<IEvsCamera_1_1> pCam1 = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam1, nullptr); + + // Get the parameter list; this test will use the first command in both + // lists. + std::vector<CameraParam> cam0Cmds, cam1Cmds; + pCam0->getParameterList([&cam0Cmds](hidl_vec<CameraParam> cmdList) { + cam0Cmds.reserve(cmdList.size()); + for (auto &&cmd : cmdList) { + cam0Cmds.push_back(cmd); + } + } + ); + + pCam1->getParameterList([&cam1Cmds](hidl_vec<CameraParam> cmdList) { + cam1Cmds.reserve(cmdList.size()); + for (auto &&cmd : cmdList) { + cam1Cmds.push_back(cmd); + } + } + ); + if (cam0Cmds.size() < 1 || cam1Cmds.size() < 1) { + // Cannot execute this test. + return; + } + + // 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 parameter. + EvsResult result = EvsResult::OK; + // Get a valid parameter value range + int32_t minVal, maxVal, step; + pCam1->getIntParameterRange( + cam1Cmds[0], + [&minVal, &maxVal, &step](int32_t val0, int32_t val1, int32_t val2) { + minVal = val0; + maxVal = val1; + step = val2; + } + ); + + if (cam1Cmds[0] == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + int32_t val1 = 0; + pCam1->getIntParameter(CameraParam::AUTO_FOCUS, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + if (val1 != 0) { + pCam1->setIntParameter(CameraParam::AUTO_FOCUS, 0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val1, 0); + } + } + + // Try to program a parameter with a random value [minVal, maxVal] + int32_t val0 = minVal + (std::rand() % (maxVal - minVal)); + int32_t val1 = 0; + + // Rounding down + val0 = val0 - (val0 % step); + + result = pCam1->setMaster(); + ASSERT_EQ(EvsResult::OK, result); + + pCam1->setIntParameter(cam1Cmds[0], val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + + // Verify a change notification + EvsEvent aNotification = {}; + bool timeout = + frameHandler0->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification); + ASSERT_FALSE(timeout) << "Expected event does not arrive"; + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), + EvsEventType::PARAMETER_CHANGED); + ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]), + cam1Cmds[0]); + 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(EvsEventType::MASTER_RELEASED, aNotification); + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), + EvsEventType::MASTER_RELEASED); + + // Client 0 programs a parameter + val0 = minVal + (std::rand() % (maxVal - minVal)); + val1 = 0; + + // Rounding down + val0 = val0 - (val0 % step); + + if (cam0Cmds[0] == CameraParam::ABSOLUTE_FOCUS) { + // Try to turn off auto-focus + int32_t val1 = 0; + pCam0->getIntParameter(CameraParam::AUTO_FOCUS, + [&result, &val1](auto status, auto value) { + result = status; + if (status == EvsResult::OK) { + val1 = value; + } + }); + if (val1 != 0) { + pCam0->setIntParameter(CameraParam::AUTO_FOCUS, 0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + ASSERT_EQ(val1, 0); + } + } + + pCam0->setIntParameter(cam0Cmds[0], val0, + [&result, &val1](auto status, auto effectiveValue) { + result = status; + val1 = effectiveValue; + }); + ASSERT_EQ(EvsResult::OK, result); + + // Verify a change notification + timeout = + frameHandler1->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification); + ASSERT_FALSE(timeout) << "Expected event does not arrive"; + ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), + EvsEventType::PARAMETER_CHANGED); + ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]), + cam0Cmds[0]); + 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); +} + + +/* + * CameraUseStreamConfigToDisplay: + * End to end test of data flowing from the camera to the display. Similar to + * CameraToDisplayRoundTrip test case but this case retrieves available stream + * configurations from EVS and uses one of them to start a video stream. + */ +TEST_F(EvsHidlTest, CameraUseStreamConfigToDisplay) { + ALOGI("Starting CameraUseStreamConfigToDisplay 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) { + // choose a configuration that has a frame rate faster than minReqFps. + Stream targetCfg = {}; + const int32_t minReqFps = 15; + int32_t maxArea = 0; + camera_metadata_entry_t streamCfgs; + bool foundCfg = false; + if (!find_camera_metadata_entry( + reinterpret_cast<camera_metadata_t *>(cam.metadata.data()), + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + &streamCfgs)) { + // Stream configurations are found in metadata + RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32); + for (unsigned idx = 0; idx < streamCfgs.count; idx += kStreamCfgSz) { + if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) { + + if (ptr->width * ptr->height > maxArea && + ptr->framerate >= minReqFps) { + targetCfg.width = ptr->width; + targetCfg.height = ptr->height; + + maxArea = ptr->width * ptr->height; + foundCfg = true; + } + } + ++ptr; + } + } + targetCfg.format = + static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888); + + if (!foundCfg) { + // Current EVS camera does not provide stream configurations in the + // metadata. + continue; + } + + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam, nullptr); + + // Set up a frame receiver object which will fire up its own thread. + sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam, + pDisplay, + FrameHandler::eAutoReturn); + + + // Activate the display + pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME); + + // Start the camera's video stream + bool startResult = frameHandler->startStream(); + ASSERT_TRUE(startResult); + + // Wait a while to let the data flow + static const int kSecondsToWait = 5; + const int streamTimeMs = kSecondsToWait * kSecondsToMilliseconds - + kMaxStreamStartMilliseconds; + const unsigned minimumFramesExpected = streamTimeMs * kMinimumFramesPerSecond / + kSecondsToMilliseconds; + sleep(kSecondsToWait); + unsigned framesReceived = 0; + unsigned framesDisplayed = 0; + frameHandler->getFramesCounters(&framesReceived, &framesDisplayed); + EXPECT_EQ(framesReceived, framesDisplayed); + EXPECT_GE(framesDisplayed, minimumFramesExpected); + + // Turn off the display (yes, before the stream stops -- it should be handled) + pDisplay->setDisplayState(DisplayState::NOT_VISIBLE); + + // Shut down the streamer + frameHandler->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCam); + } + + // Explicitly release the display + pEnumerator->closeDisplay(pDisplay); +} + + +/* + * MultiCameraStreamUseConfig: + * Verify that each client can start and stop video streams on the same + * underlying camera with same configuration. + */ +TEST_F(EvsHidlTest, MultiCameraStreamUseConfig) { + ALOGI("Starting MultiCameraStream 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) { + // choose a configuration that has a frame rate faster than minReqFps. + Stream targetCfg = {}; + const int32_t minReqFps = 15; + int32_t maxArea = 0; + camera_metadata_entry_t streamCfgs; + bool foundCfg = false; + if (!find_camera_metadata_entry( + reinterpret_cast<camera_metadata_t *>(cam.metadata.data()), + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + &streamCfgs)) { + // Stream configurations are found in metadata + RawStreamConfig *ptr = reinterpret_cast<RawStreamConfig *>(streamCfgs.data.i32); + for (unsigned idx = 0; idx < streamCfgs.count; idx += kStreamCfgSz) { + if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) { + + if (ptr->width * ptr->height > maxArea && + ptr->framerate >= minReqFps) { + targetCfg.width = ptr->width; + targetCfg.height = ptr->height; + + maxArea = ptr->width * ptr->height; + foundCfg = true; + } + } + ++ptr; + } + } + targetCfg.format = + static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888); + + if (!foundCfg) { + ALOGI("Device %s does not provide a list of supported stream configurations, skipped", + cam.v1.cameraId.c_str()); + + continue; + } + + // Create the first camera client with a selected stream configuration. + sp<IEvsCamera_1_1> pCam0 = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam0, nullptr); + + // Try to create the second camera client with different stream + // configuration. + int32_t id = targetCfg.id; + targetCfg.id += 1; // EVS manager sees only the stream id. + sp<IEvsCamera_1_1> pCam1 = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg)) + .withDefault(nullptr); + ASSERT_EQ(pCam1, nullptr); + + // Try again with same stream configuration. + targetCfg.id = id; + pCam1 = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, targetCfg)) + .withDefault(nullptr); + ASSERT_NE(pCam1, nullptr); + + // Set up per-client frame receiver objects which will fire up its own thread + sp<FrameHandler> frameHandler0 = new FrameHandler(pCam0, cam, + nullptr, + FrameHandler::eAutoReturn); + ASSERT_NE(frameHandler0, nullptr); + + sp<FrameHandler> frameHandler1 = new FrameHandler(pCam1, cam, + nullptr, + FrameHandler::eAutoReturn); + ASSERT_NE(frameHandler1, nullptr); + + // Start the camera's video stream via client 0 + bool startResult = false; + startResult = frameHandler0->startStream() && + frameHandler1->startStream(); + ASSERT_TRUE(startResult); + + // Ensure the stream starts + frameHandler0->waitForFrameCount(1); + frameHandler1->waitForFrameCount(1); + + nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC); + + // Wait a bit, then ensure both clients get at least the required minimum number of frames + sleep(5); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); + unsigned framesReceived0 = 0, framesReceived1 = 0; + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + framesReceived0 = framesReceived0 - 1; // Back out the first frame we already waited for + framesReceived1 = framesReceived1 - 1; // Back out the first frame we already waited for + nsecs_t runTime = end - firstFrame; + float framesPerSecond0 = framesReceived0 / (runTime * kNanoToSeconds); + float framesPerSecond1 = framesReceived1 / (runTime * kNanoToSeconds); + ALOGI("Measured camera rate %3.2f fps and %3.2f fps", framesPerSecond0, framesPerSecond1); + EXPECT_GE(framesPerSecond0, kMinimumFramesPerSecond); + EXPECT_GE(framesPerSecond1, kMinimumFramesPerSecond); + + // Shutdown one client + frameHandler0->shutdown(); + + // Read frame counters again + frameHandler0->getFramesCounters(&framesReceived0, nullptr); + frameHandler1->getFramesCounters(&framesReceived1, nullptr); + + // Wait a bit again + sleep(5); + unsigned framesReceivedAfterStop0 = 0, framesReceivedAfterStop1 = 0; + frameHandler0->getFramesCounters(&framesReceivedAfterStop0, nullptr); + frameHandler1->getFramesCounters(&framesReceivedAfterStop1, nullptr); + EXPECT_EQ(framesReceived0, framesReceivedAfterStop0); + EXPECT_LT(framesReceived1, framesReceivedAfterStop1); + + // Shutdown another + frameHandler1->shutdown(); + + // Explicitly release the camera + pEnumerator->closeCamera(pCam0); + pEnumerator->closeCamera(pCam1); + } +} + + +int main(int argc, char** argv) { + ::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance()); + ::testing::InitGoogleTest(&argc, argv); + EvsHidlEnvironment::Instance()->init(&argc, argv); + int status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + return status; +} diff --git a/automotive/evs/common/utils/default/Android.bp b/automotive/evs/common/utils/default/Android.bp new file mode 100644 index 0000000000..7734f5c19e --- /dev/null +++ b/automotive/evs/common/utils/default/Android.bp @@ -0,0 +1,31 @@ +// +// 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.evs@common-default-lib", + vendor_available: true, + relative_install_path: "hw", + cflags: [ + "-O0", + "-g", + ], + srcs: [ + "FormatConvert.cpp" + ], + export_include_dirs: ["include"], + shared_libs: [ + ], +} diff --git a/automotive/evs/1.0/vts/functional/FormatConvert.cpp b/automotive/evs/common/utils/default/FormatConvert.cpp index 3d82d32cac..d4c7da0363 100644 --- a/automotive/evs/1.0/vts/functional/FormatConvert.cpp +++ b/automotive/evs/common/utils/default/FormatConvert.cpp @@ -18,10 +18,15 @@ #include "FormatConvert.h" +namespace android { +namespace hardware { +namespace automotive { +namespace evs { +namespace common { // Round up to the nearest multiple of the given alignment value template<unsigned alignment> -int align(int value) { +int Utils::align(int value) { static_assert((alignment && !(alignment & (alignment - 1))), "alignment must be a power of 2"); @@ -31,15 +36,17 @@ int align(int value) { // Limit the given value to the provided range. :) -static inline float clamp(float v, float min, float max) { +inline float Utils::clamp(float v, float min, float max) { if (v < min) return min; if (v > max) return max; return v; } -static uint32_t yuvToRgbx(const unsigned char Y, const unsigned char Uin, const unsigned char Vin, - bool bgrxFormat = false) { +uint32_t Utils::yuvToRgbx(const unsigned char Y, + const unsigned char Uin, + const unsigned char Vin, + bool bgrxFormat) { // Don't use this if you want to see the best performance. :) // Better to do this in a pixel shader if we really have to, but on actual // embedded hardware we expect to be able to texture directly from the YUV data @@ -67,10 +74,10 @@ static uint32_t yuvToRgbx(const unsigned char Y, const unsigned char Uin, const } -void copyNV21toRGB32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels, - bool bgrxFormat) +void Utils::copyNV21toRGB32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels, + bool bgrxFormat) { // The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved // U/V array. It assumes an even width and height for the overall image, and a horizontal @@ -99,10 +106,10 @@ void copyNV21toRGB32(unsigned width, unsigned height, } -void copyYV12toRGB32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels, - bool bgrxFormat) +void Utils::copyYV12toRGB32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels, + bool bgrxFormat) { // The YV12 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 U array, followed // by another 1/2 x 1/2 V array. It assumes an even width and height for the overall image, @@ -134,10 +141,10 @@ void copyYV12toRGB32(unsigned width, unsigned height, } -void copyYUYVtoRGB32(unsigned width, unsigned height, - uint8_t* src, unsigned srcStridePixels, - uint32_t* dst, unsigned dstStridePixels, - bool bgrxFormat) +void Utils::copyYUYVtoRGB32(unsigned width, unsigned height, + uint8_t* src, unsigned srcStridePixels, + uint32_t* dst, unsigned dstStridePixels, + bool bgrxFormat) { uint32_t* srcWords = (uint32_t*)src; @@ -167,34 +174,34 @@ void copyYUYVtoRGB32(unsigned width, unsigned height, } -void copyNV21toBGR32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels) +void Utils::copyNV21toBGR32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels) { return copyNV21toRGB32(width, height, src, dst, dstStridePixels, true); } -void copyYV12toBGR32(unsigned width, unsigned height, - uint8_t* src, - uint32_t* dst, unsigned dstStridePixels) +void Utils::copyYV12toBGR32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels) { return copyYV12toRGB32(width, height, src, dst, dstStridePixels, true); } -void copyYUYVtoBGR32(unsigned width, unsigned height, - uint8_t* src, unsigned srcStridePixels, - uint32_t* dst, unsigned dstStridePixels) +void Utils::copyYUYVtoBGR32(unsigned width, unsigned height, + uint8_t* src, unsigned srcStridePixels, + uint32_t* dst, unsigned dstStridePixels) { return copyYUYVtoRGB32(width, height, src, srcStridePixels, dst, dstStridePixels, true); } -void copyMatchedInterleavedFormats(unsigned width, unsigned height, - void* src, unsigned srcStridePixels, - void* dst, unsigned dstStridePixels, - unsigned pixelSize) { +void Utils::copyMatchedInterleavedFormats(unsigned width, unsigned height, + void* src, unsigned srcStridePixels, + void* dst, unsigned dstStridePixels, + unsigned pixelSize) { for (unsigned row = 0; row < height; row++) { // Copy the entire row of pixel data memcpy(dst, src, width * pixelSize); @@ -204,3 +211,10 @@ void copyMatchedInterleavedFormats(unsigned width, unsigned height, dst = (uint8_t*)dst + dstStridePixels * pixelSize; } } + +} // namespace common +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android + diff --git a/automotive/evs/common/utils/default/include/FormatConvert.h b/automotive/evs/common/utils/default/include/FormatConvert.h new file mode 100644 index 0000000000..2bb89554da --- /dev/null +++ b/automotive/evs/common/utils/default/include/FormatConvert.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef EVS_VTS_FORMATCONVERT_H +#define EVS_VTS_FORMATCONVERT_H + +#include <queue> +#include <stdint.h> + + +namespace android { +namespace hardware { +namespace automotive { +namespace evs { +namespace common { + +class Utils { +public: + // Given an image buffer in NV21 format (HAL_PIXEL_FORMAT_YCRCB_420_SP), output 32bit RGBx/BGRx + // values. The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved + // U/V array. It assumes an even width and height for the overall image, and a horizontal + // stride that is an even multiple of 16 bytes for both the Y and UV arrays. + static void copyNV21toRGB32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels, + bool bgrxFormat = false); + + static void copyNV21toBGR32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels); + + + // Given an image buffer in YV12 format (HAL_PIXEL_FORMAT_YV12), output 32bit RGBx/BGRx values. + // The YV12 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 U array, followed + // by another 1/2 x 1/2 V array. It assumes an even width and height for the overall image, + // and a horizontal stride that is an even multiple of 16 bytes for each of the Y, U, + // and V arrays. + static void copyYV12toRGB32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels, + bool bgrxFormat = false); + + static void copyYV12toBGR32(unsigned width, unsigned height, + uint8_t* src, + uint32_t* dst, unsigned dstStridePixels); + + // Given an image buffer in YUYV format (HAL_PIXEL_FORMAT_YCBCR_422_I), output 32bit RGBx/BGRx + // values. The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved + // U/V array. It assumes an even width and height for the overall image, and a horizontal + // stride that is an even multiple of 16 bytes for both the Y and UV arrays. + static void copyYUYVtoRGB32(unsigned width, unsigned height, + uint8_t* src, unsigned srcStrideBytes, + uint32_t* dst, unsigned dstStrideBytes, + bool bgrxFormat = false); + + static void copyYUYVtoBGR32(unsigned width, unsigned height, + uint8_t* src, unsigned srcStrideBytes, + uint32_t* dst, unsigned dstStrideBytes); + + + // Given an simple rectangular image buffer with an integer number of bytes per pixel, + // copy the pixel values into a new rectangular buffer (potentially with a different stride). + // This is typically used to copy RGBx data into an RGBx output buffer. + static void copyMatchedInterleavedFormats(unsigned width, unsigned height, + void* src, unsigned srcStridePixels, + void* dst, unsigned dstStridePixels, + unsigned pixelSize); + +private: + template<unsigned alignment> + static int align(int value); + + static inline float clamp(float v, float min, float max); + static uint32_t yuvToRgbx(const unsigned char Y, + const unsigned char Uin, + const unsigned char Vin, + bool bgrxFormat = false); +}; + +} // namespace common +} // namespace evs +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // EVS_VTS_FORMATCONVERT_H diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index ed09859443..f9c25d1d0e 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -58,6 +58,7 @@ cc_library_static { defaults: ["vhal_v2_0_defaults"], srcs: [ "impl/vhal_v2_0/CommConn.cpp", + "impl/vhal_v2_0/EmulatedVehicleConnector.cpp", "impl/vhal_v2_0/EmulatedVehicleHal.cpp", "impl/vhal_v2_0/VehicleEmulator.cpp", "impl/vhal_v2_0/PipeComm.cpp", diff --git a/automotive/vehicle/2.0/default/VehicleService.cpp b/automotive/vehicle/2.0/default/VehicleService.cpp index d1fd55567e..127eb98e43 100644 --- a/automotive/vehicle/2.0/default/VehicleService.cpp +++ b/automotive/vehicle/2.0/default/VehicleService.cpp @@ -20,8 +20,9 @@ #include <iostream> -#include <vhal_v2_0/VehicleHalManager.h> +#include <vhal_v2_0/EmulatedVehicleConnector.h> #include <vhal_v2_0/EmulatedVehicleHal.h> +#include <vhal_v2_0/VehicleHalManager.h> using namespace android; using namespace android::hardware; @@ -29,9 +30,11 @@ using namespace android::hardware::automotive::vehicle::V2_0; int main(int /* argc */, char* /* argv */ []) { auto store = std::make_unique<VehiclePropertyStore>(); - auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get()); + auto connector = impl::makeEmulatedPassthroughConnector(); + auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get()); auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get()); auto service = std::make_unique<VehicleHalManager>(hal.get()); + connector->setValuePool(hal->getValuePool()); configureRpcThreadpool(4, true /* callerWillJoin */); diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleConnector.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleConnector.h new file mode 100644 index 0000000000..56ecd67ea2 --- /dev/null +++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleConnector.h @@ -0,0 +1,141 @@ +/* + * 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_automotive_vehicle_V2_0_VehicleConnector_H_ +#define android_hardware_automotive_vehicle_V2_0_VehicleConnector_H_ + +#include <vector> + +#include <android/hardware/automotive/vehicle/2.0/types.h> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +/** + * This file defines the interface of client/server pair for HAL-vehicle + * communication. Vehicle HAL may use this interface to talk to the vehicle + * regardless of the underlying communication channels. + */ + +/** + * Vehicle HAL talks to the vehicle through a client, instead of accessing + * the car bus directly, to give us more flexibility on the implementation. + * Android OS do not need direct access to the vehicle, and the communication + * channel is also customizable. + * + * Client lives on the Android (HAL) side to talk to the vehicle + */ +class IVehicleClient { + public: + IVehicleClient() = default; + + IVehicleClient(const IVehicleClient&) = delete; + + IVehicleClient& operator=(const IVehicleClient&) = delete; + + IVehicleClient(IVehicleClient&&) = default; + + virtual ~IVehicleClient() = default; + + // Get configuration of all properties from server + virtual std::vector<VehiclePropConfig> getAllPropertyConfig() const = 0; + + // Send the set property request to server + virtual StatusCode setProperty(const VehiclePropValue& value) = 0; + + // Receive a new property value from server + virtual void onPropertyValue(const VehiclePropValue& value) = 0; +}; + +/** + * Server lives on the vehicle side to talk to Android HAL + */ +class IVehicleServer { + public: + IVehicleServer() = default; + + IVehicleServer(const IVehicleServer&) = delete; + + IVehicleServer& operator=(const IVehicleServer&) = delete; + + IVehicleServer(IVehicleServer&&) = default; + + virtual ~IVehicleServer() = default; + + // Receive the get property configuration request from HAL. + // Return a list of all property config + virtual std::vector<VehiclePropConfig> onGetAllPropertyConfig() const = 0; + + // Receive the set property request from HAL. + // Process the setting and return the status code + virtual StatusCode onSetProperty(const VehiclePropValue& value) = 0; + + // Receive a new property value from car (via direct connection to the car bus or the emulator) + // and forward the value to HAL + virtual void onPropertyValueFromCar(const VehiclePropValue& value) = 0; +}; + +/** + * If Android has direct access to the vehicle, then the client and + * the server may act in passthrough mode to avoid extra IPC + * + * Template is used here for spliting the logic of operating Android objects (VehicleClientType), + * talking to cars (VehicleServerType) and the commucation between client and server (passthrough + * mode in this case), so that we can easily combine different parts together without duplicating + * codes (for example, in Google VHAL, the server talks to the fake car in the same way no matter + * if it is on top of passthrough connector or VSOCK or any other communication channels between + * client and server) + * + * The alternative may be factoring the common logic of every operations for both client and + * server. Which is not always the case. Making sure different non-template connectors calling + * the same method is hard, especially when the engineer maintaining the code may not be aware + * of it when making changes. Template is a clean and easy way to solve this problem in this + * case. + */ +template <typename VehicleClientType, typename VehicleServerType> +class IPassThroughConnector : public VehicleClientType, public VehicleServerType { + static_assert(std::is_base_of_v<IVehicleClient, VehicleClientType>); + static_assert(std::is_base_of_v<IVehicleServer, VehicleServerType>); + + public: + std::vector<VehiclePropConfig> getAllPropertyConfig() const override { + return this->onGetAllPropertyConfig(); + } + + StatusCode setProperty(const VehiclePropValue& value) override { + return this->onSetProperty(value); + } + + void onPropertyValueFromCar(const VehiclePropValue& value) override { + return this->onPropertyValue(value); + } + + // To be implemented: + // virtual std::vector<VehiclePropConfig> onGetAllPropertyConfig() = 0; + // virtual void onPropertyValue(const VehiclePropValue& value) = 0; + // virtual StatusCode onSetProperty(const VehiclePropValue& value) = 0; +}; + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_VehicleConnector_H_ diff --git a/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp index 94ace455cc..24b777c75f 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 @@ bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue, 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 8a89322a75..2dbcbba28f 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,34 @@ const int32_t kGenerateFakeDataControllingProperty = 0x0666 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED; /** + * This property is used for test purpose to set properties' value from vehicle. + * For example: Mocking hard button press triggering a HVAC fan speed change. + * Android set kSetPropertyFromVehcileForTest with an array of integer {HVAC_FAN_SPEED, value of + * fan speed} and a long value indicates the timestamp of the events . + * It only works with integer type properties. + */ +const int32_t kSetIntPropertyFromVehcileForTest = + 0x1112 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED; +/** + * This property is used for test purpose to set properties' value from vehicle. + * It only works with float type properties. + */ +const int32_t kSetFloatPropertyFromVehcileForTest = + 0x1113 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED; +/** + * This property is used for test purpose to set properties' value from vehicle. + * It only works with boolean type properties. + */ +const int32_t kSetBooleanPropertyFromVehcileForTest = + 0x1114 | 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. @@ -167,7 +195,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::STATIC, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.floatValues = {15000.0f}}}, @@ -177,14 +204,13 @@ const ConfigDeclaration kVehicleProperties[]{ .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::STATIC, }, - .initialValue = {.int32Values = {1}}}, + .initialValue = {.int32Values = {(int)FuelType::FUEL_TYPE_UNLEADED}}}, {.config = { .prop = toInt(VehicleProperty::INFO_EV_BATTERY_CAPACITY), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::STATIC, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.floatValues = {150000.0f}}}, @@ -194,14 +220,13 @@ const ConfigDeclaration kVehicleProperties[]{ .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::STATIC, }, - .initialValue = {.int32Values = {1}}}, + .initialValue = {.int32Values = {(int)EvConnectorType::IEC_TYPE_1_AC}}}, {.config = { .prop = toInt(VehicleProperty::INFO_DRIVER_SEAT), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::STATIC, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {SEAT_1_LEFT}}}, @@ -210,7 +235,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::INFO_FUEL_DOOR_LOCATION), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::STATIC, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {FUEL_DOOR_REAR_LEFT}}}, @@ -219,7 +243,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::INFO_EV_PORT_LOCATION), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::STATIC, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {CHARGE_PORT_FRONT_LEFT}}}, @@ -234,7 +257,7 @@ const ConfigDeclaration kVehicleProperties[]{ { .prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED), .access = VehiclePropertyAccess::READ, - .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, .minSampleRate = 1.0f, .maxSampleRate = 10.0f, }, @@ -265,7 +288,9 @@ const ConfigDeclaration kVehicleProperties[]{ { .prop = toInt(VehicleProperty::PERF_ODOMETER), .access = VehiclePropertyAccess::READ, - .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 0.0f, + .maxSampleRate = 10.0f, }, .initialValue = {.floatValues = {0.0f}}}, @@ -285,8 +310,9 @@ const ConfigDeclaration kVehicleProperties[]{ { .prop = toInt(VehicleProperty::FUEL_LEVEL), .access = VehiclePropertyAccess::READ, - .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 0.0f, + .maxSampleRate = 100.0f, }, .initialValue = {.floatValues = {15000.0f}}}, @@ -295,7 +321,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::FUEL_DOOR_OPEN), .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {0}}}, @@ -303,8 +328,9 @@ const ConfigDeclaration kVehicleProperties[]{ { .prop = toInt(VehicleProperty::EV_BATTERY_LEVEL), .access = VehiclePropertyAccess::READ, - .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 0.0f, + .maxSampleRate = 100.0f, }, .initialValue = {.floatValues = {150000.0f}}}, @@ -313,7 +339,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::EV_CHARGE_PORT_OPEN), .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {0}}}, @@ -322,7 +347,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::EV_CHARGE_PORT_CONNECTED), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {0}}}, @@ -330,17 +354,17 @@ const ConfigDeclaration kVehicleProperties[]{ { .prop = toInt(VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE), .access = VehiclePropertyAccess::READ, - .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, + .changeMode = VehiclePropertyChangeMode::CONTINUOUS, + .minSampleRate = 1.0f, + .maxSampleRate = 10.0f, }, .initialValue = {.floatValues = {0.0f}}}, {.config = { .prop = toInt(VehicleProperty::RANGE_REMAINING), - .access = VehiclePropertyAccess::READ, + .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::CONTINUOUS, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, .minSampleRate = 1.0f, .maxSampleRate = 2.0f, }, @@ -374,7 +398,7 @@ const ConfigDeclaration kVehicleProperties[]{ .minSampleRate = 1.0f, .maxSampleRate = 2.0f, }, - .initialValue = {.floatValues = {200}}}, // units in kPa + .initialValue = {.floatValues = {200.0f}}}, // units in kPa {.config = { @@ -397,7 +421,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::FUEL_LEVEL_LOW), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {0}}}, @@ -430,6 +453,17 @@ const ConfigDeclaration kVehicleProperties[]{ .areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}}, .initialValue = {.int32Values = {0}} // Will be used for all areas. }, + { + .config = {.prop = toInt(VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON), + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .areaConfigs = + {VehicleAreaConfig{ + .areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD)}, + VehicleAreaConfig{ + .areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}}, + .initialValue = {.int32Values = {0}} // Will be used for all areas. + }, {.config = {.prop = toInt(VehicleProperty::HVAC_MAX_DEFROST_ON), .access = VehiclePropertyAccess::READ_WRITE, @@ -558,14 +592,10 @@ const ConfigDeclaration kVehicleProperties[]{ }, .initialValue = {.floatValues = {25.0f}}}, - {.config = - { - .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS), - .access = VehiclePropertyAccess::READ_WRITE, - .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, - .configArray = {(int)VehicleUnit::FAHRENHEIT, (int)VehicleUnit::CELSIUS}, - }, + {.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS), + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .configArray = {(int)VehicleUnit::FAHRENHEIT, (int)VehicleUnit::CELSIUS}}, .initialValue = {.int32Values = {(int)VehicleUnit::FAHRENHEIT}}}, {.config = @@ -573,8 +603,8 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::DISTANCE_DISPLAY_UNITS), .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, .configArray = {(int)VehicleUnit::KILOMETER, (int)VehicleUnit::MILE}, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}} }, .initialValue = {.int32Values = {(int)VehicleUnit::MILE}}}, @@ -596,6 +626,14 @@ const ConfigDeclaration kVehicleProperties[]{ {.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, @@ -626,6 +664,50 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = kGenerateFakeDataControllingProperty, .access = VehiclePropertyAccess::WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .configArray = {1, 0, 0, 2, 0, 0, 0, 0, 0}, + }, + }, + + { + .config = + { + .prop = kSetIntPropertyFromVehcileForTest, + .access = VehiclePropertyAccess::WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .configArray = {0, 0, 0, 2, 1, 0, 0, 0, 0}, + }, + }, + + { + .config = + { + .prop = kSetFloatPropertyFromVehcileForTest, + .access = VehiclePropertyAccess::WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .configArray = {0, 0, 1, 0, 1, 0, 1, 0, 0}, + }, + }, + + { + .config = + { + .prop = kSetBooleanPropertyFromVehcileForTest, + .access = VehiclePropertyAccess::WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .configArray = {0, 1, 1, 0, 1, 0, 0, 0, 0}, + }, + }, + + { + .config = {.prop = kMixedTypePropertyForTest, + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .configArray = {1, 1, 0, 2, 0, 0, 1, 0, 0}}, + .initialValue = + { + .int32Values = {1 /* indicate TRUE boolean value */, 2, 3}, + .floatValues = {4.5f}, + .stringValue = "MIXED property", }, }, @@ -757,7 +839,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::HEADLIGHTS_STATE), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_STATE_ON}}}, @@ -766,7 +847,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::HIGH_BEAM_LIGHTS_STATE), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_STATE_ON}}}, @@ -775,7 +855,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::FOG_LIGHTS_STATE), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_STATE_ON}}}, @@ -784,7 +863,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::HAZARD_LIGHTS_STATE), .access = VehiclePropertyAccess::READ, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_STATE_ON}}}, @@ -793,7 +871,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::HEADLIGHTS_SWITCH), .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}}, @@ -802,7 +879,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::HIGH_BEAM_LIGHTS_SWITCH), .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}}, @@ -811,7 +887,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::FOG_LIGHTS_SWITCH), .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}}, @@ -820,7 +895,6 @@ const ConfigDeclaration kVehicleProperties[]{ .prop = toInt(VehicleProperty::HAZARD_LIGHTS_SWITCH), .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE, - .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}, }, .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}}, @@ -874,6 +948,24 @@ const ConfigDeclaration kVehicleProperties[]{ .access = VehiclePropertyAccess::READ_WRITE, .changeMode = VehiclePropertyChangeMode::ON_CHANGE}, .initialValue = {.stringValue = "Vendor String Property"}}, + + {.config = + { + .prop = toInt(VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION), + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + .configArray = + {kMixedTypePropertyForTest, + (int)VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_INFO, + (int)VehicleVendorPermission::PERMISSION_SET_VENDOR_CATEGORY_INFO, + VENDOR_EXTENSION_INT_PROPERTY, + (int)VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_SEAT, + (int)VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE, + VENDOR_EXTENSION_FLOAT_PROPERTY, + (int)VehicleVendorPermission::PERMISSION_DEFAULT, + (int)VehicleVendorPermission::PERMISSION_DEFAULT}, + }, + .initialValue = {.int32Values = {1}}}, }; } // impl diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp new file mode 100644 index 0000000000..168999d829 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp @@ -0,0 +1,254 @@ +/* + * 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 <utils/SystemClock.h> + +#include "DefaultConfig.h" +#include "EmulatedVehicleConnector.h" +#include "JsonFakeValueGenerator.h" +#include "LinearFakeValueGenerator.h" +#include "Obd2SensorStore.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +namespace impl { + +void EmulatedVehicleClient::onPropertyValue(const VehiclePropValue& value) { + if (!mPropCallback) { + LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!"; + return; + } + return mPropCallback(value); +} + +void EmulatedVehicleClient::registerPropertyValueCallback(PropertyCallBackType&& callback) { + if (mPropCallback) { + LOG(ERROR) << __func__ << ": Cannot register multiple callbacks!"; + return; + } + mPropCallback = std::move(callback); +} + +GeneratorHub* EmulatedVehicleServer::getGenerator() { + return &mGeneratorHub; +} + +VehiclePropValuePool* EmulatedVehicleServer::getValuePool() const { + if (!mValuePool) { + LOG(WARNING) << __func__ << ": Value pool not set!"; + } + return mValuePool; +} + +void EmulatedVehicleServer::setValuePool(VehiclePropValuePool* valuePool) { + if (!valuePool) { + LOG(WARNING) << __func__ << ": Setting value pool to nullptr!"; + } + mValuePool = valuePool; +} + +void EmulatedVehicleServer::onFakeValueGenerated(const VehiclePropValue& value) { + LOG(DEBUG) << __func__ << ": " << toString(value); + auto updatedPropValue = getValuePool()->obtain(value); + if (updatedPropValue) { + updatedPropValue->timestamp = value.timestamp; + updatedPropValue->status = VehiclePropertyStatus::AVAILABLE; + onPropertyValueFromCar(*updatedPropValue); + } +} + +std::vector<VehiclePropConfig> EmulatedVehicleServer::onGetAllPropertyConfig() const { + std::vector<VehiclePropConfig> vehiclePropConfigs; + constexpr size_t numOfVehiclePropConfigs = + sizeof(kVehicleProperties) / sizeof(kVehicleProperties[0]); + vehiclePropConfigs.reserve(numOfVehiclePropConfigs); + for (auto& it : kVehicleProperties) { + vehiclePropConfigs.emplace_back(it.config); + } + return vehiclePropConfigs; +} + +StatusCode EmulatedVehicleServer::handleGenerateFakeDataRequest(const VehiclePropValue& request) { + LOG(INFO) << __func__; + const auto& v = request.value; + if (!v.int32Values.size()) { + LOG(ERROR) << __func__ << ": expected at least \"command\" field in int32Values"; + return StatusCode::INVALID_ARG; + } + + FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]); + + switch (command) { + case FakeDataCommand::StartLinear: { + LOG(INFO) << __func__ << ", FakeDataCommand::StartLinear"; + if (v.int32Values.size() < 2) { + LOG(ERROR) << __func__ << ": expected property ID in int32Values"; + return StatusCode::INVALID_ARG; + } + if (!v.int64Values.size()) { + LOG(ERROR) << __func__ << ": interval is not provided in int64Values"; + return StatusCode::INVALID_ARG; + } + if (v.floatValues.size() < 3) { + LOG(ERROR) << __func__ << ": expected at least 3 elements in floatValues, got: " + << v.floatValues.size(); + return StatusCode::INVALID_ARG; + } + int32_t cookie = v.int32Values[1]; + getGenerator()->registerGenerator(cookie, + std::make_unique<LinearFakeValueGenerator>(request)); + break; + } + case FakeDataCommand::StartJson: { + LOG(INFO) << __func__ << ", FakeDataCommand::StartJson"; + if (v.stringValue.empty()) { + LOG(ERROR) << __func__ << ": path to JSON file is missing"; + return StatusCode::INVALID_ARG; + } + int32_t cookie = std::hash<std::string>()(v.stringValue); + getGenerator()->registerGenerator(cookie, + std::make_unique<JsonFakeValueGenerator>(request)); + break; + } + case FakeDataCommand::StopLinear: { + LOG(INFO) << __func__ << ", FakeDataCommand::StopLinear"; + if (v.int32Values.size() < 2) { + LOG(ERROR) << __func__ << ": expected property ID in int32Values"; + return StatusCode::INVALID_ARG; + } + int32_t cookie = v.int32Values[1]; + getGenerator()->unregisterGenerator(cookie); + break; + } + case FakeDataCommand::StopJson: { + LOG(INFO) << __func__ << ", FakeDataCommand::StopJson"; + if (v.stringValue.empty()) { + LOG(ERROR) << __func__ << ": path to JSON file is missing"; + return StatusCode::INVALID_ARG; + } + int32_t cookie = std::hash<std::string>()(v.stringValue); + getGenerator()->unregisterGenerator(cookie); + break; + } + case FakeDataCommand::KeyPress: { + LOG(INFO) << __func__ << ", FakeDataCommand::KeyPress"; + int32_t keyCode = request.value.int32Values[2]; + int32_t display = request.value.int32Values[3]; + // Send back to HAL + onPropertyValueFromCar( + *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display)); + onPropertyValueFromCar( + *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display)); + break; + } + default: { + LOG(ERROR) << __func__ << ": unexpected command: " << toInt(command); + return StatusCode::INVALID_ARG; + } + } + return StatusCode::OK; +} + +VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createApPowerStateReq( + VehicleApPowerStateReq state, int32_t param) { + auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2); + req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); + req->areaId = 0; + req->timestamp = elapsedRealtimeNano(); + req->status = VehiclePropertyStatus::AVAILABLE; + req->value.int32Values[0] = toInt(state); + req->value.int32Values[1] = param; + return req; +} + +VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createHwInputKeyProp( + VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) { + auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3); + keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT); + keyEvent->areaId = 0; + keyEvent->timestamp = elapsedRealtimeNano(); + keyEvent->status = VehiclePropertyStatus::AVAILABLE; + keyEvent->value.int32Values[0] = toInt(action); + keyEvent->value.int32Values[1] = keyCode; + keyEvent->value.int32Values[2] = targetDisplay; + return keyEvent; +} + +StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value) { + // Some properties need to be treated non-trivially + switch (value.prop) { + case AP_POWER_STATE_REPORT: + switch (value.value.int32Values[0]) { + case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT): + case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED): + case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL): + // CPMS is in WAIT_FOR_VHAL state, simply move to ON + // Send back to HAL + onPropertyValueFromCar( + *createApPowerStateReq(VehicleApPowerStateReq::ON, 0)); + break; + case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY): + case toInt(VehicleApPowerStateReport::SHUTDOWN_START): + // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command + // Send back to HAL + onPropertyValueFromCar( + *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0)); + break; + case toInt(VehicleApPowerStateReport::ON): + case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE): + case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE): + // Do nothing + break; + default: + // Unknown state + break; + } + break; + default: + break; + } + + // In the real vhal, the value will be sent to Car ECU. + // We just pretend it is done here. + return StatusCode::OK; +} + +StatusCode EmulatedVehicleServer::onSetPropertyFromVehicle(const VehiclePropValue& value) { + if (value.prop == kGenerateFakeDataControllingProperty) { + auto status = handleGenerateFakeDataRequest(value); + return status; + } else { + // Send back to HAL + onPropertyValueFromCar(value); + return StatusCode::OK; + } +} + +EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() { + return std::make_unique<EmulatedPassthroughConnector>(); +} + +} // namespace impl + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h new file mode 100644 index 0000000000..d424cd83e7 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h @@ -0,0 +1,110 @@ +/* + * 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_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ +#define android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ + +#include <vhal_v2_0/VehicleConnector.h> +#include <vhal_v2_0/VehicleHal.h> + +#include "GeneratorHub.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +namespace impl { + +// Extension of the client/server interfaces for emulated vehicle + +class EmulatedVehicleClient : public IVehicleClient { + public: + // Type of callback function for handling the new property values + using PropertyCallBackType = std::function<void(const VehiclePropValue&)>; + + // Method from IVehicleClient + void onPropertyValue(const VehiclePropValue& value) override; + + // Request to change the value on the VEHICLE side (for testing) + virtual StatusCode setPropertyFromVehicle(const VehiclePropValue& value) = 0; + + void registerPropertyValueCallback(PropertyCallBackType&& callback); + + private: + PropertyCallBackType mPropCallback; +}; + +class EmulatedVehicleServer : public IVehicleServer { + public: + // Methods from IVehicleServer + + std::vector<VehiclePropConfig> onGetAllPropertyConfig() const override; + + StatusCode onSetProperty(const VehiclePropValue& value) override; + + // Process the request to change the value on the VEHICLE side (for testing) + StatusCode onSetPropertyFromVehicle(const VehiclePropValue& value); + + // Set the Property Value Pool used in this server + void setValuePool(VehiclePropValuePool* valuePool); + + private: + GeneratorHub* getGenerator(); + + VehiclePropValuePool* getValuePool() const; + + void onFakeValueGenerated(const VehiclePropValue& value); + + StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request); + + VehicleHal::VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param); + + VehicleHal::VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, + int32_t keyCode, int32_t targetDisplay); + + // private data members + + GeneratorHub mGeneratorHub{ + std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)}; + + VehiclePropValuePool* mValuePool{nullptr}; +}; + +class EmulatedPassthroughConnector + : public IPassThroughConnector<EmulatedVehicleClient, EmulatedVehicleServer> { + public: + StatusCode setPropertyFromVehicle(const VehiclePropValue& value) override { + return this->onSetPropertyFromVehicle(value); + } +}; + +// Helper functions + +using EmulatedPassthroughConnectorPtr = std::unique_ptr<EmulatedPassthroughConnector>; + +EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector(); + +} // namespace impl + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ 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 e1da0301f9..6508efea2b 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 @@ -87,17 +87,19 @@ static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorInt return sensorStore; } -EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore) +EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, + EmulatedVehicleClient* client) : mPropStore(propStore), mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)), mRecurrentTimer( std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)), - mGeneratorHub( - std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1)) { + mVehicleClient(client) { initStaticConfig(); for (size_t i = 0; i < arraysize(kVehicleProperties); i++) { mPropStore->registerProperty(kVehicleProperties[i].config); } + mVehicleClient->registerPropertyValueCallback( + std::bind(&EmulatedVehicleHal::onPropertyValue, this, std::placeholders::_1)); } VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get( @@ -131,8 +133,39 @@ VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get( StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { static constexpr bool shouldUpdateStatus = false; + // set the value from vehcile side, used in end to end test. + if (propValue.prop == kSetIntPropertyFromVehcileForTest) { + auto mockValue = createVehiclePropValue(VehiclePropertyType::INT32, 1); + mockValue->prop = propValue.value.int32Values[0]; + mockValue->value.int32Values[0] = propValue.value.int32Values[1]; + mockValue->timestamp = propValue.value.int64Values[0]; + mockValue->areaId = propValue.areaId; + setPropertyFromVehicle(*mockValue); + return StatusCode::OK; + } + + if (propValue.prop == kSetFloatPropertyFromVehcileForTest) { + auto mockValue = createVehiclePropValue(VehiclePropertyType::FLOAT, 1); + mockValue->prop = propValue.value.int32Values[0]; + mockValue->value.floatValues[0] = propValue.value.floatValues[0]; + mockValue->timestamp = propValue.value.int64Values[0]; + mockValue->areaId = propValue.areaId; + setPropertyFromVehicle(*mockValue); + return StatusCode::OK; + } + if (propValue.prop == kSetBooleanPropertyFromVehcileForTest) { + auto mockValue = createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1); + mockValue->prop = propValue.value.int32Values[1]; + mockValue->value.int32Values[0] = propValue.value.int32Values[0]; + mockValue->timestamp = propValue.value.int64Values[0]; + mockValue->areaId = propValue.areaId; + setPropertyFromVehicle(*mockValue); + return StatusCode::OK; + } + if (propValue.prop == kGenerateFakeDataControllingProperty) { - StatusCode status = handleGenerateFakeDataRequest(propValue); + // send the generator controlling request to the server + auto status = mVehicleClient->setPropertyFromVehicle(propValue); if (status != StatusCode::OK) { return status; } @@ -156,29 +189,6 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { // Placeholder for future implementation of VMS property in the default hal. For // now, just returns OK; otherwise, hal clients crash with property not supported. return StatusCode::OK; - case AP_POWER_STATE_REPORT: - switch (propValue.value.int32Values[0]) { - case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT): - case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED): - case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL): - // CPMS is in WAIT_FOR_VHAL state, simply move to ON - doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::ON, 0)); - break; - case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY): - case toInt(VehicleApPowerStateReport::SHUTDOWN_START): - // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command - doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0)); - break; - case toInt(VehicleApPowerStateReport::ON): - case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE): - case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE): - // Do nothing - break; - default: - // Unknown state - break; - } - break; } } @@ -198,12 +208,25 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { return StatusCode::NOT_AVAILABLE; } - if (!mPropStore->writeValue(propValue, shouldUpdateStatus)) { - return StatusCode::INVALID_ARG; + /** + * After checking all conditions, such as the property is available, a real vhal will + * sent the events to Car ECU to take actions. + */ + VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(propValue); + updatedPropValue->timestamp = elapsedRealtimeNano(); + + // Send the value to the vehicle server, the server will talk to the (real or emulated) car + auto setValueStatus = mVehicleClient->setProperty(*updatedPropValue); + if (setValueStatus != StatusCode::OK) { + return setValueStatus; + } + + if (!mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) { + return StatusCode::INTERNAL_ERROR; } - getEmulatorOrDie()->doSetValueFromClient(propValue); - doHalEvent(getValuePool()->obtain(propValue)); + getEmulatorOrDie()->doSetValueFromClient(*updatedPropValue); + doHalEvent(std::move(updatedPropValue)); return StatusCode::OK; } @@ -324,144 +347,19 @@ bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const { } bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) { - static constexpr bool shouldUpdateStatus = true; - - if (propValue.prop == kGenerateFakeDataControllingProperty) { - StatusCode status = handleGenerateFakeDataRequest(propValue); - if (status != StatusCode::OK) { - return false; - } - } - - if (mPropStore->writeValue(propValue, shouldUpdateStatus)) { - doHalEvent(getValuePool()->obtain(propValue)); - return true; - } else { - return false; - } + return mVehicleClient->setPropertyFromVehicle(propValue) == StatusCode::OK; } std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const { return mPropStore->readAllValues(); } -StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) { - ALOGI("%s", __func__); - const auto& v = request.value; - if (!v.int32Values.size()) { - ALOGE("%s: expected at least \"command\" field in int32Values", __func__); - return StatusCode::INVALID_ARG; - } - - FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]); - - switch (command) { - case FakeDataCommand::StartLinear: { - ALOGI("%s, FakeDataCommand::StartLinear", __func__); - if (v.int32Values.size() < 2) { - ALOGE("%s: expected property ID in int32Values", __func__); - return StatusCode::INVALID_ARG; - } - if (!v.int64Values.size()) { - ALOGE("%s: interval is not provided in int64Values", __func__); - return StatusCode::INVALID_ARG; - } - if (v.floatValues.size() < 3) { - ALOGE("%s: expected at least 3 elements in floatValues, got: %zu", __func__, - v.floatValues.size()); - return StatusCode::INVALID_ARG; - } - int32_t cookie = v.int32Values[1]; - mGeneratorHub.registerGenerator(cookie, - std::make_unique<LinearFakeValueGenerator>(request)); - break; - } - case FakeDataCommand::StartJson: { - ALOGI("%s, FakeDataCommand::StartJson", __func__); - if (v.stringValue.empty()) { - ALOGE("%s: path to JSON file is missing", __func__); - return StatusCode::INVALID_ARG; - } - int32_t cookie = std::hash<std::string>()(v.stringValue); - mGeneratorHub.registerGenerator(cookie, - std::make_unique<JsonFakeValueGenerator>(request)); - break; - } - case FakeDataCommand::StopLinear: { - ALOGI("%s, FakeDataCommand::StopLinear", __func__); - if (v.int32Values.size() < 2) { - ALOGE("%s: expected property ID in int32Values", __func__); - return StatusCode::INVALID_ARG; - } - int32_t cookie = v.int32Values[1]; - mGeneratorHub.unregisterGenerator(cookie); - break; - } - case FakeDataCommand::StopJson: { - ALOGI("%s, FakeDataCommand::StopJson", __func__); - if (v.stringValue.empty()) { - ALOGE("%s: path to JSON file is missing", __func__); - return StatusCode::INVALID_ARG; - } - int32_t cookie = std::hash<std::string>()(v.stringValue); - mGeneratorHub.unregisterGenerator(cookie); - break; - } - case FakeDataCommand::KeyPress: { - ALOGI("%s, FakeDataCommand::KeyPress", __func__); - int32_t keyCode = request.value.int32Values[2]; - int32_t display = request.value.int32Values[3]; - doHalEvent( - createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display)); - doHalEvent(createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display)); - break; - } - default: { - ALOGE("%s: unexpected command: %d", __func__, command); - return StatusCode::INVALID_ARG; - } - } - return StatusCode::OK; -} - -VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createApPowerStateReq( - VehicleApPowerStateReq state, int32_t param) { - auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2); - req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); - req->areaId = 0; - req->timestamp = elapsedRealtimeNano(); - req->status = VehiclePropertyStatus::AVAILABLE; - req->value.int32Values[0] = toInt(state); - req->value.int32Values[1] = param; - return req; -} - -VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createHwInputKeyProp( - VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) { - auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3); - keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT); - keyEvent->areaId = 0; - keyEvent->timestamp = elapsedRealtimeNano(); - keyEvent->status = VehiclePropertyStatus::AVAILABLE; - keyEvent->value.int32Values[0] = toInt(action); - keyEvent->value.int32Values[1] = keyCode; - keyEvent->value.int32Values[2] = targetDisplay; - return keyEvent; -} - -void EmulatedVehicleHal::onFakeValueGenerated(const VehiclePropValue& value) { - ALOGD("%s: %s", __func__, toString(value).c_str()); - static constexpr bool shouldUpdateStatus = false; - +void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value) { + static constexpr bool shouldUpdateStatus = true; VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value); - if (updatedPropValue) { - updatedPropValue->timestamp = elapsedRealtimeNano(); - updatedPropValue->status = VehiclePropertyStatus::AVAILABLE; - mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus); - auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode; - if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) { - doHalEvent(std::move(updatedPropValue)); - } + + if (mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) { + doHalEvent(std::move(updatedPropValue)); } } diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h index 78895e3db2..98315ec32e 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h @@ -30,6 +30,7 @@ #include "vhal_v2_0/VehiclePropertyStore.h" #include "DefaultConfig.h" +#include "EmulatedVehicleConnector.h" #include "GeneratorHub.h" #include "VehicleEmulator.h" @@ -44,7 +45,8 @@ namespace impl { /** Implementation of VehicleHal that connected to emulator instead of real vehicle network. */ class EmulatedVehicleHal : public EmulatedVehicleHalIface { public: - EmulatedVehicleHal(VehiclePropertyStore* propStore); + EmulatedVehicleHal(VehiclePropertyStore* propStore, + EmulatedVehicleClient* client); ~EmulatedVehicleHal() = default; // Methods from VehicleHal @@ -66,10 +68,7 @@ private: } StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request); - void onFakeValueGenerated(const VehiclePropValue& value); - VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param); - VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode, - int32_t targetDisplay); + void onPropertyValue(const VehiclePropValue& value); void onContinuousPropertyTimer(const std::vector<int32_t>& properties); bool isContinuousProperty(int32_t propId) const; @@ -85,7 +84,7 @@ private: VehiclePropertyStore* mPropStore; std::unordered_set<int32_t> mHvacPowerProps; RecurrentTimer mRecurrentTimer; - GeneratorHub mGeneratorHub; + EmulatedVehicleClient* mVehicleClient; }; } // impl diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp index 9eb8894385..068333cf36 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/SocketComm.cpp @@ -92,7 +92,10 @@ bool SocketComm::listen() { } ALOGI("%s: Listening for connections on port %d", __FUNCTION__, DEBUG_SOCKET); - ::listen(mListenFd, 1); + if (::listen(mListenFd, 1) == -1) { + ALOGE("%s: Error on listening: errno: %d: %s", __FUNCTION__, errno, strerror(errno)); + return false; + } return true; } diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp index 6754843bf5..2eedecda2a 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp @@ -29,3 +29,64 @@ cc_library_static { ], srcs: ["VehicleHalProto.proto"] } + +cc_library_static { + name: "android.hardware.automotive.vehicle@2.0-grpc", + vendor: true, + include_dirs: [ + "external/protobuf/src", + ], + generated_headers: [ + "DefaultVehicleHalProtoStub_h", + ], + export_generated_headers: [ + "DefaultVehicleHalProtoStub_h", + ], + generated_sources: [ + "DefaultVehicleHalProtoStub_cc", + ], + shared_libs: [ + "libgrpc++_unsecure", + ], + cflags: [ + "-Wno-unused-parameter" + ], +} + +genrule { + name: "DefaultVehicleHalProtoStub_h", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + "VehicleHalProto.proto", + "VehicleServer.proto", + ], + out: [ + "VehicleHalProto.pb.h", + "VehicleHalProto.grpc.pb.h", + "VehicleServer.pb.h", + "VehicleServer.grpc.pb.h", + ], +} + +genrule { + name: "DefaultVehicleHalProtoStub_cc", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + "VehicleHalProto.proto", + "VehicleServer.proto", + ], + out: [ + "VehicleHalProto.pb.cc", + "VehicleHalProto.grpc.pb.cc", + "VehicleServer.pb.cc", + "VehicleServer.grpc.pb.cc", + ], +} diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto index 2ef64fbfab..04df5a8a21 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto @@ -15,7 +15,6 @@ */ syntax = "proto2"; -option optimize_for = LITE_RUNTIME; package emulator; diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto new file mode 100644 index 0000000000..7ce3c32273 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto @@ -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. + */ + +syntax = "proto2"; + +package emulator; + +import "google/protobuf/empty.proto"; +import "VehicleHalProto.proto"; + +// correspond to StatusCode defined in types.hal +enum VehicleHalStatusCode { + OK = 0; + TRY_AGAIN = 1; + INVALID_ARG = 2; + NOT_AVAILABLE = 3; + ACCESS_DENIED = 4; + INTERNAL_ERROR = 5; +} + +message VehicleHalCallStatus { + required VehicleHalStatusCode status_code = 1; +} + +service VehicleServer { + rpc GetAllPropertyConfig(google.protobuf.Empty) returns (stream VehiclePropConfig) {} + + // Change the property value of the vehicle + rpc SetProperty(VehiclePropValue) returns (VehicleHalCallStatus) {} + + // Start a vehicle property value stream + rpc StartPropertyValuesStream(google.protobuf.Empty) returns (stream VehiclePropValue) {} +} + diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal index 0f20dd18c0..279d5cd30a 100644 --- a/automotive/vehicle/2.0/types.hal +++ b/automotive/vehicle/2.0/types.hal @@ -35,6 +35,21 @@ enum VehiclePropertyType : int32_t { /** * 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, @@ -799,7 +814,7 @@ enum VehicleProperty : int32_t { | VehicleArea:SEAT), /** - * On/off defrost for designated window + * Fan-based defrost for designated window. * * @change_mode VehiclePropertyChangeMode:ON_CHANGE * @access VehiclePropertyAccess:READ_WRITE @@ -1076,6 +1091,7 @@ enum VehicleProperty : int32_t { * * @change_mode VehiclePropertyChangeMode:STATIC * @access VehiclePropertyAccess:READ + * @data_enum VehicleHvacFanDirection */ HVAC_FAN_DIRECTION_AVAILABLE = ( 0x0511 @@ -1118,6 +1134,18 @@ enum VehicleProperty : int32_t { | VehiclePropertyType:INT32 | VehicleArea:SEAT), + /** + * Electric defrosters' status + * + * @change_mode VehiclePropertyChangeMode:ON_CHANGE + * @access VehiclePropertyAccess:READ_WRITE + */ + HVAC_ELECTRIC_DEFROSTER_ON = ( + 0x0514 + | VehiclePropertyGroup:SYSTEM + | VehiclePropertyType:BOOLEAN + | VehicleArea:WINDOW), + /** * Distance units for display * @@ -2321,6 +2349,96 @@ enum VehicleProperty : int32_t { | VehiclePropertyType:INT32 | VehicleArea:SEAT), + /** + * Support customize permissions for vendor properties + * + * Implement this property if vehicle hal support customize vendor permissions feature. + * VehiclePropConfig.configArray is used to indicate vendor properties and permissions + * which selected for this vendor property. The permission must be one of enum in + * VehicleVendorPermission. + * The configArray is set as follows: + * configArray[n] = propId : property ID for the vendor property + * configArray[n+1] = one of enums in VehicleVendorPermission. It indicates the permission + * for reading value of the property. + * configArray[n+2] = one of enums in VehicleVendorPermission. It indicates the permission + * for writing value of the property. + * + * For example: + * configArray = { + * vendor_prop_1, PERMISSION_VENDOR_SEAT_READ, PERMISSION_VENDOR_SEAT_WRITE, + * vendor_prop_2, PERMISSION_VENDOR_INFO, PERMISSION_NOT_ACCESSIBLE, + * } + * If vendor properties are not in this array, they will have the default vendor permission. + * If vendor chose PERMISSION_NOT_ACCESSIBLE, android will not have access to the property. In + * the example, Android can not write value for vendor_prop_2. + * + * @change_mode VehiclePropertyChangeMode:STATIC + * @access VehiclePropertyAccess:READ + */ + SUPPORT_CUSTOMIZE_VENDOR_PERMISSION = ( + 0x0F05 + | VehiclePropertyGroup:SYSTEM + | VehiclePropertyType:BOOLEAN + | VehicleArea:GLOBAL), + +}; + +/** + * Used by SUPPORT_CUSTOMIZE_VENDOR_PERMISSION to indicate the permission of vendor properties. + */ +enum VehicleVendorPermission : int32_t { + PERMISSION_DEFAULT = 0x00000000, + + // permissions for the property related with window + PERMISSION_SET_VENDOR_CATEGORY_WINDOW= 0X00000001, + PERMISSION_GET_VENDOR_CATEGORY_WINDOW = 0x00000002, + // permissions for the property related with door + PERMISSION_SET_VENDOR_CATEGORY_DOOR = 0x00000003, + PERMISSION_GET_VENDOR_CATEGORY_DOOR = 0x00000004, + // permissions for the property related with seat + PERMISSION_SET_VENDOR_CATEGORY_SEAT = 0x00000005, + PERMISSION_GET_VENDOR_CATEGORY_SEAT = 0x00000006, + // permissions for the property related with mirror + PERMISSION_SET_VENDOR_CATEGORY_MIRROR= 0x00000007, + PERMISSION_GET_VENDOR_CATEGORY_MIRROR = 0x00000008, + + // permissions for the property related with car's information + PERMISSION_SET_VENDOR_CATEGORY_INFO = 0x00000009, + PERMISSION_GET_VENDOR_CATEGORY_INFO = 0x0000000A, + // permissions for the property related with car's engine + PERMISSION_SET_VENDOR_CATEGORY_ENGINE= 0x0000000B, + PERMISSION_GET_VENDOR_CATEGORY_ENGINE = 0x0000000C, + // permissions for the property related with car's HVAC + PERMISSION_SET_VENDOR_CATEGORY_HVAC = 0x0000000D, + PERMISSION_GET_VENDOR_CATEGORY_HVAC = 0x0000000E, + // permissions for the property related with car's light + PERMISSION_SET_VENDOR_CATEGORY_LIGHT = 0x0000000F, + PERMISSION_GET_VENDOR_CATEGORY_LIGHT = 0x00000010, + + // permissions reserved for other vendor permission + PERMISSION_SET_VENDOR_CATEGORY_1 = 0x00010000, + PERMISSION_GET_VENDOR_CATEGORY_1 = 0x00011000, + PERMISSION_SET_VENDOR_CATEGORY_2 = 0x00020000, + PERMISSION_GET_VENDOR_CATEGORY_2 = 0x00021000, + PERMISSION_SET_VENDOR_CATEGORY_3 = 0x00030000, + PERMISSION_GET_VENDOR_CATEGORY_3 = 0x00031000, + PERMISSION_SET_VENDOR_CATEGORY_4 = 0x00040000, + PERMISSION_GET_VENDOR_CATEGORY_4 = 0x00041000, + PERMISSION_SET_VENDOR_CATEGORY_5 = 0x00050000, + PERMISSION_GET_VENDOR_CATEGORY_5 = 0x00051000, + PERMISSION_SET_VENDOR_CATEGORY_6 = 0x00060000, + PERMISSION_GET_VENDOR_CATEGORY_6 = 0x00061000, + PERMISSION_SET_VENDOR_CATEGORY_7 = 0x00070000, + PERMISSION_GET_VENDOR_CATEGORY_7 = 0x00071000, + PERMISSION_SET_VENDOR_CATEGORY_8 = 0x00080000, + PERMISSION_GET_VENDOR_CATEGORY_8 = 0x00081000, + PERMISSION_SET_VENDOR_CATEGORY_9 = 0x00090000, + PERMISSION_GET_VENDOR_CATEGORY_9 = 0x00091000, + PERMISSION_SET_VENDOR_CATEGORY_10 = 0x000A0000, + PERMISSION_GET_VENDOR_CATEGORY_10 = 0x000A1000, + + // Indicate not available for android to access. + PERMISSION_NOT_ACCESSIBLE = 0xF0000000 }; /** @@ -2458,9 +2576,19 @@ enum FuelType : int32_t { * Bit flags for fan direction */ enum VehicleHvacFanDirection : int32_t { + UNKNOWN = 0x0, + FACE = 0x1, FLOOR = 0x2, + /** + * FACE_AND_FLOOR = FACE | FLOOR + */ + FACE_AND_FLOOR = 0x3, DEFROST = 0x4, + /** + * DEFROST_AND_FLOOR = DEFROST | FLOOR + */ + DEFROST_AND_FLOOR = 0x06, }; enum VehicleOilLevel : int32_t { @@ -2499,7 +2627,7 @@ enum VehicleApPowerStateReq : int32_t { * power controller must change power state to this state to shutdown * system. * - * int32Values[1] : one of enum_vehicle_ap_power_state_shutdown_param_type + * int32Values[1] : one of VehicleApPowerStateShutdownParam * * SHUTDOWN_PRPARE may be requested from either WAIT_FOR_VHAL or ON states. */ @@ -2531,6 +2659,11 @@ enum VehicleApPowerStateShutdownParam : int32_t { /** AP can only shutdown with postponing allowed. */ SHUTDOWN_ONLY = 3, + + /** + * AP may enter deep sleep, but must either sleep or shut down immediately. + * Postponing is not allowed. */ + SLEEP_IMMEDIATELY = 4, }; enum VehicleApPowerStateReport : int32_t { @@ -2723,6 +2856,8 @@ enum VehiclePropertyStatus : int32_t { * Various gears which can be selected by user and chosen in system. */ enum VehicleGear : int32_t { + GEAR_UNKNOWN = 0x0000, + GEAR_NEUTRAL = 0x0001, GEAR_REVERSE = 0x0002, GEAR_PARK = 0x0004, @@ -3541,4 +3676,3 @@ enum VmsAvailabilityStateIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex { enum VmsPublisherInformationIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex { PUBLISHER_ID = 1, }; - diff --git a/camera/common/1.0/default/Android.bp b/camera/common/1.0/default/Android.bp index 3e5c6d7812..f4390b2e30 100644 --- a/camera/common/1.0/default/Android.bp +++ b/camera/common/1.0/default/Android.bp @@ -21,6 +21,7 @@ cc_library_static { "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 b8c40e95b8..ac32c954c4 100644 --- a/camera/common/1.0/default/HandleImporter.cpp +++ b/camera/common/1.0/default/HandleImporter.cpp @@ -27,7 +27,9 @@ namespace helper { 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 @@ void HandleImporter::initializeLocked() { return; } + mMapperV4 = IMapperV4::getService(); + if (mMapperV4 != nullptr) { + mInitialized = true; + return; + } + mMapperV3 = IMapperV3::getService(); if (mMapperV3 != nullptr) { mInitialized = true; @@ -53,6 +61,7 @@ void HandleImporter::initializeLocked() { } void HandleImporter::cleanup() { + mMapperV4.clear(); mMapperV3.clear(); mMapperV2.clear(); mInitialized = false; @@ -151,6 +160,10 @@ bool HandleImporter::importBuffer(buffer_handle_t& handle) { initializeLocked(); } + if (mMapperV4 != nullptr) { + return importBufferInternal<IMapperV4, MapperErrorV4>(mMapperV4, handle); + } + if (mMapperV3 != nullptr) { return importBufferInternal<IMapperV3, MapperErrorV3>(mMapperV3, handle); } @@ -159,7 +172,7 @@ bool HandleImporter::importBuffer(buffer_handle_t& handle) { 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 @@ void HandleImporter::freeBuffer(buffer_handle_t handle) { } 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,26 @@ void* HandleImporter::lock( 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) { + 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 +299,16 @@ YCbCrLayout HandleImporter::lockYCbCr( initializeLocked(); } + if (mMapperV4 != nullptr) { + // No device currently supports IMapper 4.0 so it is safe to just return an error code here. + // + // This will be supported by a combination of lock and BufferMetadata getters. We are going + // to refactor all the IAllocator/IMapper versioning code into a shared library. We will + // then add the IMapper 4.0 lockYCbCr support then. + ALOGE("%s: MapperV4 doesn't support lockYCbCr directly!", __FUNCTION__); + return {}; + } + if (mMapperV3 != nullptr) { return lockYCbCrInternal<IMapperV3, MapperErrorV3>( mMapperV3, buf, cpuUsage, accessRegion); @@ -279,11 +319,14 @@ YCbCrLayout HandleImporter::lockYCbCr( 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 +334,7 @@ int HandleImporter::unlock(buffer_handle_t& buf) { 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 a93d4554ad..fc2bbd1197 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 @@ private: 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 97d0b5f1d5..e6e64855b8 100644 --- a/camera/device/1.0/default/Android.bp +++ b/camera/device/1.0/default/Android.bp @@ -14,6 +14,7 @@ cc_library_shared { "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 e4d9e85b44..878878d3b6 100644 --- a/camera/device/3.2/default/Android.bp +++ b/camera/device/3.2/default/Android.bp @@ -13,6 +13,7 @@ cc_library_shared { "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 d964f3df3e..7d514345ba 100644 --- a/camera/device/3.3/default/Android.bp +++ b/camera/device/3.3/default/Android.bp @@ -15,6 +15,7 @@ cc_library_shared { "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 71a9e77063..59e8329186 100644 --- a/camera/device/3.4/default/Android.bp +++ b/camera/device/3.4/default/Android.bp @@ -48,6 +48,7 @@ cc_library_shared { "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", @@ -84,6 +85,7 @@ cc_library_shared { "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 43362fd1a9..1c307eeb5f 100644 --- a/camera/device/3.5/default/Android.bp +++ b/camera/device/3.5/default/Android.bp @@ -49,6 +49,7 @@ cc_library_shared { "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", @@ -81,7 +82,8 @@ cc_library_shared { "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/device/3.5/types.hal b/camera/device/3.5/types.hal index 6d861e2e72..38493b4f72 100644 --- a/camera/device/3.5/types.hal +++ b/camera/device/3.5/types.hal @@ -23,7 +23,8 @@ import @3.2::CameraBlobId; /** * If the result metadata cannot be produced for a physical camera device part of a logical * multi-camera, then HAL must invoke the notification callback and pass a message with ERROR_RESULT - * code and errorStreamId that contains the stream id associated with that physical device. + * code and errorStreamId that contains the stream id associated with that physical device. Such + * callback must be made before the final processCaptureResult() call for the corresponding request. * The behavior during absent result metadata remains unchanged for a logical or a non-logical * camera device and the errorStreamId must be set to -1. */ diff --git a/camera/metadata/3.5/Android.bp b/camera/metadata/3.5/Android.bp new file mode 100644 index 0000000000..4ebd069d3a --- /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 0000000000..b9451c852b --- /dev/null +++ b/camera/metadata/3.5/types.hal @@ -0,0 +1,74 @@ +/* + * 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 + +/** + * Main enumeration for defining camera metadata tags added in this revision + * + * <p>Partial documentation is included for each tag; for complete documentation, reference + * '/system/media/camera/docs/docs.html' in the corresponding Android source tree.</p> + */ +enum CameraMetadataTag : @3.4::CameraMetadataTag { + /** android.control.availableBokehCapabilities [static, int32[], public] + * + * <p>The list of bokeh modes that are supported by this camera device, and each bokeh mode's + * maximum streaming (non-stall) size with bokeh effect.</p> + */ + ANDROID_CONTROL_AVAILABLE_BOKEH_CAPABILITIES = android.hardware.camera.metadata@3.3::CameraMetadataTag:ANDROID_CONTROL_END_3_3, + + /** android.control.bokehMode [dynamic, enum, public] + * + * <p>Whether bokeh mode is enabled for a particular capture request.</p> + */ + ANDROID_CONTROL_BOKEH_MODE, + + ANDROID_CONTROL_END_3_5, + +}; + +/* + * Enumeration definitions for the various entries that need them + */ + +/** android.control.bokehMode enumeration values + * @see ANDROID_CONTROL_BOKEH_MODE + */ +enum CameraMetadataEnumAndroidControlBokehMode : uint32_t { + ANDROID_CONTROL_BOKEH_MODE_OFF, + ANDROID_CONTROL_BOKEH_MODE_STILL_CAPTURE, + ANDROID_CONTROL_BOKEH_MODE_CONTINUOUS, +}; + +/** 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 95e27fd8c1..9203b8d585 100644 --- a/camera/provider/2.4/default/Android.bp +++ b/camera/provider/2.4/default/Android.bp @@ -13,6 +13,7 @@ cc_library_shared { "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", @@ -51,6 +52,7 @@ cc_library_shared { "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", @@ -93,6 +95,8 @@ cc_library_shared { "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", @@ -137,6 +141,8 @@ cc_defaults { "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 2c3ed37a6a..5fe7b197d0 100644 --- a/camera/provider/2.4/vts/functional/Android.bp +++ b/camera/provider/2.4/vts/functional/Android.bp @@ -43,9 +43,11 @@ cc_test { "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 a5369e7b8d..6505440a26 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> @@ -622,7 +624,7 @@ public: Return<void> returnStreamBuffers(const hidl_vec<StreamBuffer>& buffers) override; - void setCurrentStreamConfig(const hidl_vec<V3_2::Stream>& streams, + void setCurrentStreamConfig(const hidl_vec<V3_4::Stream>& streams, const hidl_vec<V3_2::HalStream>& halStreams); void waitForBuffersReturned(); @@ -639,7 +641,7 @@ public: /* members for requestStreamBuffers() and returnStreamBuffers()*/ std::mutex mLock; // protecting members below bool mUseHalBufManager = false; - hidl_vec<V3_2::Stream> mStreams; + hidl_vec<V3_4::Stream> mStreams; hidl_vec<V3_2::HalStream> mHalStreams; uint64_t mNextBufferId = 1; using OutstandingBuffers = std::unordered_map<uint64_t, hidl_handle>; @@ -776,6 +778,7 @@ public: const CameraMetadata& chars, int deviceVersion, const hidl_vec<hidl_string>& deviceNames); void verifyCameraCharacteristics(Status status, const CameraMetadata& chars); + void verifyBokehCharacteristics(const camera_metadata_t* metadata); void verifyRecommendedConfigs(const CameraMetadata& metadata); void verifyMonochromeCharacteristics(const CameraMetadata& chars, int deviceVersion); void verifyMonochromeCameraResult( @@ -799,7 +802,7 @@ public: bool isDepthOnly(camera_metadata_t* staticMeta); - static Status getAvailableOutputStreams(camera_metadata_t *staticMeta, + static Status getAvailableOutputStreams(const camera_metadata_t *staticMeta, std::vector<AvailableStream> &outputStreams, const AvailableStream *threshold = nullptr); static Status getJpegBufferSize(camera_metadata_t *staticMeta, @@ -865,6 +868,8 @@ protected: int32_t partialResultCount; // For buffer drop errors, the stream ID for the stream that lost a buffer. + // For physical sub-camera result errors, the Id of the physical stream + // for the physical sub-camera. // Otherwise -1. int32_t errorStreamId; @@ -878,6 +883,8 @@ protected: // return from HAL but framework. ::android::Vector<StreamBuffer> resultOutputBuffers; + std::unordered_set<string> expectedPhysicalResults; + InFlightRequest() : shutterTimestamp(0), errorCodeValid(false), @@ -907,6 +914,24 @@ protected: partialResultCount(0), errorStreamId(-1), hasInputBuffer(hasInput) {} + + InFlightRequest(ssize_t numBuffers, bool hasInput, + bool partialResults, uint32_t partialCount, + const std::unordered_set<string>& extraPhysicalResult, + std::shared_ptr<ResultMetadataQueue> queue = nullptr) : + shutterTimestamp(0), + errorCodeValid(false), + errorCode(ErrorCode::ERROR_BUFFER), + usePartialResult(partialResults), + numPartialResults(partialCount), + resultQueue(queue), + haveResultMetadata(false), + numBuffersLeft(numBuffers), + frameNumber(0), + partialResultCount(0), + errorStreamId(-1), + hasInputBuffer(hasInput), + expectedPhysicalResults(extraPhysicalResult) {} }; // Map from frame number to the in-flight request state @@ -1124,6 +1149,13 @@ bool CameraHidlTest::DeviceCb::processCaptureResultLocked(const CaptureResult& r return notify; } + if (physicalCameraMetadata.size() != request->expectedPhysicalResults.size()) { + ALOGE("%s: Frame %d: Returned physical metadata count %zu " + "must be equal to expected count %zu", __func__, frameNumber, + physicalCameraMetadata.size(), request->expectedPhysicalResults.size()); + ADD_FAILURE(); + return notify; + } std::vector<::android::hardware::camera::device::V3_2::CameraMetadata> physResultMetadata; physResultMetadata.resize(physicalCameraMetadata.size()); for (size_t i = 0; i < physicalCameraMetadata.size(); i++) { @@ -1251,11 +1283,11 @@ bool CameraHidlTest::DeviceCb::processCaptureResultLocked(const CaptureResult& r } void CameraHidlTest::DeviceCb::setCurrentStreamConfig( - const hidl_vec<V3_2::Stream>& streams, const hidl_vec<V3_2::HalStream>& halStreams) { + const hidl_vec<V3_4::Stream>& streams, const hidl_vec<V3_2::HalStream>& halStreams) { ASSERT_EQ(streams.size(), halStreams.size()); ASSERT_NE(streams.size(), 0); for (size_t i = 0; i < streams.size(); i++) { - ASSERT_EQ(streams[i].id, halStreams[i].id); + ASSERT_EQ(streams[i].v3_2.id, halStreams[i].id); } std::lock_guard<std::mutex> l(mLock); mUseHalBufManager = true; @@ -1293,16 +1325,6 @@ Return<void> CameraHidlTest::DeviceCb::notify( std::lock_guard<std::mutex> l(mParent->mLock); for (size_t i = 0; i < messages.size(); i++) { - ssize_t idx = mParent->mInflightMap.indexOfKey( - messages[i].msg.shutter.frameNumber); - if (::android::NAME_NOT_FOUND == idx) { - ALOGE("%s: Unexpected frame number! received: %u", - __func__, messages[i].msg.shutter.frameNumber); - ADD_FAILURE(); - break; - } - InFlightRequest *r = mParent->mInflightMap.editValueAt(idx); - switch(messages[i].type) { case MsgType::ERROR: if (ErrorCode::ERROR_DEVICE == messages[i].msg.error.errorCode) { @@ -1310,13 +1332,59 @@ Return<void> CameraHidlTest::DeviceCb::notify( __func__); ADD_FAILURE(); } else { - r->errorCodeValid = true; - r->errorCode = messages[i].msg.error.errorCode; - r->errorStreamId = messages[i].msg.error.errorStreamId; + ssize_t idx = mParent->mInflightMap.indexOfKey( + messages[i].msg.error.frameNumber); + if (::android::NAME_NOT_FOUND == idx) { + ALOGE("%s: Unexpected error frame number! received: %u", + __func__, messages[i].msg.error.frameNumber); + ADD_FAILURE(); + break; + } + InFlightRequest *r = mParent->mInflightMap.editValueAt(idx); + + if (ErrorCode::ERROR_RESULT == messages[i].msg.error.errorCode && + messages[i].msg.error.errorStreamId != -1) { + if (r->haveResultMetadata) { + ALOGE("%s: Camera must report physical camera result error before " + "the final capture result!", __func__); + ADD_FAILURE(); + } else { + for (size_t j = 0; j < mStreams.size(); j++) { + if (mStreams[j].v3_2.id == messages[i].msg.error.errorStreamId) { + hidl_string physicalCameraId = mStreams[j].physicalCameraId; + bool idExpected = r->expectedPhysicalResults.find( + physicalCameraId) != r->expectedPhysicalResults.end(); + if (!idExpected) { + ALOGE("%s: ERROR_RESULT's error stream's physicalCameraId " + "%s must be expected", __func__, + physicalCameraId.c_str()); + ADD_FAILURE(); + } else { + r->expectedPhysicalResults.erase(physicalCameraId); + } + break; + } + } + } + } else { + r->errorCodeValid = true; + r->errorCode = messages[i].msg.error.errorCode; + r->errorStreamId = messages[i].msg.error.errorStreamId; + } } break; case MsgType::SHUTTER: + { + ssize_t idx = mParent->mInflightMap.indexOfKey(messages[i].msg.shutter.frameNumber); + if (::android::NAME_NOT_FOUND == idx) { + ALOGE("%s: Unexpected shutter frame number! received: %u", + __func__, messages[i].msg.shutter.frameNumber); + ADD_FAILURE(); + break; + } + InFlightRequest *r = mParent->mInflightMap.editValueAt(idx); r->shutterTimestamp = messages[i].msg.shutter.timestamp; + } break; default: ALOGE("%s: Unsupported notify message %d", __func__, @@ -1357,7 +1425,7 @@ Return<void> CameraHidlTest::DeviceCb::requestStreamBuffers( for (size_t i = 0; i < bufReqs.size(); i++) { bool found = false; for (size_t idx = 0; idx < mStreams.size(); idx++) { - if (bufReqs[i].streamId == mStreams[idx].id) { + if (bufReqs[i].streamId == mStreams[idx].v3_2.id) { found = true; indexes[i] = idx; break; @@ -1381,7 +1449,7 @@ Return<void> CameraHidlTest::DeviceCb::requestStreamBuffers( const auto& halStream = mHalStreams[idx]; const V3_5::BufferRequest& bufReq = bufReqs[i]; if (mOutstandingBufferIds[idx].size() + bufReq.numBuffersRequested > halStream.maxBuffers) { - bufRets[i].streamId = stream.id; + bufRets[i].streamId = stream.v3_2.id; bufRets[i].val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED); allStreamOk = false; continue; @@ -1390,17 +1458,17 @@ Return<void> CameraHidlTest::DeviceCb::requestStreamBuffers( hidl_vec<StreamBuffer> tmpRetBuffers(bufReq.numBuffersRequested); for (size_t j = 0; j < bufReq.numBuffersRequested; j++) { hidl_handle buffer_handle; - mParent->allocateGraphicBuffer(stream.width, stream.height, + mParent->allocateGraphicBuffer(stream.v3_2.width, stream.v3_2.height, android_convertGralloc1To0Usage( halStream.producerUsage, halStream.consumerUsage), halStream.overrideFormat, &buffer_handle); - tmpRetBuffers[j] = {stream.id, mNextBufferId, buffer_handle, BufferStatus::OK, + tmpRetBuffers[j] = {stream.v3_2.id, mNextBufferId, buffer_handle, BufferStatus::OK, nullptr, nullptr}; mOutstandingBufferIds[idx].insert(std::make_pair(mNextBufferId++, buffer_handle)); } atLeastOneStreamOk = true; - bufRets[i].streamId = stream.id; + bufRets[i].streamId = stream.v3_2.id; bufRets[i].val.buffers(std::move(tmpRetBuffers)); } @@ -1426,11 +1494,11 @@ Return<void> CameraHidlTest::DeviceCb::returnStreamBuffers( ADD_FAILURE(); } - std::lock_guard<std::mutex> l(mLock); + std::unique_lock<std::mutex> l(mLock); for (const auto& buf : buffers) { bool found = false; for (size_t idx = 0; idx < mOutstandingBufferIds.size(); idx++) { - if (mStreams[idx].id == buf.streamId && + if (mStreams[idx].v3_2.id == buf.streamId && mOutstandingBufferIds[idx].count(buf.bufferId) == 1) { mOutstandingBufferIds[idx].erase(buf.bufferId); // TODO: check do we need to close/delete native handle or assume we have enough @@ -1446,6 +1514,10 @@ Return<void> CameraHidlTest::DeviceCb::returnStreamBuffers( ALOGE("%s: unknown buffer ID %" PRIu64, __FUNCTION__, buf.bufferId); ADD_FAILURE(); } + if (!hasOutstandingBuffersLocked()) { + l.unlock(); + mFlushedCondition.notify_one(); + } return Void(); } @@ -4157,7 +4229,7 @@ TEST_F(CameraHidlTest, processMultiCaptureRequestPreview) { ASSERT_TRUE(resultQueueRet.isOk()); InFlightRequest inflightReq = {static_cast<ssize_t> (halStreamConfig.streams.size()), false, - supportsPartialResults, partialResultCount, resultQueue}; + supportsPartialResults, partialResultCount, physicalIds, resultQueue}; std::vector<hidl_handle> graphicBuffers; graphicBuffers.reserve(halStreamConfig.streams.size()); @@ -4236,7 +4308,7 @@ TEST_F(CameraHidlTest, processMultiCaptureRequestPreview) { request.v3_2.outputBuffers[0].buffer = nullptr; mInflightMap.clear(); inflightReq = {static_cast<ssize_t> (physicalIds.size()), false, - supportsPartialResults, partialResultCount, resultQueue}; + supportsPartialResults, partialResultCount, physicalIds, resultQueue}; mInflightMap.add(request.v3_2.frameNumber, &inflightReq); } @@ -4789,7 +4861,7 @@ TEST_F(CameraHidlTest, providerDeviceStateNotification) { // Retrieve all valid output stream resolutions from the camera // static characteristics. -Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta, +Status CameraHidlTest::getAvailableOutputStreams(const camera_metadata_t *staticMeta, std::vector<AvailableStream> &outputStreams, const AvailableStream *threshold) { AvailableStream depthPreviewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, @@ -5315,10 +5387,10 @@ void CameraHidlTest::configurePreviewStreams3_4(const std::string &name, int32_t ASSERT_EQ(physicalIds.size(), halConfig.streams.size()); *halStreamConfig = halConfig; if (*useHalBufManager) { - hidl_vec<V3_2::Stream> streams(physicalIds.size()); + hidl_vec<V3_4::Stream> streams(physicalIds.size()); hidl_vec<V3_2::HalStream> halStreams(physicalIds.size()); for (size_t i = 0; i < physicalIds.size(); i++) { - streams[i] = streams3_4[i].v3_2; + streams[i] = streams3_4[i]; halStreams[i] = halConfig.streams[i].v3_3.v3_2; } cb->setCurrentStreamConfig(streams, halStreams); @@ -5493,9 +5565,9 @@ void CameraHidlTest::configurePreviewStream(const std::string &name, int32_t dev halStreamConfig->streams.resize(1); halStreamConfig->streams[0] = halConfig.streams[0].v3_3.v3_2; if (*useHalBufManager) { - hidl_vec<V3_2::Stream> streams(1); + hidl_vec<V3_4::Stream> streams(1); hidl_vec<V3_2::HalStream> halStreams(1); - streams[0] = stream3_2; + streams[0] = config3_4.streams[0]; halStreams[0] = halConfig.streams[0].v3_3.v3_2; cb->setCurrentStreamConfig(streams, halStreams); } @@ -5772,6 +5844,101 @@ void CameraHidlTest::verifyCameraCharacteristics(Status status, const CameraMeta ADD_FAILURE() << "Get Heic maxJpegAppSegmentsCount failed!"; } } + + verifyBokehCharacteristics(metadata); +} + +void CameraHidlTest::verifyBokehCharacteristics(const camera_metadata_t* metadata) { + camera_metadata_ro_entry entry; + int retcode = 0; + + // Check key availability in capabilities, request and result. + + retcode = find_camera_metadata_ro_entry(metadata, + ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, &entry); + bool hasBokehRequestKey = false; + if ((0 == retcode) && (entry.count > 0)) { + hasBokehRequestKey = std::find(entry.data.i32, entry.data.i32+entry.count, + ANDROID_CONTROL_BOKEH_MODE) != entry.data.i32+entry.count; + } else { + ADD_FAILURE() << "Get camera availableRequestKeys failed!"; + } + + retcode = find_camera_metadata_ro_entry(metadata, + ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, &entry); + bool hasBokehResultKey = false; + if ((0 == retcode) && (entry.count > 0)) { + hasBokehResultKey = std::find(entry.data.i32, entry.data.i32+entry.count, + ANDROID_CONTROL_BOKEH_MODE) != entry.data.i32+entry.count; + } else { + ADD_FAILURE() << "Get camera availableResultKeys failed!"; + } + + retcode = find_camera_metadata_ro_entry(metadata, + ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, &entry); + bool hasBokehCharacteristicsKey = false; + if ((0 == retcode) && (entry.count > 0)) { + hasBokehCharacteristicsKey = std::find(entry.data.i32, entry.data.i32+entry.count, + ANDROID_CONTROL_AVAILABLE_BOKEH_CAPABILITIES) != entry.data.i32+entry.count; + } else { + ADD_FAILURE() << "Get camera availableCharacteristicsKeys failed!"; + } + retcode = find_camera_metadata_ro_entry(metadata, + ANDROID_CONTROL_AVAILABLE_BOKEH_CAPABILITIES, &entry); + bool hasAvailableBokehCaps = (0 == retcode && entry.count > 0); + + // Bokeh keys must all be available, or all be unavailable. + bool noBokeh = !hasBokehRequestKey && !hasBokehResultKey && !hasBokehCharacteristicsKey && + !hasAvailableBokehCaps; + if (noBokeh) { + return; + } + bool hasBokeh = hasBokehRequestKey && hasBokehResultKey && hasBokehCharacteristicsKey && + hasAvailableBokehCaps; + ASSERT_TRUE(hasBokeh); + + // Must have OFF, and must have one of STILL_CAPTURE and CONTINUOUS. + ASSERT_TRUE(entry.count == 6 || entry.count == 9); + bool hasOffMode = false; + bool hasStillCaptureMode = false; + bool hasContinuousMode = false; + std::vector<AvailableStream> outputStreams; + ASSERT_EQ(Status::OK, getAvailableOutputStreams(metadata, outputStreams)); + for (int i = 0; i < entry.count; i += 3) { + int32_t mode = entry.data.i32[i]; + int32_t maxWidth = entry.data.i32[i+1]; + int32_t maxHeight = entry.data.i32[i+2]; + switch (mode) { + case ANDROID_CONTROL_BOKEH_MODE_OFF: + hasOffMode = true; + ASSERT_TRUE(maxWidth == 0 && maxHeight == 0); + break; + case ANDROID_CONTROL_BOKEH_MODE_STILL_CAPTURE: + hasStillCaptureMode = true; + break; + case ANDROID_CONTROL_BOKEH_MODE_CONTINUOUS: + hasContinuousMode = true; + break; + default: + ADD_FAILURE() << "Invalid bokehMode advertised: " << mode; + break; + } + + if (mode != ANDROID_CONTROL_BOKEH_MODE_OFF) { + bool sizeSupported = false; + for (const auto& stream : outputStreams) { + if ((stream.format == static_cast<int32_t>(PixelFormat::YCBCR_420_888) || + stream.format == static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)) + && stream.width == maxWidth && stream.height == maxHeight) { + sizeSupported = true; + break; + } + } + ASSERT_TRUE(sizeSupported); + } + } + ASSERT_TRUE(hasOffMode); + ASSERT_TRUE(hasStillCaptureMode || hasContinuousMode); } void CameraHidlTest::verifyMonochromeCharacteristics(const CameraMetadata& chars, @@ -6148,13 +6315,46 @@ void CameraHidlTest::allocateGraphicBuffer(uint32_t width, uint32_t height, uint 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::hidl_vec<uint8_t> descriptor; + android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo descriptorInfo{}; + descriptorInfo.name = "VtsHalCameraProviderV2_4"; + 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<uint8_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::hidl_vec<uint32_t> descriptor; android::hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo descriptorInfo {}; descriptorInfo.width = width; descriptorInfo.height = height; @@ -6180,6 +6380,7 @@ void CameraHidlTest::allocateGraphicBuffer(uint32_t width, uint32_t height, uint }); ASSERT_TRUE(ret.isOk()); } else { + ::android::hardware::hidl_vec<uint32_t> descriptor; ASSERT_NE(mapper.get(), nullptr); ASSERT_NE(allocator.get(), nullptr); android::hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo descriptorInfo {}; diff --git a/cas/1.0/vts/functional/Android.bp b/cas/1.0/vts/functional/Android.bp index 622baa5988..ab39c0e93b 100644 --- a/cas/1.0/vts/functional/Android.bp +++ b/cas/1.0/vts/functional/Android.bp @@ -29,6 +29,6 @@ cc_test { shared_libs: [ "libbinder", ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts-core"], } diff --git a/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp b/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp index 14b8bbdd5a..0f16de5e90 100644 --- a/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp +++ b/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp @@ -16,8 +16,6 @@ #define LOG_TAG "mediacas_hidl_hal_test" -#include <VtsHalHidlTargetTestBase.h> -#include <VtsHalHidlTargetTestEnvBase.h> #include <android-base/logging.h> #include <android/hardware/cas/1.0/ICas.h> #include <android/hardware/cas/1.0/ICasListener.h> @@ -27,8 +25,11 @@ #include <android/hardware/cas/native/1.0/IDescrambler.h> #include <android/hardware/cas/native/1.0/types.h> #include <binder/MemoryDealer.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> #include <hidl/HidlSupport.h> #include <hidl/HidlTransportSupport.h> +#include <hidl/ServiceManagement.h> #include <hidl/Status.h> #include <hidlmemory/FrameworkUtils.h> #include <utils/Condition.h> @@ -208,29 +209,16 @@ void MediaCasListener::testEventEcho(sp<ICas>& mediaCas, int32_t& event, int32_t EXPECT_TRUE(mEventData == eventData); } -// Test environment for Cas HIDL HAL. -class CasHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { - public: - // get the test environment singleton - static CasHidlEnvironment* Instance() { - static CasHidlEnvironment* instance = new CasHidlEnvironment; - return instance; - } - - virtual void registerTestServices() override { registerTestService<IMediaCasService>(); } -}; - -class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase { - public: +class MediaCasHidlTest : public testing::TestWithParam<std::string> { + public: virtual void SetUp() override { - mService = ::testing::VtsHalHidlTargetTestBase::getService<IMediaCasService>( - CasHidlEnvironment::Instance()->getServiceName<IMediaCasService>()); + mService = IMediaCasService::getService(GetParam()); ASSERT_NE(mService, nullptr); } - sp<IMediaCasService> mService; + sp<IMediaCasService> mService = nullptr; - protected: + protected: static void description(const std::string& description) { RecordProperty("description", description); } @@ -325,7 +313,7 @@ class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase { return ::testing::AssertionFailure(); } - uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mem->pointer())); + uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mem->unsecurePointer())); memcpy(ipBuffer, kInBinaryBuffer, sizeof(kInBinaryBuffer)); // hidlMemory is not to be passed out of scope! @@ -419,7 +407,7 @@ class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase { return ::testing::AssertionResult(returnVoid.isOk()); } -TEST_F(MediaCasHidlTest, EnumeratePlugins) { +TEST_P(MediaCasHidlTest, EnumeratePlugins) { description("Test enumerate plugins"); hidl_vec<HidlCasPluginDescriptor> descriptors; EXPECT_TRUE(mService @@ -440,7 +428,7 @@ TEST_F(MediaCasHidlTest, EnumeratePlugins) { } } -TEST_F(MediaCasHidlTest, TestInvalidSystemIdFails) { +TEST_P(MediaCasHidlTest, TestInvalidSystemIdFails) { description("Test failure for invalid system ID"); sp<MediaCasListener> casListener = new MediaCasListener(); @@ -458,7 +446,7 @@ TEST_F(MediaCasHidlTest, TestInvalidSystemIdFails) { EXPECT_EQ(descramblerBase, nullptr); } -TEST_F(MediaCasHidlTest, TestClearKeyPluginInstalled) { +TEST_P(MediaCasHidlTest, TestClearKeyPluginInstalled) { description("Test if ClearKey plugin is installed"); hidl_vec<HidlCasPluginDescriptor> descriptors; EXPECT_TRUE(mService @@ -480,7 +468,7 @@ TEST_F(MediaCasHidlTest, TestClearKeyPluginInstalled) { ASSERT_TRUE(false) << "ClearKey plugin not installed"; } -TEST_F(MediaCasHidlTest, TestClearKeyApis) { +TEST_P(MediaCasHidlTest, TestClearKeyApis) { description("Test that valid call sequences succeed"); ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); @@ -568,7 +556,7 @@ TEST_F(MediaCasHidlTest, TestClearKeyApis) { EXPECT_EQ(Status::OK, descrambleStatus); ASSERT_NE(nullptr, dataMemory.get()); - uint8_t* opBuffer = static_cast<uint8_t*>(static_cast<void*>(dataMemory->pointer())); + uint8_t* opBuffer = static_cast<uint8_t*>(static_cast<void*>(dataMemory->unsecurePointer())); int compareResult = memcmp(static_cast<const void*>(opBuffer), static_cast<const void*>(kOutRefBinaryBuffer), @@ -584,7 +572,7 @@ TEST_F(MediaCasHidlTest, TestClearKeyApis) { EXPECT_EQ(Status::OK, returnStatus); } -TEST_F(MediaCasHidlTest, TestClearKeySessionClosedAfterRelease) { +TEST_P(MediaCasHidlTest, TestClearKeySessionClosedAfterRelease) { description("Test that all sessions are closed after a MediaCas object is released"); ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); @@ -611,7 +599,7 @@ TEST_F(MediaCasHidlTest, TestClearKeySessionClosedAfterRelease) { EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus); } -TEST_F(MediaCasHidlTest, TestClearKeyErrors) { +TEST_P(MediaCasHidlTest, TestClearKeyErrors) { description("Test that invalid call sequences fail with expected error codes"); ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); @@ -700,7 +688,7 @@ TEST_F(MediaCasHidlTest, TestClearKeyErrors) { EXPECT_FALSE(mDescramblerBase->requiresSecureDecoderComponent("bad")); } -TEST_F(MediaCasHidlTest, TestClearKeyOobFails) { +TEST_P(MediaCasHidlTest, TestClearKeyOobFails) { description("Test that oob descramble request fails with expected error"); ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); @@ -849,11 +837,7 @@ TEST_F(MediaCasHidlTest, TestClearKeyOobFails) { } // anonymous namespace -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(CasHidlEnvironment::Instance()); - ::testing::InitGoogleTest(&argc, argv); - CasHidlEnvironment::Instance()->init(&argc, argv); - int status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - return status; -} +INSTANTIATE_TEST_SUITE_P( + PerInstance, MediaCasHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IMediaCasService::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/cas/1.1/vts/functional/Android.bp b/cas/1.1/vts/functional/Android.bp index 8afd19abda..9e8eb52efe 100644 --- a/cas/1.1/vts/functional/Android.bp +++ b/cas/1.1/vts/functional/Android.bp @@ -30,6 +30,6 @@ cc_test { shared_libs: [ "libbinder", ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts-core"], } diff --git a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp index 88f1fb01d6..7e5a61a6ff 100644 --- a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp +++ b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp @@ -27,8 +27,11 @@ #include <android/hardware/cas/native/1.0/IDescrambler.h> #include <android/hardware/cas/native/1.0/types.h> #include <binder/MemoryDealer.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> #include <hidl/HidlSupport.h> #include <hidl/HidlTransportSupport.h> +#include <hidl/ServiceManagement.h> #include <hidl/Status.h> #include <hidlmemory/FrameworkUtils.h> #include <utils/Condition.h> @@ -251,27 +254,14 @@ void MediaCasListener::testSessionEventEcho(sp<ICas>& mediaCas, const hidl_vec<u EXPECT_TRUE(mEventData == eventData); } -// Test environment for Cas HIDL HAL. -class CasHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { - public: - // get the test environment singleton - static CasHidlEnvironment* Instance() { - static CasHidlEnvironment* instance = new CasHidlEnvironment; - return instance; - } - - virtual void registerTestServices() override { registerTestService<IMediaCasService>(); } -}; - -class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class MediaCasHidlTest : public testing::TestWithParam<std::string> { public: virtual void SetUp() override { - mService = ::testing::VtsHalHidlTargetTestBase::getService<IMediaCasService>( - CasHidlEnvironment::Instance()->getServiceName<IMediaCasService>()); + mService = IMediaCasService::getService(GetParam()); ASSERT_NE(mService, nullptr); } - sp<IMediaCasService> mService; + sp<IMediaCasService> mService = nullptr; protected: static void description(const std::string& description) { @@ -366,7 +356,7 @@ class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase { return ::testing::AssertionFailure(); } - uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mem->pointer())); + uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mem->unsecurePointer())); memcpy(ipBuffer, kInBinaryBuffer, sizeof(kInBinaryBuffer)); // hidlMemory is not to be passed out of scope! @@ -453,7 +443,7 @@ class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase { return ::testing::AssertionResult(returnVoid.isOk()); } -TEST_F(MediaCasHidlTest, TestClearKeyApisWithSession) { +TEST_P(MediaCasHidlTest, TestClearKeyApisWithSession) { description("Test that valid call sequences with SessionEvent send and receive"); ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); @@ -543,7 +533,7 @@ TEST_F(MediaCasHidlTest, TestClearKeyApisWithSession) { EXPECT_EQ(Status::OK, descrambleStatus); ASSERT_NE(nullptr, dataMemory.get()); - uint8_t* opBuffer = static_cast<uint8_t*>(static_cast<void*>(dataMemory->pointer())); + uint8_t* opBuffer = static_cast<uint8_t*>(static_cast<void*>(dataMemory->unsecurePointer())); int compareResult = memcmp(static_cast<const void*>(opBuffer), @@ -561,11 +551,7 @@ TEST_F(MediaCasHidlTest, TestClearKeyApisWithSession) { } // anonymous namespace -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(CasHidlEnvironment::Instance()); - ::testing::InitGoogleTest(&argc, argv); - CasHidlEnvironment::Instance()->init(&argc, argv); - int status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - return status; -} +INSTANTIATE_TEST_SUITE_P( + PerInstance, MediaCasHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IMediaCasService::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/cas/1.2/types.hal b/cas/1.2/types.hal index 40c06cf1c7..06199cda16 100644 --- a/cas/1.2/types.hal +++ b/cas/1.2/types.hal @@ -45,6 +45,10 @@ enum Status : @1.0::Status { * ERROR_CAS_BLACKOUT is used to report geographical blackout. */ ERROR_CAS_BLACKOUT, + /** + * ERROR_CAS_REBOOTING is used to report CAS is during rebooting. + */ + ERROR_CAS_REBOOTING, }; /** diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index f0b5966e4d..4bd833251a 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -123,7 +123,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.cas</name> - <version>1.1</version> + <version>1.1-2</version> <interface> <name>IMediaCasService</name> <instance>default</instance> @@ -197,8 +197,8 @@ </hal> <hal format="hidl" optional="false"> <name>android.hardware.graphics.allocator</name> - <version>2.0</version> <version>3.0</version> + <version>4.0</version> <interface> <name>IAllocator</name> <instance>default</instance> @@ -206,7 +206,7 @@ </hal> <hal format="hidl" optional="false"> <name>android.hardware.graphics.composer</name> - <version>2.1-3</version> + <version>2.1-4</version> <interface> <name>IComposer</name> <instance>default</instance> @@ -214,8 +214,8 @@ </hal> <hal format="hidl" optional="false"> <name>android.hardware.graphics.mapper</name> - <version>2.1</version> <version>3.0</version> + <version>4.0</version> <interface> <name>IMapper</name> <instance>default</instance> @@ -280,7 +280,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.media.c2</name> - <version>1.0</version> + <version>1.0-1</version> <interface> <name>IComponentStore</name> <regex-instance>default[0-9]*</regex-instance> @@ -500,7 +500,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.wifi</name> - <version>1.0-3</version> + <version>1.0-4</version> <interface> <name>IWifi</name> <instance>default</instance> @@ -516,7 +516,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.wifi.supplicant</name> - <version>1.0-2</version> + <version>1.0-3</version> <interface> <name>ISupplicant</name> <instance>default</instance> diff --git a/current.txt b/current.txt index c1991c36db..d33d1b78a8 100644 --- a/current.txt +++ b/current.txt @@ -572,13 +572,14 @@ efbb061c969fa9553d243da6ee23b83fe5d4aa663a7b8896adc52e2b015bc2f3 android.hardwar cfa81f229b69f9011c58f48264fcb552447430fe68610eac514e811e65bc306a android.hardware.wifi.supplicant@1.2::types # ABI preserving changes to HALs during Android R +2410dd02d67786a732d36e80b0f8ccf55086604ef37f9838e2013ff2c571e404 android.hardware.camera.device@3.5::types b69a7615c508acf5c5201efd1bfa3262167874fc3594e2db5a3ff93addd8ac75 android.hardware.keymaster@4.0::IKeymasterDevice eb2fa0c883c2185d514be0b84c179b283753ef0c1b77b45b4f359bd23bba8b75 android.hardware.neuralnetworks@1.0::IPreparedModel f1109cbb10297b7429a11fab42afa912710b303c9bf20bd5cdb8bd57b9c84186 android.hardware.neuralnetworks@1.0::types 9d8ee57c490ffeaa28f702eaea8d198cb510e4bbfb99e6cb5f63e73341057c7c android.hardware.neuralnetworks@1.1::types fb382e986c10b8fbb797a8546e8f9ea6d1107bfe6f3fb7e57f6bbbf1f807a906 android.hardware.neuralnetworks@1.2::IDevice 40e71cd693de5b832325c5d8f081f2ff20a7ba2b89d401cee5b4b3eb0e241681 android.hardware.neuralnetworks@1.2::IPreparedModel -71c0f7127335e5b74d1615d5e7f129831b43ffbae5318ad0924d7d8d8910a859 android.hardware.neuralnetworks@1.2::types +72de91c3feba4b19c159cd1c413cbea596b78240caa43e31194e20e6f5b05c49 android.hardware.neuralnetworks@1.2::types a785a57447a81e9c130eef6904c3a5c256076c6a04588c40620ebd6fa2660d77 android.hardware.radio@1.2::types 1a6e2bd289f22931c526b21916910f1d4c436b7acb9556e4243de4ce8e6cc2e4 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardware.wifi@1.0::IWifiP2pIface @@ -588,17 +589,29 @@ fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardwar 40ab2c6866c18d32baf6e49e3053949e79601f56963a791e93e68b9ee18f718d android.hardware.bluetooth@1.1::IBluetoothHciCallbacks 07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl 74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types +c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas +9811f867def49b420d8c707f7e38d3bdd64f835244e1d2a5e9762ab9835672dc android.hardware.cas@1.2::ICasListener +f18695dd36ee205640b8326a17453858a7b4596653aaa6ef0016b0aef1bd4dac android.hardware.cas@1.2::IMediaCasService +4d85e814f94949dae4dc6cb82bbd7d6bb24ffafda6ddb2eac928d2a4fc2e21ce android.hardware.cas@1.2::types ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth 26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback db47f4ceceb1f06c656f39caa70c557b0f8471ef59fd58611bea667ffca20101 android.hardware.health@2.1::types c228aaa27f66c48e147159a4f4996c5273191fece1b08de31bd171c61334855e android.hardware.keymaster@4.1::IKeymasterDevice adb0efdf1462e9b2e742c0dcadd598666aac551f178be06e755bfcdf5797abd0 android.hardware.keymaster@4.1::IOperation 7a04ea5595ed418ca3e91c28b8bd7353dd988be9be7b0c8c9e64fb4b77bd4523 android.hardware.keymaster@4.1::types +df9c79c4fdde2821550c6d5c3d07f5ec0adfb1b702561ce543c906ddef698703 android.hardware.media.c2@1.1::IComponent +a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardware.media.c2@1.1::IComponentStore 9e59fffceed0dd72a9799e04505db5f777bbbea1af0695ba4107ef6d967c6fda android.hardware.neuralnetworks@1.3::IDevice 4a6c3b3556da951b4def21ba579a227c022980fe4465df6cdfbe20628fa75f5a android.hardware.neuralnetworks@1.3::IPreparedModel 94e803236398bed1febb11cc21051bc42ec003700139b099d6c479e02a7ca3c3 android.hardware.neuralnetworks@1.3::IPreparedModelCallback -c511b1427b1c3f76af90967bbddaaf250db983a8d3abb9ff189fb5a807cf3d4d android.hardware.neuralnetworks@1.3::types +2d16429145dc1158bf3e45c7de86a39e461dec3ec00512c11a7e5249535a2e96 android.hardware.neuralnetworks@1.3::types +3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi +a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant +44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface +619fc9839ec6e369cfa9b28e3e9412e6885720ff8f9b5750c1b6ffb905120391 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback +c9273429fcf98d797d3bb07fdba6f1be95bf960f9255cde169fd1ca4db85f856 android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork +9b0a3ab6f4f74b971ed094426d8a443e29b512ff03e1ab50c07156396cdb2483 android.hardware.wifi.supplicant@1.3::types 274fb1254a6d1a97824ec5c880eeefc0e410dc6d3a2a4c34052201169d2b7de0 android.hardware.radio@1.5::types c8e81d912827a5d49b2ddcdc4eb4556c5d231a899a1dca879309e04210daa4a0 android.hardware.radio@1.5::IRadio a62a93faf173b14a6175b683ebf61ffa568dc61f81e369d2dce7b1265e86cf2f android.hardware.radio@1.5::IRadioIndication -260ce05806d753d728f844d405e832179ed7d9b65986ec18fef3d21cf7285587 android.hardware.radio@1.5::IRadioResponse +260ce05806d753d728f844d405e832179ed7d9b65986ec18fef3d21cf7285587 android.hardware.radio@1.5::IRadioResponse
\ No newline at end of file diff --git a/drm/TEST_MAPPING b/drm/TEST_MAPPING new file mode 100644 index 0000000000..cff681917f --- /dev/null +++ b/drm/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "imports": [ + // gts and cts filters + { + "path": "frameworks/av/drm/libmediadrm" + } + ] +} diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp index cc34290fa8..bdd02d2e6e 100644 --- a/gnss/1.1/vts/functional/Android.bp +++ b/gnss/1.1/vts/functional/Android.bp @@ -30,5 +30,5 @@ cc_test { shared_libs: [ "android.hardware.gnss.measurement_corrections@1.0", ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts-core"], } diff --git a/gnss/1.1/vts/functional/VtsHalGnssV1_1TargetTest.cpp b/gnss/1.1/vts/functional/VtsHalGnssV1_1TargetTest.cpp index ca9eef4822..4a0a7f9ffa 100644 --- a/gnss/1.1/vts/functional/VtsHalGnssV1_1TargetTest.cpp +++ b/gnss/1.1/vts/functional/VtsHalGnssV1_1TargetTest.cpp @@ -15,15 +15,15 @@ */ #define LOG_TAG "VtsHalGnssV1_1TargetTest" -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "gnss_hal_test.h" -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(GnssHidlEnvironment::Instance()); - ::testing::InitGoogleTest(&argc, argv); - GnssHidlEnvironment::Instance()->init(&argc, argv); - int status = RUN_ALL_TESTS(); - ALOGI("Test result = %d", status); - return status; -} +using android::hardware::gnss::V1_1::IGnss; + +INSTANTIATE_TEST_SUITE_P( + PerInstance, GnssHalTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IGnss::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/gnss/1.1/vts/functional/gnss_hal_test.cpp b/gnss/1.1/vts/functional/gnss_hal_test.cpp index f3b376e6bd..2c8a7b1399 100644 --- a/gnss/1.1/vts/functional/gnss_hal_test.cpp +++ b/gnss/1.1/vts/functional/gnss_hal_test.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "GnssHalTest" #include <android/hidl/manager/1.2/IServiceManager.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> #include <hidl/ServiceManagement.h> #include <gnss_hal_test.h> @@ -28,18 +30,8 @@ using ::android::hardware::hidl_vec; using ::android::hardware::gnss::common::Utils; -// Implementations for the main test class for GNSS HAL -GnssHalTest::GnssHalTest() - : info_called_count_(0), - capabilities_called_count_(0), - location_called_count_(0), - name_called_count_(0), - notify_count_(0) {} - void GnssHalTest::SetUp() { - gnss_hal_ = ::testing::VtsHalHidlTargetTestBase::getService<IGnss>( - GnssHidlEnvironment::Instance()->getServiceName<IGnss>()); - list_gnss_sv_status_.clear(); + gnss_hal_ = IGnss::getService(GetParam()); ASSERT_NE(gnss_hal_, nullptr); SetUpGnssCallback(); @@ -48,14 +40,15 @@ void GnssHalTest::SetUp() { void GnssHalTest::TearDown() { if (gnss_hal_ != nullptr) { gnss_hal_->cleanup(); + gnss_hal_ = nullptr; } - if (notify_count_ > 0) { - ALOGW("%d unprocessed callbacks discarded", notify_count_); - } + + // Set to nullptr to destruct the callback event queues and warn of any unprocessed events. + gnss_cb_ = nullptr; } void GnssHalTest::SetUpGnssCallback() { - gnss_cb_ = new GnssCallback(*this); + gnss_cb_ = new GnssCallback(); ASSERT_NE(gnss_cb_, nullptr); auto result = gnss_hal_->setCallback_1_1(gnss_cb_); @@ -69,13 +62,13 @@ void GnssHalTest::SetUpGnssCallback() { /* * All capabilities, name and systemInfo callbacks should trigger */ - EXPECT_EQ(std::cv_status::no_timeout, wait(TIMEOUT_SEC)); - EXPECT_EQ(std::cv_status::no_timeout, wait(TIMEOUT_SEC)); - EXPECT_EQ(std::cv_status::no_timeout, wait(TIMEOUT_SEC)); + EXPECT_TRUE(gnss_cb_->capabilities_cbq_.retrieve(gnss_cb_->last_capabilities_, TIMEOUT_SEC)); + EXPECT_TRUE(gnss_cb_->info_cbq_.retrieve(gnss_cb_->last_info_, TIMEOUT_SEC)); + EXPECT_TRUE(gnss_cb_->name_cbq_.retrieve(gnss_cb_->last_name_, TIMEOUT_SEC)); - EXPECT_EQ(capabilities_called_count_, 1); - EXPECT_EQ(info_called_count_, 1); - EXPECT_EQ(name_called_count_, 1); + EXPECT_EQ(gnss_cb_->capabilities_cbq_.calledCount(), 1); + EXPECT_EQ(gnss_cb_->info_cbq_.calledCount(), 1); + EXPECT_EQ(gnss_cb_->name_cbq_.calledCount(), 1); } void GnssHalTest::StopAndClearLocations() { @@ -89,9 +82,9 @@ void GnssHalTest::StopAndClearLocations() { * the last reply for final startup messages to arrive (esp. system * info.) */ - while (wait(TIMEOUT_SEC) == std::cv_status::no_timeout) { + while (gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_, TIMEOUT_SEC)) { } - location_called_count_ = 0; + gnss_cb_->location_cbq_.reset(); } void GnssHalTest::SetPositionMode(const int min_interval_msec, const bool low_power_mode) { @@ -118,19 +111,22 @@ bool GnssHalTest::StartAndCheckFirstLocation() { */ const int kFirstGnssLocationTimeoutSeconds = 75; - wait(kFirstGnssLocationTimeoutSeconds); - EXPECT_EQ(location_called_count_, 1); + EXPECT_TRUE(gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_, + kFirstGnssLocationTimeoutSeconds)); + int locationCalledCount = gnss_cb_->location_cbq_.calledCount(); + EXPECT_EQ(locationCalledCount, 1); - if (location_called_count_ > 0) { + if (locationCalledCount > 0) { // don't require speed on first fix - CheckLocation(last_location_, false); + CheckLocation(gnss_cb_->last_location_, false); return true; } return false; } void GnssHalTest::CheckLocation(GnssLocation& location, bool check_speed) { - bool check_more_accuracies = (info_called_count_ > 0 && last_info_.yearOfHw >= 2017); + const bool check_more_accuracies = + (gnss_cb_->info_cbq_.calledCount() > 0 && gnss_cb_->last_info_.yearOfHw >= 2017); Utils::checkLocation(location, check_speed, check_more_accuracies); } @@ -145,12 +141,14 @@ void GnssHalTest::StartAndCheckLocations(int count) { EXPECT_TRUE(StartAndCheckFirstLocation()); for (int i = 1; i < count; i++) { - EXPECT_EQ(std::cv_status::no_timeout, wait(kLocationTimeoutSubsequentSec)); - EXPECT_EQ(location_called_count_, i + 1); + EXPECT_TRUE(gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_, + kLocationTimeoutSubsequentSec)); + int locationCalledCount = gnss_cb_->location_cbq_.calledCount(); + EXPECT_EQ(locationCalledCount, i + 1); // Don't cause confusion by checking details if no location yet - if (location_called_count_ > 0) { + if (locationCalledCount > 0) { // Should be more than 1 location by now, but if not, still don't check first fix speed - CheckLocation(last_location_, location_called_count_ > 1); + CheckLocation(gnss_cb_->last_location_, locationCalledCount > 1); } } } @@ -177,60 +175,41 @@ bool GnssHalTest::IsGnssHalVersion_1_1() const { return hasGnssHalVersion_1_1 && !hasGnssHalVersion_2_0; } -void GnssHalTest::notify() { - std::unique_lock<std::mutex> lock(mtx_); - notify_count_++; - cv_.notify_one(); -} - -std::cv_status GnssHalTest::wait(int timeout_seconds) { - std::unique_lock<std::mutex> lock(mtx_); - - auto status = std::cv_status::no_timeout; - while (notify_count_ == 0) { - status = cv_.wait_for(lock, std::chrono::seconds(timeout_seconds)); - if (status == std::cv_status::timeout) return status; - } - notify_count_--; - return status; -} +GnssHalTest::GnssCallback::GnssCallback() + : info_cbq_("system_info"), + name_cbq_("name"), + capabilities_cbq_("capabilities"), + location_cbq_("location"), + sv_status_cbq_("sv_status") {} Return<void> GnssHalTest::GnssCallback::gnssSetSystemInfoCb( const IGnssCallback::GnssSystemInfo& info) { ALOGI("Info received, year %d", info.yearOfHw); - parent_.info_called_count_++; - parent_.last_info_ = info; - parent_.notify(); + info_cbq_.store(info); return Void(); } Return<void> GnssHalTest::GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) { ALOGI("Capabilities received %d", capabilities); - parent_.capabilities_called_count_++; - parent_.last_capabilities_ = capabilities; - parent_.notify(); + capabilities_cbq_.store(capabilities); return Void(); } Return<void> GnssHalTest::GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) { ALOGI("Name received: %s", name.c_str()); - parent_.name_called_count_++; - parent_.last_name_ = name; - parent_.notify(); + name_cbq_.store(name); return Void(); } Return<void> GnssHalTest::GnssCallback::gnssLocationCb(const GnssLocation& location) { ALOGI("Location received"); - parent_.location_called_count_++; - parent_.last_location_ = location; - parent_.notify(); + location_cbq_.store(location); return Void(); } Return<void> GnssHalTest::GnssCallback::gnssSvStatusCb( const IGnssCallback::GnssSvStatus& svStatus) { ALOGI("GnssSvStatus received"); - parent_.list_gnss_sv_status_.emplace_back(svStatus); + sv_status_cbq_.store(svStatus); return Void(); } diff --git a/gnss/1.1/vts/functional/gnss_hal_test.h b/gnss/1.1/vts/functional/gnss_hal_test.h index 84a9f846fc..169cd62c59 100644 --- a/gnss/1.1/vts/functional/gnss_hal_test.h +++ b/gnss/1.1/vts/functional/gnss_hal_test.h @@ -19,46 +19,26 @@ #include <android/hardware/gnss/1.1/IGnss.h> -#include <VtsHalHidlTargetTestBase.h> -#include <VtsHalHidlTargetTestEnvBase.h> - -#include <condition_variable> -#include <list> -#include <mutex> +#include <gtest/gtest.h> +#include "GnssCallbackEventQueue.h" using android::hardware::Return; using android::hardware::Void; using android::hardware::gnss::V1_0::GnssLocation; +using android::hardware::gnss::common::GnssCallbackEventQueue; +using android::hardware::gnss::V1_0::GnssLocationFlags; using android::hardware::gnss::V1_1::IGnss; using android::hardware::gnss::V1_1::IGnssCallback; -using android::hardware::gnss::V1_0::GnssLocationFlags; using android::sp; #define TIMEOUT_SEC 2 // for basic commands/responses -// Test environment for GNSS HIDL HAL. -class GnssHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { - public: - // get the test environment singleton - static GnssHidlEnvironment* Instance() { - static GnssHidlEnvironment* instance = new GnssHidlEnvironment; - return instance; - } - - virtual void registerTestServices() override { registerTestService<IGnss>(); } - - private: - GnssHidlEnvironment() {} -}; - // The main test class for GNSS HAL. -class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { - public: - GnssHalTest(); - +class GnssHalTest : public testing::TestWithParam<std::string> { + public: virtual void SetUp() override; virtual void TearDown() override; @@ -72,32 +52,40 @@ class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { /* Callback class for data & Event. */ class GnssCallback : public IGnssCallback { public: - GnssHalTest& parent_; - - GnssCallback(GnssHalTest& parent) : parent_(parent){}; - - virtual ~GnssCallback() = default; - - // Dummy callback handlers - Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue /* status */) override { - return Void(); - } - Return<void> gnssNmeaCb(int64_t /* timestamp */, - const android::hardware::hidl_string& /* nmea */) override { - return Void(); - } - Return<void> gnssAcquireWakelockCb() override { return Void(); } - Return<void> gnssReleaseWakelockCb() override { return Void(); } - Return<void> gnssRequestLocationCb(bool /* independentFromGnss */) override { - return Void(); - } - Return<void> gnssRequestTimeCb() override { return Void(); } - // Actual (test) callback handlers - Return<void> gnssNameCb(const android::hardware::hidl_string& name) override; - Return<void> gnssLocationCb(const GnssLocation& location) override; - Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override; - Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override; - Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override; + IGnssCallback::GnssSystemInfo last_info_; + android::hardware::hidl_string last_name_; + uint32_t last_capabilities_; + GnssLocation last_location_; + + GnssCallbackEventQueue<IGnssCallback::GnssSystemInfo> info_cbq_; + GnssCallbackEventQueue<android::hardware::hidl_string> name_cbq_; + GnssCallbackEventQueue<uint32_t> capabilities_cbq_; + GnssCallbackEventQueue<GnssLocation> location_cbq_; + GnssCallbackEventQueue<IGnssCallback::GnssSvStatus> sv_status_cbq_; + + GnssCallback(); + virtual ~GnssCallback() = default; + + // Dummy callback handlers + Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue /* status */) override { + return Void(); + } + Return<void> gnssNmeaCb(int64_t /* timestamp */, + const android::hardware::hidl_string& /* nmea */) override { + return Void(); + } + Return<void> gnssAcquireWakelockCb() override { return Void(); } + Return<void> gnssReleaseWakelockCb() override { return Void(); } + Return<void> gnssRequestLocationCb(bool /* independentFromGnss */) override { + return Void(); + } + Return<void> gnssRequestTimeCb() override { return Void(); } + // Actual (test) callback handlers + Return<void> gnssNameCb(const android::hardware::hidl_string& name) override; + Return<void> gnssLocationCb(const GnssLocation& location) override; + Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override; + Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override; + Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override; }; /* @@ -152,26 +140,7 @@ class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { bool IsGnssHalVersion_1_1() const; sp<IGnss> gnss_hal_; // GNSS HAL to call into - sp<IGnssCallback> gnss_cb_; // Primary callback interface - - /* Count of calls to set the following items, and the latest item (used by - * test.) - */ - int info_called_count_; - IGnssCallback::GnssSystemInfo last_info_; - uint32_t last_capabilities_; - int capabilities_called_count_; - int location_called_count_; - GnssLocation last_location_; - list<IGnssCallback::GnssSvStatus> list_gnss_sv_status_; - - int name_called_count_; - android::hardware::hidl_string last_name_; - - private: - std::mutex mtx_; - std::condition_variable cv_; - int notify_count_; + sp<GnssCallback> gnss_cb_; // Primary callback interface }; #endif // GNSS_HAL_TEST_H_ diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp index ee236ba5d1..79da84ac10 100644 --- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp +++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp @@ -18,9 +18,8 @@ #include <gnss_hal_test.h> -#include <VtsHalHidlTargetTestBase.h> - #include <android/hardware/gnss/1.1/IGnssConfiguration.h> +#include <gtest/gtest.h> using android::hardware::hidl_vec; @@ -39,18 +38,18 @@ using android::hardware::gnss::V1_1::IGnssMeasurement; * * Empty test fixture to verify basic Setup & Teardown */ -TEST_F(GnssHalTest, SetupTeardownCreateCleanup) {} +TEST_P(GnssHalTest, SetupTeardownCreateCleanup) {} /* * TestGnssMeasurementCallback: * Gets the GnssMeasurementExtension and verify that it returns an actual extension. */ -TEST_F(GnssHalTest, TestGnssMeasurementCallback) { +TEST_P(GnssHalTest, TestGnssMeasurementCallback) { auto gnssMeasurement_1_1 = gnss_hal_->getExtensionGnssMeasurement_1_1(); ASSERT_TRUE(gnssMeasurement_1_1.isOk()); auto gnssMeasurement_1_0 = gnss_hal_->getExtensionGnssMeasurement(); ASSERT_TRUE(gnssMeasurement_1_0.isOk()); - if (last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENTS) { + if (gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENTS) { sp<IGnssMeasurement_1_1> iGnssMeas_1_1 = gnssMeasurement_1_1; sp<IGnssMeasurement_1_0> iGnssMeas_1_0 = gnssMeasurement_1_0; // At least one interface must be non-null. @@ -65,7 +64,7 @@ TEST_F(GnssHalTest, TestGnssMeasurementCallback) { * NO_LOCATION_PERIOD_SEC and verfiy that no location is received. Also perform validity checks on * each received location. */ -TEST_F(GnssHalTest, GetLocationLowPower) { +TEST_P(GnssHalTest, GetLocationLowPower) { if (!IsGnssHalVersion_1_1()) { ALOGI("Test GetLocationLowPower skipped. GNSS HAL version is greater than 1.1."); return; @@ -78,8 +77,10 @@ TEST_F(GnssHalTest, GetLocationLowPower) { const bool kLowPowerMode = true; // Warmup period - VTS doesn't have AGPS access via GnssLocationProvider - StartAndCheckLocations(5); + gnss_cb_->location_cbq_.reset(); + StartAndCheckLocations(kLocationsToCheck); StopAndClearLocations(); + gnss_cb_->location_cbq_.reset(); // Start of Low Power Mode test SetPositionMode(kMinIntervalMsec, kLowPowerMode); @@ -93,24 +94,26 @@ TEST_F(GnssHalTest, GetLocationLowPower) { // Verify that kMinIntervalMsec is respected by waiting kNoLocationPeriodSec and // ensure that no location is received yet - wait(kNoLocationPeriodSec); + gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_, kNoLocationPeriodSec); + const int location_called_count = gnss_cb_->location_cbq_.calledCount(); // Tolerate (ignore) one extra location right after the first one // to handle startup edge case scheduling limitations in some implementations - if ((i == 1) && (location_called_count_ == 2)) { - CheckLocation(last_location_, true); + if ((i == 1) && (location_called_count == 2)) { + CheckLocation(gnss_cb_->last_location_, true); continue; // restart the quiet wait period after this too-fast location } - EXPECT_LE(location_called_count_, i); - if (location_called_count_ != i) { + EXPECT_LE(location_called_count, i); + if (location_called_count != i) { ALOGW("GetLocationLowPower test - not enough locations received. %d vs. %d expected ", - location_called_count_, i); + location_called_count, i); } - if (std::cv_status::no_timeout != - wait(kLocationTimeoutSubsequentSec - kNoLocationPeriodSec)) { + if (!gnss_cb_->location_cbq_.retrieve( + gnss_cb_->last_location_, + kLocationTimeoutSubsequentSec - kNoLocationPeriodSec)) { ALOGW("GetLocationLowPower test - timeout awaiting location %d", i); } else { - CheckLocation(last_location_, true); + CheckLocation(gnss_cb_->last_location_, true); } } @@ -127,7 +130,8 @@ TEST_F(GnssHalTest, GetLocationLowPower) { */ IGnssConfiguration::BlacklistedSource FindStrongFrequentNonGpsSource( - const list<IGnssCallback::GnssSvStatus> list_gnss_sv_status, const int min_observations) { + const std::list<IGnssCallback::GnssSvStatus> list_gnss_sv_status, + const int min_observations) { struct ComparableBlacklistedSource { IGnssConfiguration::BlacklistedSource id; @@ -213,7 +217,7 @@ IGnssConfiguration::BlacklistedSource FindStrongFrequentNonGpsSource( * 5b) Retry a few times, in case GNSS search strategy takes a while to reacquire even the * formerly strongest satellite */ -TEST_F(GnssHalTest, BlacklistIndividualSatellites) { +TEST_P(GnssHalTest, BlacklistIndividualSatellites) { if (!IsGnssHalVersion_1_1()) { ALOGI("Test BlacklistIndividualSatellites skipped. GNSS HAL version is greater than 1.1."); return; @@ -222,12 +226,15 @@ TEST_F(GnssHalTest, BlacklistIndividualSatellites) { const int kLocationsToAwait = 3; const int kRetriesToUnBlacklist = 10; + gnss_cb_->location_cbq_.reset(); StartAndCheckLocations(kLocationsToAwait); + int location_called_count = gnss_cb_->location_cbq_.calledCount(); // Tolerate 1 less sv status to handle edge cases in reporting. - EXPECT_GE((int)list_gnss_sv_status_.size() + 1, kLocationsToAwait); - ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)", - (int)list_gnss_sv_status_.size(), kLocationsToAwait, location_called_count_); + int sv_status_cbq_size = gnss_cb_->sv_status_cbq_.size(); + EXPECT_GE(sv_status_cbq_size + 1, kLocationsToAwait); + ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)", sv_status_cbq_size, + kLocationsToAwait, location_called_count); /* * Identify strongest SV seen at least kLocationsToAwait -1 times @@ -235,8 +242,14 @@ TEST_F(GnssHalTest, BlacklistIndividualSatellites) { * observability (one epoch RF null) */ + const int kGnssSvStatusTimeout = 2; + std::list<IGnssCallback::GnssSvStatus> sv_status_list; + int count = gnss_cb_->sv_status_cbq_.retrieve(sv_status_list, sv_status_cbq_size, + kGnssSvStatusTimeout); + ASSERT_EQ(count, sv_status_cbq_size); + IGnssConfiguration::BlacklistedSource source_to_blacklist = - FindStrongFrequentNonGpsSource(list_gnss_sv_status_, kLocationsToAwait - 1); + FindStrongFrequentNonGpsSource(sv_status_list, kLocationsToAwait - 1); if (source_to_blacklist.constellation == GnssConstellationType::UNKNOWN) { // Cannot find a non-GPS satellite. Let the test pass. @@ -260,21 +273,26 @@ TEST_F(GnssHalTest, BlacklistIndividualSatellites) { EXPECT_TRUE(result); // retry and ensure satellite not used - list_gnss_sv_status_.clear(); + gnss_cb_->sv_status_cbq_.reset(); + gnss_cb_->location_cbq_.reset(); StartAndCheckLocations(kLocationsToAwait); // early exit if test is being run with insufficient signal - if (location_called_count_ == 0) { + location_called_count = gnss_cb_->location_cbq_.calledCount(); + if (location_called_count == 0) { ALOGE("0 Gnss locations received - ensure sufficient signal and retry"); } - ASSERT_TRUE(location_called_count_ > 0); + ASSERT_TRUE(location_called_count > 0); // Tolerate 1 less sv status to handle edge cases in reporting. - EXPECT_GE((int)list_gnss_sv_status_.size() + 1, kLocationsToAwait); - ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)", - (int)list_gnss_sv_status_.size(), kLocationsToAwait, location_called_count_); - for (const auto& gnss_sv_status : list_gnss_sv_status_) { + sv_status_cbq_size = gnss_cb_->sv_status_cbq_.size(); + EXPECT_GE(sv_status_cbq_size + 1, kLocationsToAwait); + ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)", sv_status_cbq_size, + kLocationsToAwait, location_called_count); + for (int i = 0; i < sv_status_cbq_size; ++i) { + IGnssCallback::GnssSvStatus gnss_sv_status; + gnss_cb_->sv_status_cbq_.retrieve(gnss_sv_status, kGnssSvStatusTimeout); for (uint32_t iSv = 0; iSv < gnss_sv_status.numSvs; iSv++) { const auto& gnss_sv = gnss_sv_status.gnssSvList[iSv]; EXPECT_FALSE((gnss_sv.svid == source_to_blacklist.svid) && @@ -295,24 +313,28 @@ TEST_F(GnssHalTest, BlacklistIndividualSatellites) { int unblacklist_loops_remaining = kRetriesToUnBlacklist; while (!strongest_sv_is_reobserved && (unblacklist_loops_remaining-- > 0)) { StopAndClearLocations(); - list_gnss_sv_status_.clear(); + gnss_cb_->sv_status_cbq_.reset(); + gnss_cb_->location_cbq_.reset(); StartAndCheckLocations(kLocationsToAwait); // early exit loop if test is being run with insufficient signal - if (location_called_count_ == 0) { + location_called_count = gnss_cb_->location_cbq_.calledCount(); + if (location_called_count == 0) { ALOGE("0 Gnss locations received - ensure sufficient signal and retry"); } - ASSERT_TRUE(location_called_count_ > 0); + ASSERT_TRUE(location_called_count > 0); // Tolerate 1 less sv status to handle edge cases in reporting. - EXPECT_GE((int)list_gnss_sv_status_.size() + 1, kLocationsToAwait); - ALOGD( - "Clear blacklist, observed %d GnssSvStatus, while awaiting %d Locations" - ", tries remaining %d", - (int)list_gnss_sv_status_.size(), kLocationsToAwait, unblacklist_loops_remaining); - - for (const auto& gnss_sv_status : list_gnss_sv_status_) { + sv_status_cbq_size = gnss_cb_->sv_status_cbq_.size(); + EXPECT_GE(sv_status_cbq_size + 1, kLocationsToAwait); + ALOGD("Clear blacklist, observed %d GnssSvStatus, while awaiting %d Locations" + ", tries remaining %d", + sv_status_cbq_size, kLocationsToAwait, unblacklist_loops_remaining); + + for (int i = 0; i < sv_status_cbq_size; ++i) { + IGnssCallback::GnssSvStatus gnss_sv_status; + gnss_cb_->sv_status_cbq_.retrieve(gnss_sv_status, kGnssSvStatusTimeout); for (uint32_t iSv = 0; iSv < gnss_sv_status.numSvs; iSv++) { const auto& gnss_sv = gnss_sv_status.gnssSvList[iSv]; if ((gnss_sv.svid == source_to_blacklist.svid) && @@ -339,7 +361,7 @@ TEST_F(GnssHalTest, BlacklistIndividualSatellites) { * GnssStatus does not use any constellation but GPS. * 4a & b) Clean up by turning off location, and send in empty blacklist. */ -TEST_F(GnssHalTest, BlacklistConstellation) { +TEST_P(GnssHalTest, BlacklistConstellation) { if (!IsGnssHalVersion_1_1()) { ALOGI("Test BlacklistConstellation skipped. GNSS HAL version is greater than 1.1."); return; @@ -347,16 +369,22 @@ TEST_F(GnssHalTest, BlacklistConstellation) { const int kLocationsToAwait = 3; + gnss_cb_->location_cbq_.reset(); StartAndCheckLocations(kLocationsToAwait); + const int location_called_count = gnss_cb_->location_cbq_.calledCount(); // Tolerate 1 less sv status to handle edge cases in reporting. - EXPECT_GE((int)list_gnss_sv_status_.size() + 1, kLocationsToAwait); - ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)", - (int)list_gnss_sv_status_.size(), kLocationsToAwait, location_called_count_); + int sv_status_cbq_size = gnss_cb_->sv_status_cbq_.size(); + EXPECT_GE(sv_status_cbq_size + 1, kLocationsToAwait); + ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)", sv_status_cbq_size, + kLocationsToAwait, location_called_count); // Find first non-GPS constellation to blacklist + const int kGnssSvStatusTimeout = 2; GnssConstellationType constellation_to_blacklist = GnssConstellationType::UNKNOWN; - for (const auto& gnss_sv_status : list_gnss_sv_status_) { + for (int i = 0; i < sv_status_cbq_size; ++i) { + IGnssCallback::GnssSvStatus gnss_sv_status; + gnss_cb_->sv_status_cbq_.retrieve(gnss_sv_status, kGnssSvStatusTimeout); for (uint32_t iSv = 0; iSv < gnss_sv_status.numSvs; iSv++) { const auto& gnss_sv = gnss_sv_status.gnssSvList[iSv]; if ((gnss_sv.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX) && @@ -395,16 +423,19 @@ TEST_F(GnssHalTest, BlacklistConstellation) { EXPECT_TRUE(result); // retry and ensure constellation not used - list_gnss_sv_status_.clear(); + gnss_cb_->sv_status_cbq_.reset(); - location_called_count_ = 0; + gnss_cb_->location_cbq_.reset(); StartAndCheckLocations(kLocationsToAwait); // Tolerate 1 less sv status to handle edge cases in reporting. - EXPECT_GE((int)list_gnss_sv_status_.size() + 1, kLocationsToAwait); - ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations", (int)list_gnss_sv_status_.size(), + sv_status_cbq_size = gnss_cb_->sv_status_cbq_.size(); + EXPECT_GE(sv_status_cbq_size + 1, kLocationsToAwait); + ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations", sv_status_cbq_size, kLocationsToAwait); - for (const auto& gnss_sv_status : list_gnss_sv_status_) { + for (int i = 0; i < sv_status_cbq_size; ++i) { + IGnssCallback::GnssSvStatus gnss_sv_status; + gnss_cb_->sv_status_cbq_.retrieve(gnss_sv_status, kGnssSvStatusTimeout); for (uint32_t iSv = 0; iSv < gnss_sv_status.numSvs; iSv++) { const auto& gnss_sv = gnss_sv_status.gnssSvList[iSv]; EXPECT_FALSE((gnss_sv.constellation == source_to_blacklist.constellation) && @@ -425,9 +456,9 @@ TEST_F(GnssHalTest, BlacklistConstellation) { * * Ensure successfully injecting a location. */ -TEST_F(GnssHalTest, InjectBestLocation) { +TEST_P(GnssHalTest, InjectBestLocation) { StartAndCheckLocations(1); - GnssLocation gnssLocation = last_location_; + GnssLocation gnssLocation = gnss_cb_->last_location_; CheckLocation(gnssLocation, true); auto result = gnss_hal_->injectBestLocation(gnssLocation); @@ -444,10 +475,10 @@ TEST_F(GnssHalTest, InjectBestLocation) { * GnssDebugValuesSanityTest: * Ensures that GnssDebug values make sense. */ -TEST_F(GnssHalTest, GnssDebugValuesSanityTest) { +TEST_P(GnssHalTest, GnssDebugValuesSanityTest) { auto gnssDebug = gnss_hal_->getExtensionGnssDebug(); ASSERT_TRUE(gnssDebug.isOk()); - if (info_called_count_ > 0 && last_info_.yearOfHw >= 2017) { + if (gnss_cb_->info_cbq_.calledCount() > 0 && gnss_cb_->last_info_.yearOfHw >= 2017) { sp<IGnssDebug> iGnssDebug = gnssDebug; EXPECT_NE(iGnssDebug, nullptr); diff --git a/gnss/2.0/vts/functional/Android.bp b/gnss/2.0/vts/functional/Android.bp index 278d87b117..9aa13349b0 100644 --- a/gnss/2.0/vts/functional/Android.bp +++ b/gnss/2.0/vts/functional/Android.bp @@ -30,4 +30,5 @@ cc_test { "android.hardware.gnss@2.0", "android.hardware.gnss@common-vts-lib", ], + test_suites: ["general-tests", "vts-core"], } diff --git a/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp b/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp index ae36c50689..2c74fa37be 100644 --- a/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp +++ b/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp @@ -15,15 +15,15 @@ */ #define LOG_TAG "VtsHalGnssV2_0TargetTest" -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "gnss_hal_test.h" -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(GnssHidlEnvironment::Instance()); - ::testing::InitGoogleTest(&argc, argv); - GnssHidlEnvironment::Instance()->init(&argc, argv); - int status = RUN_ALL_TESTS(); - ALOGI("Test result = %d", status); - return status; -} +using android::hardware::gnss::V2_0::IGnss; + +INSTANTIATE_TEST_SUITE_P( + PerInstance, GnssHalTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IGnss::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/gnss/2.0/vts/functional/gnss_hal_test.cpp b/gnss/2.0/vts/functional/gnss_hal_test.cpp index 14ae43ce26..8ca3f684a3 100644 --- a/gnss/2.0/vts/functional/gnss_hal_test.cpp +++ b/gnss/2.0/vts/functional/gnss_hal_test.cpp @@ -20,12 +20,13 @@ #include <chrono> #include "Utils.h" +#include <gtest/gtest.h> + using ::android::hardware::gnss::common::Utils; // Implementations for the main test class for GNSS HAL void GnssHalTest::SetUp() { - gnss_hal_ = ::testing::VtsHalHidlTargetTestBase::getService<IGnss>( - GnssHidlEnvironment::Instance()->getServiceName<IGnss>()); + gnss_hal_ = IGnss::getService(GetParam()); ASSERT_NE(gnss_hal_, nullptr); SetUpGnssCallback(); diff --git a/gnss/2.0/vts/functional/gnss_hal_test.h b/gnss/2.0/vts/functional/gnss_hal_test.h index 90a7866082..4f7b87a485 100644 --- a/gnss/2.0/vts/functional/gnss_hal_test.h +++ b/gnss/2.0/vts/functional/gnss_hal_test.h @@ -18,18 +18,15 @@ #define GNSS_HAL_TEST_H_ #include <android/hardware/gnss/2.0/IGnss.h> -#include <VtsHalHidlTargetTestBase.h> -#include <VtsHalHidlTargetTestEnvBase.h> +#include "GnssCallbackEventQueue.h" -#include <condition_variable> -#include <deque> -#include <list> -#include <mutex> +#include <gtest/gtest.h> using android::hardware::hidl_vec; using android::hardware::Return; using android::hardware::Void; +using android::hardware::gnss::common::GnssCallbackEventQueue; using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrectionsCallback; using android::hardware::gnss::V1_0::GnssLocationFlags; using android::hardware::gnss::V2_0::IGnss; @@ -48,72 +45,13 @@ using android::sp; #define TIMEOUT_SEC 2 // for basic commands/responses -// Test environment for GNSS HIDL HAL. -class GnssHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { - public: - // get the test environment singleton - static GnssHidlEnvironment* Instance() { - static GnssHidlEnvironment* instance = new GnssHidlEnvironment; - return instance; - } - - virtual void registerTestServices() override { registerTestService<IGnss>(); } - - private: - GnssHidlEnvironment() {} -}; - // The main test class for GNSS HAL. -class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { - public: +class GnssHalTest : public testing::TestWithParam<std::string> { + public: virtual void SetUp() override; virtual void TearDown() override; - /* Producer/consumer queue for storing/retrieving callback events from GNSS HAL */ - template <class T> - class CallbackQueue { - public: - CallbackQueue(const std::string& name) : name_(name), called_count_(0){}; - ~CallbackQueue() { reset(); } - - /* Adds callback event to the end of the queue. */ - void store(const T& event); - - /* - * Removes the callack event at the front of the queue, stores it in event parameter - * and returns true. Returns false on timeout and event is not populated. - */ - bool retrieve(T& event, int timeout_seconds); - - /* - * Removes parameter count number of callack events at the front of the queue, stores - * them in event_list parameter and returns the number of events retrieved. Waits up to - * timeout_seconds to retrieve each event. If timeout occurs, it returns the number of - * items retrieved which will be less than count. - */ - int retrieve(list<T>& event_list, int count, int timeout_seconds); - - /* Returns the number of events pending to be retrieved from the callback event queue. */ - int size() const; - - /* Returns the number of callback events received since last reset(). */ - int calledCount() const; - - /* Clears the callback event queue and resets the calledCount() to 0. */ - void reset(); - - private: - CallbackQueue(const CallbackQueue&) = delete; - CallbackQueue& operator=(const CallbackQueue&) = delete; - - std::string name_; - int called_count_; - mutable std::recursive_mutex mtx_; - std::condition_variable_any cv_; - std::deque<T> events_; - }; - /* Callback class for data & Event. */ class GnssCallback : public IGnssCallback_2_0 { public: @@ -122,11 +60,11 @@ class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { uint32_t last_capabilities_; GnssLocation_2_0 last_location_; - CallbackQueue<IGnssCallback_1_0::GnssSystemInfo> info_cbq_; - CallbackQueue<android::hardware::hidl_string> name_cbq_; - CallbackQueue<uint32_t> capabilities_cbq_; - CallbackQueue<GnssLocation_2_0> location_cbq_; - CallbackQueue<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> sv_info_list_cbq_; + GnssCallbackEventQueue<IGnssCallback_1_0::GnssSystemInfo> info_cbq_; + GnssCallbackEventQueue<android::hardware::hidl_string> name_cbq_; + GnssCallbackEventQueue<uint32_t> capabilities_cbq_; + GnssCallbackEventQueue<GnssLocation_2_0> location_cbq_; + GnssCallbackEventQueue<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> sv_info_list_cbq_; GnssCallback(); virtual ~GnssCallback() = default; @@ -169,7 +107,7 @@ class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { /* Callback class for GnssMeasurement. */ class GnssMeasurementCallback : public IGnssMeasurementCallback_2_0 { public: - CallbackQueue<IGnssMeasurementCallback_2_0::GnssData> measurement_cbq_; + GnssCallbackEventQueue<IGnssMeasurementCallback_2_0::GnssData> measurement_cbq_; GnssMeasurementCallback() : measurement_cbq_("measurement"){}; virtual ~GnssMeasurementCallback() = default; @@ -192,7 +130,7 @@ class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { class GnssMeasurementCorrectionsCallback : public IMeasurementCorrectionsCallback { public: uint32_t last_capabilities_; - CallbackQueue<uint32_t> capabilities_cbq_; + GnssCallbackEventQueue<uint32_t> capabilities_cbq_; GnssMeasurementCorrectionsCallback() : capabilities_cbq_("capabilities"){}; virtual ~GnssMeasurementCorrectionsCallback() = default; @@ -252,61 +190,4 @@ class GnssHalTest : public ::testing::VtsHalHidlTargetTestBase { sp<GnssCallback> gnss_cb_; // Primary callback interface }; -template <class T> -void GnssHalTest::CallbackQueue<T>::store(const T& event) { - std::unique_lock<std::recursive_mutex> lock(mtx_); - events_.push_back(event); - ++called_count_; - lock.unlock(); - cv_.notify_all(); -} - -template <class T> -bool GnssHalTest::CallbackQueue<T>::retrieve(T& event, int timeout_seconds) { - std::unique_lock<std::recursive_mutex> lock(mtx_); - cv_.wait_for(lock, std::chrono::seconds(timeout_seconds), [&] { return !events_.empty(); }); - if (events_.empty()) { - return false; - } - event = events_.front(); - events_.pop_front(); - return true; -} - -template <class T> -int GnssHalTest::CallbackQueue<T>::retrieve(list<T>& event_list, int count, int timeout_seconds) { - for (int i = 0; i < count; ++i) { - T event; - if (!retrieve(event, timeout_seconds)) { - return i; - } - event_list.push_back(event); - } - - return count; -} - -template <class T> -int GnssHalTest::CallbackQueue<T>::size() const { - std::unique_lock<std::recursive_mutex> lock(mtx_); - return events_.size(); -} - -template <class T> -int GnssHalTest::CallbackQueue<T>::calledCount() const { - std::unique_lock<std::recursive_mutex> lock(mtx_); - return called_count_; -} - -template <class T> -void GnssHalTest::CallbackQueue<T>::reset() { - std::unique_lock<std::recursive_mutex> lock(mtx_); - if (!events_.empty()) { - ALOGW("%u unprocessed events discarded in callback queue %s", (unsigned int)events_.size(), - name_.c_str()); - } - events_.clear(); - called_count_ = 0; -} - #endif // GNSS_HAL_TEST_H_ diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp index 39736cc250..c442cc6bf8 100644 --- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp +++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp @@ -16,10 +16,11 @@ #define LOG_TAG "GnssHalTestCases" -#include <VtsHalHidlTargetTestBase.h> #include <gnss_hal_test.h> #include "Utils.h" +#include <gtest/gtest.h> + using android::hardware::hidl_string; using android::hardware::hidl_vec; @@ -51,13 +52,13 @@ using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl; * * Empty test fixture to verify basic Setup & Teardown */ -TEST_F(GnssHalTest, SetupTeardownCreateCleanup) {} +TEST_P(GnssHalTest, SetupTeardownCreateCleanup) {} /* * TestGnssMeasurementExtension: * Gets the GnssMeasurementExtension and verifies that it returns an actual extension. */ -TEST_F(GnssHalTest, TestGnssMeasurementExtension) { +TEST_P(GnssHalTest, TestGnssMeasurementExtension) { auto gnssMeasurement_2_0 = gnss_hal_->getExtensionGnssMeasurement_2_0(); auto gnssMeasurement_1_1 = gnss_hal_->getExtensionGnssMeasurement_1_1(); auto gnssMeasurement_1_0 = gnss_hal_->getExtensionGnssMeasurement(); @@ -80,7 +81,7 @@ TEST_F(GnssHalTest, TestGnssMeasurementExtension) { * The GNSS HAL 2.0 implementation must support @2.0::IGnssConfiguration interface due to * the deprecation of some methods in @1.0::IGnssConfiguration interface. */ -TEST_F(GnssHalTest, TestGnssConfigurationExtension) { +TEST_P(GnssHalTest, TestGnssConfigurationExtension) { auto gnssConfiguration = gnss_hal_->getExtensionGnssConfiguration_2_0(); ASSERT_TRUE(gnssConfiguration.isOk()); sp<IGnssConfiguration_2_0> iGnssConfiguration = gnssConfiguration; @@ -96,7 +97,7 @@ TEST_F(GnssHalTest, TestGnssConfigurationExtension) { * TestGnssConfiguration_setSuplEs_Deprecation: * Calls setSuplEs and verifies that it returns false. */ -TEST_F(GnssHalTest, TestGnssConfiguration_setSuplEs_Deprecation) { +TEST_P(GnssHalTest, TestGnssConfiguration_setSuplEs_Deprecation) { auto gnssConfiguration = gnss_hal_->getExtensionGnssConfiguration_2_0(); ASSERT_TRUE(gnssConfiguration.isOk()); sp<IGnssConfiguration_2_0> iGnssConfiguration = gnssConfiguration; @@ -111,7 +112,7 @@ TEST_F(GnssHalTest, TestGnssConfiguration_setSuplEs_Deprecation) { * TestGnssConfiguration_setGpsLock_Deprecation: * Calls setGpsLock and verifies that it returns false. */ -TEST_F(GnssHalTest, TestGnssConfiguration_setGpsLock_Deprecation) { +TEST_P(GnssHalTest, TestGnssConfiguration_setGpsLock_Deprecation) { auto gnssConfiguration = gnss_hal_->getExtensionGnssConfiguration_2_0(); ASSERT_TRUE(gnssConfiguration.isOk()); sp<IGnssConfiguration_2_0> iGnssConfiguration = gnssConfiguration; @@ -130,7 +131,7 @@ TEST_F(GnssHalTest, TestGnssConfiguration_setGpsLock_Deprecation) { * @2.0::IAGnssRil interface due to the deprecation of framework network API methods needed * to support the @1.0::IAGnssRil interface. */ -TEST_F(GnssHalTest, TestAGnssRilExtension) { +TEST_P(GnssHalTest, TestAGnssRilExtension) { auto agnssRil_2_0 = gnss_hal_->getExtensionAGnssRil_2_0(); ASSERT_TRUE(agnssRil_2_0.isOk()); sp<IAGnssRil_2_0> iAGnssRil_2_0 = agnssRil_2_0; @@ -148,7 +149,7 @@ TEST_F(GnssHalTest, TestAGnssRilExtension) { * 1. Updates GNSS HAL that a network has connected. * 2. Updates GNSS HAL that network has disconnected. */ -TEST_F(GnssHalTest, TestAGnssRil_UpdateNetworkState_2_0) { +TEST_P(GnssHalTest, TestAGnssRil_UpdateNetworkState_2_0) { auto agnssRil = gnss_hal_->getExtensionAGnssRil_2_0(); ASSERT_TRUE(agnssRil.isOk()); sp<IAGnssRil_2_0> iAGnssRil = agnssRil; @@ -180,7 +181,7 @@ TEST_F(GnssHalTest, TestAGnssRil_UpdateNetworkState_2_0) { * 2. constellation is valid. * 3. state is valid. */ -TEST_F(GnssHalTest, TestGnssMeasurementFields) { +TEST_P(GnssHalTest, TestGnssMeasurementFields) { const int kFirstGnssMeasurementTimeoutSeconds = 10; auto gnssMeasurement = gnss_hal_->getExtensionGnssMeasurement_2_0(); @@ -234,7 +235,7 @@ TEST_F(GnssHalTest, TestGnssMeasurementFields) { * @2.0::IAGnss interface due to the deprecation of framework network API methods needed * to support the @1.0::IAGnss interface. */ -TEST_F(GnssHalTest, TestAGnssExtension) { +TEST_P(GnssHalTest, TestAGnssExtension) { auto agnss_2_0 = gnss_hal_->getExtensionAGnss_2_0(); ASSERT_TRUE(agnss_2_0.isOk()); sp<IAGnss_2_0> iAGnss_2_0 = agnss_2_0; @@ -258,7 +259,7 @@ TEST_F(GnssHalTest, TestAGnssExtension) { * TestGnssNiExtension_Deprecation: * Gets the @1.0::IGnssNi extension and verifies that it is a nullptr. */ -TEST_F(GnssHalTest, TestGnssNiExtension_Deprecation) { +TEST_P(GnssHalTest, TestGnssNiExtension_Deprecation) { // Verify IGnssNi 1.0 is not supported. auto gnssNi = gnss_hal_->getExtensionGnssNi(); ASSERT_TRUE(!gnssNi.isOk() || ((sp<IGnssNi>)gnssNi) == nullptr); @@ -269,7 +270,7 @@ TEST_F(GnssHalTest, TestGnssNiExtension_Deprecation) { * Gets the GnssVisibilityControlExtension and if it is not null, verifies that it supports * the gnss.visibility_control@1.0::IGnssVisibilityControl interface by invoking a method. */ -TEST_F(GnssHalTest, TestGnssVisibilityControlExtension) { +TEST_P(GnssHalTest, TestGnssVisibilityControlExtension) { auto gnssVisibilityControl = gnss_hal_->getExtensionVisibilityControl(); ASSERT_TRUE(gnssVisibilityControl.isOk()); sp<IGnssVisibilityControl> iGnssVisibilityControl = gnssVisibilityControl; @@ -290,7 +291,7 @@ TEST_F(GnssHalTest, TestGnssVisibilityControlExtension) { * capabilities are reported and the mandatory LOS_SATS or the EXCESS_PATH_LENGTH * capability flag is set. */ -TEST_F(GnssHalTest, TestGnssMeasurementCorrectionsCapabilities) { +TEST_P(GnssHalTest, TestGnssMeasurementCorrectionsCapabilities) { if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS)) { return; } @@ -318,7 +319,7 @@ TEST_F(GnssHalTest, TestGnssMeasurementCorrectionsCapabilities) { * If measurement corrections capability is supported, verifies that it supports the * gnss.measurement_corrections@1.0::IMeasurementCorrections interface by invoking a method. */ -TEST_F(GnssHalTest, TestGnssMeasurementCorrections) { +TEST_P(GnssHalTest, TestGnssMeasurementCorrections) { if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS)) { return; } @@ -348,7 +349,7 @@ TEST_F(GnssHalTest, TestGnssMeasurementCorrections) { * Sets a GnssMeasurementCallback, waits for a GnssData object, and verifies the flags in member * elapsedRealitme are valid. */ -TEST_F(GnssHalTest, TestGnssDataElapsedRealtimeFlags) { +TEST_P(GnssHalTest, TestGnssDataElapsedRealtimeFlags) { const int kFirstGnssMeasurementTimeoutSeconds = 10; auto gnssMeasurement = gnss_hal_->getExtensionGnssMeasurement_2_0(); @@ -383,7 +384,7 @@ TEST_F(GnssHalTest, TestGnssDataElapsedRealtimeFlags) { iGnssMeasurement->close(); } -TEST_F(GnssHalTest, TestGnssLocationElapsedRealtime) { +TEST_P(GnssHalTest, TestGnssLocationElapsedRealtime) { StartAndCheckFirstLocation(); ASSERT_TRUE((int)gnss_cb_->last_location_.elapsedRealtime.flags <= @@ -399,7 +400,7 @@ TEST_F(GnssHalTest, TestGnssLocationElapsedRealtime) { } // This test only verify that injectBestLocation_2_0 does not crash. -TEST_F(GnssHalTest, TestInjectBestLocation_2_0) { +TEST_P(GnssHalTest, TestInjectBestLocation_2_0) { StartAndCheckFirstLocation(); gnss_hal_->injectBestLocation_2_0(gnss_cb_->last_location_); StopAndClearLocations(); @@ -410,7 +411,7 @@ TEST_F(GnssHalTest, TestInjectBestLocation_2_0) { * Gets the @2.0::IGnssBatching extension and verifies that it doesn't return an error. Support * for this interface is optional. */ -TEST_F(GnssHalTest, TestGnssBatchingExtension) { +TEST_P(GnssHalTest, TestGnssBatchingExtension) { auto gnssBatching_2_0 = gnss_hal_->getExtensionGnssBatching_2_0(); ASSERT_TRUE(gnssBatching_2_0.isOk()); } @@ -422,7 +423,7 @@ TEST_F(GnssHalTest, TestGnssBatchingExtension) { * NO_LOCATION_PERIOD_SEC and verfiy that no location is received. Also perform validity checks on * each received location. */ -TEST_F(GnssHalTest, GetLocationLowPower) { +TEST_P(GnssHalTest, GetLocationLowPower) { if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::LOW_POWER_MODE)) { ALOGI("Test GetLocationLowPower skipped. LOW_POWER_MODE capability not supported."); return; @@ -453,18 +454,18 @@ TEST_F(GnssHalTest, GetLocationLowPower) { // ensure that no location is received yet gnss_cb_->location_cbq_.retrieve(gnss_cb_->last_location_, kNoLocationPeriodSec); - const int locationCalledCount = gnss_cb_->location_cbq_.calledCount(); + const int location_called_count = gnss_cb_->location_cbq_.calledCount(); // Tolerate (ignore) one extra location right after the first one // to handle startup edge case scheduling limitations in some implementations - if ((i == 1) && (locationCalledCount == 2)) { + if ((i == 1) && (location_called_count == 2)) { CheckLocation(gnss_cb_->last_location_, true); continue; // restart the quiet wait period after this too-fast location } - EXPECT_LE(locationCalledCount, i); - if (locationCalledCount != i) { + EXPECT_LE(location_called_count, i); + if (location_called_count != i) { ALOGW("GetLocationLowPower test - not enough locations received. %d vs. %d expected ", - locationCalledCount, i); + location_called_count, i); } if (!gnss_cb_->location_cbq_.retrieve( @@ -513,7 +514,7 @@ GnssConstellationType_1_0 MapConstellationType(GnssConstellationType_2_0 constel * or a source with constellation == UNKNOWN if none are found sufficient times */ IGnssConfiguration_1_1::BlacklistedSource FindStrongFrequentNonGpsSource( - const list<hidl_vec<IGnssCallback_2_0::GnssSvInfo>>& sv_info_lists, + const std::list<hidl_vec<IGnssCallback_2_0::GnssSvInfo>>& sv_info_lists, const int min_observations) { struct ComparableBlacklistedSource { IGnssConfiguration_1_1::BlacklistedSource id; @@ -599,7 +600,7 @@ IGnssConfiguration_1_1::BlacklistedSource FindStrongFrequentNonGpsSource( * 5b) Retry a few times, in case GNSS search strategy takes a while to reacquire even the * formerly strongest satellite */ -TEST_F(GnssHalTest, BlacklistIndividualSatellites) { +TEST_P(GnssHalTest, BlacklistIndividualSatellites) { if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) { ALOGI("Test BlacklistIndividualSatellites skipped. SATELLITE_BLACKLIST capability" " not supported."); @@ -626,7 +627,7 @@ TEST_F(GnssHalTest, BlacklistIndividualSatellites) { */ const int kGnssSvStatusTimeout = 2; - list<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> sv_info_lists; + std::list<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> sv_info_lists; int count = gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_lists, sv_info_list_cbq_size, kGnssSvStatusTimeout); ASSERT_EQ(count, sv_info_list_cbq_size); @@ -744,7 +745,7 @@ TEST_F(GnssHalTest, BlacklistIndividualSatellites) { * GnssStatus does not use any constellation but GPS. * 4a & b) Clean up by turning off location, and send in empty blacklist. */ -TEST_F(GnssHalTest, BlacklistConstellation) { +TEST_P(GnssHalTest, BlacklistConstellation) { if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) { ALOGI("Test BlacklistConstellation skipped. SATELLITE_BLACKLIST capability not supported."); return; diff --git a/gnss/common/utils/vts/include/GnssCallbackEventQueue.h b/gnss/common/utils/vts/include/GnssCallbackEventQueue.h new file mode 100644 index 0000000000..3dc429b05e --- /dev/null +++ b/gnss/common/utils/vts/include/GnssCallbackEventQueue.h @@ -0,0 +1,140 @@ +/* + * 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_gnss_common_vts_GnssCallbackEventQueue_H_ +#define android_hardware_gnss_common_vts_GnssCallbackEventQueue_H_ + +#include <log/log.h> + +#include <condition_variable> +#include <deque> +#include <list> +#include <mutex> + +namespace android { +namespace hardware { +namespace gnss { +namespace common { + +/* + * Producer/consumer queue for storing/retrieving callback events from GNSS HAL. + */ +template <class T> +class GnssCallbackEventQueue { + public: + GnssCallbackEventQueue(const std::string& name) : name_(name), called_count_(0){}; + ~GnssCallbackEventQueue() { reset(); } + + /* Adds callback event to the end of the queue. */ + void store(const T& event); + + /* + * Removes the callack event at the front of the queue, stores it in event parameter + * and returns true. Returns false on timeout and event is not populated. + */ + bool retrieve(T& event, int timeout_seconds); + + /* + * Removes parameter count number of callack events at the front of the queue, stores + * them in event_list parameter and returns the number of events retrieved. Waits up to + * timeout_seconds to retrieve each event. If timeout occurs, it returns the number of + * items retrieved which will be less than count. + */ + int retrieve(std::list<T>& event_list, int count, int timeout_seconds); + + /* Returns the number of events pending to be retrieved from the callback event queue. */ + int size() const; + + /* Returns the number of callback events received since last reset(). */ + int calledCount() const; + + /* Clears the callback event queue and resets the calledCount() to 0. */ + void reset(); + + private: + GnssCallbackEventQueue(const GnssCallbackEventQueue&) = delete; + GnssCallbackEventQueue& operator=(const GnssCallbackEventQueue&) = delete; + + std::string name_; + int called_count_; + mutable std::recursive_mutex mtx_; + std::condition_variable_any cv_; + std::deque<T> events_; +}; + +template <class T> +void GnssCallbackEventQueue<T>::store(const T& event) { + std::unique_lock<std::recursive_mutex> lock(mtx_); + events_.push_back(event); + ++called_count_; + lock.unlock(); + cv_.notify_all(); +} + +template <class T> +bool GnssCallbackEventQueue<T>::retrieve(T& event, int timeout_seconds) { + std::unique_lock<std::recursive_mutex> lock(mtx_); + cv_.wait_for(lock, std::chrono::seconds(timeout_seconds), [&] { return !events_.empty(); }); + if (events_.empty()) { + return false; + } + event = events_.front(); + events_.pop_front(); + return true; +} + +template <class T> +int GnssCallbackEventQueue<T>::retrieve(std::list<T>& event_list, int count, int timeout_seconds) { + for (int i = 0; i < count; ++i) { + T event; + if (!retrieve(event, timeout_seconds)) { + return i; + } + event_list.push_back(event); + } + + return count; +} + +template <class T> +int GnssCallbackEventQueue<T>::size() const { + std::unique_lock<std::recursive_mutex> lock(mtx_); + return events_.size(); +} + +template <class T> +int GnssCallbackEventQueue<T>::calledCount() const { + std::unique_lock<std::recursive_mutex> lock(mtx_); + return called_count_; +} + +template <class T> +void GnssCallbackEventQueue<T>::reset() { + std::unique_lock<std::recursive_mutex> lock(mtx_); + if (!events_.empty()) { + ALOGW("%u unprocessed events discarded in callback queue %s", (unsigned int)events_.size(), + name_.c_str()); + } + events_.clear(); + called_count_ = 0; +} + +} // namespace common +} // namespace gnss +} // namespace hardware +} // namespace android + +#endif // android_hardware_gnss_common_vts_GnssCallbackEventQueue_H_ diff --git a/graphics/allocator/4.0/Android.bp b/graphics/allocator/4.0/Android.bp new file mode 100644 index 0000000000..f5f94585a7 --- /dev/null +++ b/graphics/allocator/4.0/Android.bp @@ -0,0 +1,20 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.graphics.allocator@4.0", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "IAllocator.hal", + ], + interfaces: [ + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hardware.graphics.mapper@4.0", + "android.hidl.base@1.0", + ], + gen_java: false, +} diff --git a/graphics/allocator/4.0/IAllocator.hal b/graphics/allocator/4.0/IAllocator.hal new file mode 100644 index 0000000000..9931685abc --- /dev/null +++ b/graphics/allocator/4.0/IAllocator.hal @@ -0,0 +1,55 @@ +/* + * 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. + */ + +package android.hardware.graphics.allocator@4.0; + +import android.hardware.graphics.mapper@4.0; + +interface IAllocator { + /** + * Retrieves implementation-defined debug information, which will be + * displayed during, for example, `dumpsys SurfaceFlinger`. + * + * @return debugInfo is a string of debug information. + */ + dumpDebugInfo() generates (string debugInfo); + + /** + * Allocates buffers with the properties specified by the descriptor. + * + * Allocations should be optimized for usage bits provided in the + * descriptor. + * + * @param descriptor Properties of the buffers to allocate. This must be + * obtained from IMapper::createDescriptor(). + * @param count The number of buffers to allocate. + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_DESCRIPTOR` if the descriptor is invalid. + * - `NO_RESOURCES` if the allocation cannot be fulfilled at this time. + * - `UNSUPPORTED` if any of the properties encoded in the descriptor + * are not supported. + * @return stride The number of pixels between two consecutive rows of + * an allocated buffer, when the concept of consecutive rows is defined. + * Otherwise, it has no meaning. + * @return buffers Array of raw handles to the allocated buffers. + */ + allocate(BufferDescriptor descriptor, uint32_t count) + generates (Error error, + uint32_t stride, + vec<handle> buffers); +}; + diff --git a/graphics/common/aidl/Android.bp b/graphics/common/aidl/Android.bp new file mode 100644 index 0000000000..e0c7674bba --- /dev/null +++ b/graphics/common/aidl/Android.bp @@ -0,0 +1,21 @@ +aidl_interface { + name: "vintf-graphics-common", + host_supported: true, + vendor_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, + srcs: [ + "android/hardware/graphics/common/*.aidl", + ], + stability: "vintf", + backend: { + java: { + enabled: false, + }, + cpp: { + enabled: false, + }, + }, +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/BlendMode.aidl b/graphics/common/aidl/android/hardware/graphics/common/BlendMode.aidl new file mode 100644 index 0000000000..242813555b --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/BlendMode.aidl @@ -0,0 +1,35 @@ +/** + * 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.graphics.common; + +/** + * Blend modes, settable per layer. + */ +@VintfStability +@Backing(type="int") +enum BlendMode { + INVALID = 0, + + /** colorOut = colorSrc */ + NONE = 1, + + /** colorOut = colorSrc + colorDst * (1 - alphaSrc) */ + PREMULTIPLIED = 2, + + /** colorOut = colorSrc * alphaSrc + colorDst * (1 - alphaSrc) */ + COVERAGE = 3, +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/ChromaSiting.aidl b/graphics/common/aidl/android/hardware/graphics/common/ChromaSiting.aidl new file mode 100644 index 0000000000..562a664587 --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/ChromaSiting.aidl @@ -0,0 +1,39 @@ +/** + * 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.graphics.common; + +/** + * Used by IAllocator/IMapper (gralloc) to describe standard chroma siting + */ +@VintfStability +@Backing(type="long") +enum ChromaSiting { + /* This format does not have chroma siting. */ + NONE = 0, + + /* This format has chroma siting but the type being used is unknown. */ + UNKNOWN = 1, + + /* Cb and Cr are sited interstitially, halfway between alternate luma samples. + * This is used by 4:2:0 for JPEG/JFIF, H.261, MPEG-1. */ + SITED_INTERSTITIAL = 2, + + /* Cb and Cr are horizontally sited coincident with a luma sample. + * Cb and Cr are vertically sited interstitially. + * This is used by 4:2:0 for MPEG-2 frame pictures. */ + COSITED_HORIZONTAL = 3, +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/Compression.aidl b/graphics/common/aidl/android/hardware/graphics/common/Compression.aidl new file mode 100644 index 0000000000..4cca1baff0 --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/Compression.aidl @@ -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. + */ + +package android.hardware.graphics.common; + +/** + * Used by IAllocator/IMapper (gralloc) to describe standard compression strategies + */ +@VintfStability +@Backing(type="long") +enum Compression { + /* Represents all uncompressed buffers */ + NONE = 0, + + /* VESA Display Stream Compression (DSC) */ + DISPLAY_STREAM_COMPRESSION = 1, +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl new file mode 100644 index 0000000000..81a21ab180 --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl @@ -0,0 +1,682 @@ +/** + * 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.graphics.common; + +@VintfStability +@Backing(type="int") +enum Dataspace { + /** + * Default-assumption data space, when not explicitly specified. + * + * It is safest to assume the buffer is an image with sRGB primaries and + * encoding ranges, but the consumer and/or the producer of the data may + * simply be using defaults. No automatic gamma transform should be + * expected, except for a possible display gamma transform when drawn to a + * screen. + */ + UNKNOWN = 0x0, + + /** + * Arbitrary dataspace with manually defined characteristics. Definition + * for colorspaces or other meaning must be communicated separately. + * + * This is used when specifying primaries, transfer characteristics, + * etc. separately. + * + * A typical use case is in video encoding parameters (e.g. for H.264), + * where a colorspace can have separately defined primaries, transfer + * characteristics, etc. + */ + ARBITRARY = 0x1, + + /** + * Color-description aspects + * + * The following aspects define various characteristics of the color + * specification. These represent bitfields, so that a data space value + * can specify each of them independently. + */ + + STANDARD_SHIFT = 16, + + /** + * Standard aspect + * + * Defines the chromaticity coordinates of the source primaries in terms of + * the CIE 1931 definition of x and y specified in ISO 11664-1. + */ + STANDARD_MASK = 63 << 16, // 63 << STANDARD_SHIFT = 0x3F + + /** + * Chromacity coordinates are unknown or are determined by the application. + * Implementations shall use the following suggested standards: + * + * All YCbCr formats: BT709 if size is 720p or larger (since most video + * content is letterboxed this corresponds to width is + * 1280 or greater, or height is 720 or greater). + * BT601_625 if size is smaller than 720p or is JPEG. + * All RGB formats: BT709. + * + * For all other formats standard is undefined, and implementations should use + * an appropriate standard for the data represented. + */ + STANDARD_UNSPECIFIED = 0 << 16, // STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.300 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT709 = 1 << 16, // 1 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.290 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + * + * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation + * for RGB conversion from the one purely determined by the primaries + * to minimize the color shift into RGB space that uses BT.709 + * primaries. + */ + STANDARD_BT601_625 = 2 << 16, // 2 << STANDARD_SHIFT, + + /** + * Primaries: x y + * green 0.290 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT601_625_UNADJUSTED = 3 << 16, // 3 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.310 0.595 + * blue 0.155 0.070 + * red 0.630 0.340 + * white (D65) 0.3127 0.3290 + * + * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation + * for RGB conversion from the one purely determined by the primaries + * to minimize the color shift into RGB space that uses BT.709 + * primaries. + */ + STANDARD_BT601_525 = 4 << 16, // 4 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.310 0.595 + * blue 0.155 0.070 + * red 0.630 0.340 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation + * for RGB conversion (as in SMPTE 240M). + */ + STANDARD_BT601_525_UNADJUSTED = 5 << 16, // 5 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.170 0.797 + * blue 0.131 0.046 + * red 0.708 0.292 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT2020 = 6 << 16, // 6 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.170 0.797 + * blue 0.131 0.046 + * red 0.708 0.292 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation + * for RGB conversion using the linear domain. + */ + STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, // 7 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.21 0.71 + * blue 0.14 0.08 + * red 0.67 0.33 + * white (C) 0.310 0.316 + * + * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT470M = 8 << 16, // 8 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.243 0.692 + * blue 0.145 0.049 + * red 0.681 0.319 + * white (C) 0.310 0.316 + * + * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation + * for RGB conversion. + */ + STANDARD_FILM = 9 << 16, // 9 << STANDARD_SHIFT + + /** + * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3) + * Primaries: x y + * green 0.265 0.690 + * blue 0.150 0.060 + * red 0.680 0.320 + * white (D65) 0.3127 0.3290 + */ + STANDARD_DCI_P3 = 10 << 16, // 10 << STANDARD_SHIFT + + /** + * Adobe RGB + * Primaries: x y + * green 0.210 0.710 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + */ + STANDARD_ADOBE_RGB = 11 << 16, // 11 << STANDARD_SHIFT + + + + TRANSFER_SHIFT = 22, + + /** + * Transfer aspect + * + * Transfer characteristics are the opto-electronic transfer characteristic + * at the source as a function of linear optical intensity (luminance). + * + * For digital signals, E corresponds to the recorded value. Normally, the + * transfer function is applied in RGB space to each of the R, G and B + * components independently. This may result in color shift that can be + * minized by applying the transfer function in Lab space only for the L + * component. Implementation may apply the transfer function in RGB space + * for all pixel formats if desired. + */ + + TRANSFER_MASK = 31 << 22, // 31 << TRANSFER_SHIFT = 0x1F + + /** + * Transfer characteristics are unknown or are determined by the + * application. + * + * Implementations should use the following transfer functions: + * + * For YCbCr formats: use TRANSFER_SMPTE_170M + * For RGB formats: use TRANSFER_SRGB + * + * For all other formats transfer function is undefined, and implementations + * should use an appropriate standard for the data represented. + */ + TRANSFER_UNSPECIFIED = 0 << 22, // 0 << TRANSFER_SHIFT + + /** + * Transfer characteristic curve: + * E = L + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_LINEAR = 1 << 22, // 1 << TRANSFER_SHIFT + + /** + * Transfer characteristic curve: + * + * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1 + * = 12.92 * L for 0 <= L < 0.0031308 + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_SRGB = 2 << 22, // 2 << TRANSFER_SHIFT + + /** + * BT.601 525, BT.601 625, BT.709, BT.2020 + * + * Transfer characteristic curve: + * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1 + * = 4.500 * L for 0 <= L < 0.018 + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_SMPTE_170M = 3 << 22, // 3 << TRANSFER_SHIFT + + /** + * Assumed display gamma 2.2. + * + * Transfer characteristic curve: + * E = L ^ (1/2.2) + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_GAMMA2_2 = 4 << 22, // 4 << TRANSFER_SHIFT + + /** + * display gamma 2.6. + * + * Transfer characteristic curve: + * E = L ^ (1/2.6) + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_GAMMA2_6 = 5 << 22, // 5 << TRANSFER_SHIFT + + /** + * display gamma 2.8. + * + * Transfer characteristic curve: + * E = L ^ (1/2.8) + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_GAMMA2_8 = 6 << 22, // 6 << TRANSFER_SHIFT + + /** + * SMPTE ST 2084 (Dolby Perceptual Quantizer) + * + * Transfer characteristic curve: + * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m + * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375 + * c2 = 32 * 2413 / 4096 = 18.8515625 + * c3 = 32 * 2392 / 4096 = 18.6875 + * m = 128 * 2523 / 4096 = 78.84375 + * n = 0.25 * 2610 / 4096 = 0.1593017578125 + * L - luminance of image 0 <= L <= 1 for HDR colorimetry. + * L = 1 corresponds to 10000 cd/m2 + * E - corresponding electrical signal + */ + TRANSFER_ST2084 = 7 << 22, // 7 << TRANSFER_SHIFT + + /** + * ARIB STD-B67 Hybrid Log Gamma + * + * Transfer characteristic curve: + * E = r * L^0.5 for 0 <= L <= 1 + * = a * ln(L - b) + c for 1 < L + * a = 0.17883277 + * b = 0.28466892 + * c = 0.55991073 + * r = 0.5 + * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds + * to reference white level of 100 cd/m2 + * E - corresponding electrical signal + */ + TRANSFER_HLG = 8 << 22, // 8 << TRANSFER_SHIFT + + RANGE_SHIFT = 27, + + /** + * Range aspect + * + * Defines the range of values corresponding to the unit range of 0-1. + * This is defined for YCbCr only, but can be expanded to RGB space. + */ + RANGE_MASK = 7 << 27, // 7 << RANGE_SHIFT = 0x7 + + /** + * Range is unknown or are determined by the application. Implementations + * shall use the following suggested ranges: + * + * All YCbCr formats: limited range. + * All RGB or RGBA formats (including RAW and Bayer): full range. + * All Y formats: full range + * + * For all other formats range is undefined, and implementations should use + * an appropriate range for the data represented. + */ + RANGE_UNSPECIFIED = 0 << 27, // 0 << RANGE_SHIFT = 0x0 + + /** + * Full range uses all values for Y, Cb and Cr from + * 0 to 2^b-1, where b is the bit depth of the color format. + */ + RANGE_FULL = 1 << 27, // 1 << RANGE_SHIFT = 0x8000000 + + /** + * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and + * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of + * the color format. + * + * E.g. For 8-bit-depth formats: + * Luma (Y) samples should range from 16 to 235, inclusive + * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive + * + * For 10-bit-depth formats: + * Luma (Y) samples should range from 64 to 940, inclusive + * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive + */ + RANGE_LIMITED = 2 << 27, // 2 << RANGE_SHIFT = 0x10000000 + + /** + * Extended range is used for scRGB. Intended for use with + * floating point pixel formats. [0.0 - 1.0] is the standard + * sRGB space. Values outside the range 0.0 - 1.0 can encode + * color outside the sRGB gamut. + * Used to blend / merge multiple dataspaces on a single display. + */ + RANGE_EXTENDED = 3 << 27, // 3 << RANGE_SHIFT = 0x18000000 + + /** + * sRGB linear encoding: + * + * The red, green, and blue components are stored in sRGB space, but + * are linear, not gamma-encoded. + * The RGB primaries and the white point are the same as BT.709. + * + * The values are encoded using the full range ([0,255] for 8-bit) for all + * components. + */ + SRGB_LINEAR = 1 << 16 | 1 << 22 | 1 << 27, // deprecated, use V0_SRGB_LINEAR + + V0_SRGB_LINEAR = 1 << 16 | 1 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL + + + /** + * scRGB linear encoding: + * + * The red, green, and blue components are stored in extended sRGB space, + * but are linear, not gamma-encoded. + * The RGB primaries and the white point are the same as BT.709. + * + * The values are floating point. + * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits. + * Values beyond the range [0.0 - 1.0] would correspond to other colors + * spaces and/or HDR content. + */ + V0_SCRGB_LINEAR = 1 << 16 | 1 << 22 | 3 << 27, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED + + + /** + * sRGB gamma encoding: + * + * The red, green and blue components are stored in sRGB space, and + * converted to linear space when read, using the SRGB transfer function + * for each of the R, G and B components. When written, the inverse + * transformation is performed. + * + * The alpha component, if present, is always stored in linear space and + * is left unmodified when read or written. + * + * Use full range and BT.709 standard. + */ + SRGB = 1 << 16 | 2 << 22 | 1 << 27, // deprecated, use V0_SRGB + + V0_SRGB = 1 << 16 | 2 << 22 | 1 << 27, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL + + + /** + * scRGB: + * + * The red, green, and blue components are stored in extended sRGB space, + * but are linear, not gamma-encoded. + * The RGB primaries and the white point are the same as BT.709. + * + * The values are floating point. + * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits. + * Values beyond the range [0.0 - 1.0] would correspond to other colors + * spaces and/or HDR content. + */ + V0_SCRGB = 1 << 16 | 2 << 22 | 3 << 27, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED + + /** + * YCbCr Colorspaces + * ----------------- + * + * Primaries are given using (x,y) coordinates in the CIE 1931 definition + * of x and y specified by ISO 11664-1. + * + * Transfer characteristics are the opto-electronic transfer characteristic + * at the source as a function of linear optical intensity (luminance). + */ + + /** + * JPEG File Interchange Format (JFIF) + * + * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255 + * + * Use full range, BT.601 transfer and BT.601_625 standard. + */ + JFIF = 2 << 16 | 3 << 22 | 1 << 27, // deprecated, use V0_JFIF + + V0_JFIF = 2 << 16 | 3 << 22 | 1 << 27, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL + + /** + * ITU-R Recommendation 601 (BT.601) - 625-line + * + * Standard-definition television, 625 Lines (PAL) + * + * Use limited range, BT.601 transfer and BT.601_625 standard. + */ + BT601_625 = 2 << 16 | 3 << 22 | 2 << 27, // deprecated, use V0_BT601_625 + + V0_BT601_625 = 2 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED + + + /** + * ITU-R Recommendation 601 (BT.601) - 525-line + * + * Standard-definition television, 525 Lines (NTSC) + * + * Use limited range, BT.601 transfer and BT.601_525 standard. + */ + BT601_525 = 4 << 16 | 3 << 22 | 2 << 27, // deprecated, use V0_BT601_525 + + V0_BT601_525 = 4 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED + + /** + * ITU-R Recommendation 709 (BT.709) + * + * High-definition television + * + * Use limited range, BT.709 transfer and BT.709 standard. + */ + BT709 = 1 << 16 | 3 << 22 | 2 << 27, // deprecated, use V0_BT709 + + V0_BT709 = 1 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED + + + /** + * SMPTE EG 432-1 and SMPTE RP 431-2. + * + * Digital Cinema DCI-P3 + * + * Use full range, linear transfer and D65 DCI-P3 standard + */ + DCI_P3_LINEAR = 10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL + + + /** + * SMPTE EG 432-1 and SMPTE RP 431-2. + * + * Digital Cinema DCI-P3 + * + * Use full range, gamma 2.6 transfer and D65 DCI-P3 standard + * Note: Application is responsible for gamma encoding the data as + * a 2.6 gamma encoding is not supported in HW. + */ + DCI_P3 = 10 << 16 | 5 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL + + + /** + * Display P3 + * + * Display P3 uses same primaries and white-point as DCI-P3 + * linear transfer function makes this the same as DCI_P3_LINEAR. + */ + DISPLAY_P3_LINEAR = 10 << 16 | 1 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_LINEAR | RANGE_FULL + + + /** + * Display P3 + * + * Use same primaries and white-point as DCI-P3 + * but sRGB transfer function. + */ + DISPLAY_P3 = 10 << 16 | 2 << 22 | 1 << 27, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL + + + /** + * Adobe RGB + * + * Use full range, gamma 2.2 transfer and Adobe RGB primaries + * Note: Application is responsible for gamma encoding the data as + * a 2.2 gamma encoding is not supported in HW. + */ + ADOBE_RGB = 11 << 16 | 4 << 22 | 1 << 27, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL + + + /** + * ITU-R Recommendation 2020 (BT.2020) + * + * Ultra High-definition television + * + * Use full range, linear transfer and BT2020 standard + */ + BT2020_LINEAR = 6 << 16 | 1 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_LINEAR | RANGE_FULL + + + /** + * ITU-R Recommendation 2020 (BT.2020) + * + * Ultra High-definition television + * + * Use full range, BT.709 transfer and BT2020 standard + */ + BT2020 = 6 << 16 | 3 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL + + /** + * ITU-R Recommendation 2020 (BT.2020) + * + * Ultra High-definition television + * + * Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard + */ + BT2020_PQ = 6 << 16 | 7 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL + + + /** + * Data spaces for non-color formats + */ + + /** + * The buffer contains depth ranging measurements from a depth camera. + * This value is valid with formats: + * HAL_PIXEL_FORMAT_Y16: 16-bit samples, consisting of a depth measurement + * and an associated confidence value. The 3 MSBs of the sample make + * up the confidence value, and the low 13 LSBs of the sample make up + * the depth measurement. + * For the confidence section, 0 means 100% confidence, 1 means 0% + * confidence. The mapping to a linear float confidence value between + * 0.f and 1.f can be obtained with + * float confidence = (((depthSample >> 13) - 1) & 0x7) / 7.0f; + * The depth measurement can be extracted simply with + * uint16_t range = (depthSample & 0x1FFF); + * HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as + * a variable-length float (x,y,z, confidence) coordinate point list. + * The point cloud will be represented with the android_depth_points + * structure. + */ + DEPTH = 0x1000, + + /** + * The buffer contains sensor events from sensor direct report. + * This value is valid with formats: + * HAL_PIXEL_FORMAT_BLOB: an array of sensor event structure that forms + * a lock free queue. Format of sensor event structure is specified + * in Sensors HAL. + */ + SENSOR = 0x1001, + + /** + * ITU-R Recommendation 2020 (BT.2020) + * + * Ultra High-definition television + * + * Use limited range, BT.709 transfer and BT2020 standard + */ + BT2020_ITU = 6 << 16 | 3 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_LIMITED + + /** + * ITU-R Recommendation 2100 (BT.2100) + * + * High dynamic range television + * + * Use limited/full range, PQ/HLG transfer, and BT2020 standard + * limited range is the preferred / normative definition for BT.2100 + */ + BT2020_ITU_PQ = 6 << 16 | 7 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED + BT2020_ITU_HLG = 6 << 16 | 8 << 22 | 2 << 27, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED + BT2020_HLG = 6 << 16 | 8 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL + + /** + * ITU-R Recommendation 2020 (BT.2020) + * + * Ultra High-definition television + * + * Use full range, sRGB transfer and BT2020 standard + */ + DISPLAY_BT2020 = 6 << 16 | 2 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_SRGB | RANGE_FULL + + /** + * ISO 16684-1:2011(E) + * + * Embedded depth metadata following the dynamic depth specification. + */ + DYNAMIC_DEPTH = 0x1002, + + /** + * JPEG APP segments format as specified by JEIDA spec + * + * The buffer must only contain APP1 (Application Marker) segment followed + * by zero or more APPn segments, as is specified by JEITA CP-3451C section 4.5.4. + * The APP1 segment optionally contains a thumbnail. The buffer will not + * contain main compressed image. + * + * This value is valid with formats: + * HAL_PIXEL_FORMAT_BLOB: JPEG APP segments optionally containing thumbnail image + * in APP1. BLOB buffer with this dataspace is output by HAL, and used by + * camera framework to encode into a HEIC image. + */ + JPEG_APP_SEGMENTS = 0x1003, + + /** + * ISO/IEC 23008-12 + * + * High Efficiency Image File Format (HEIF) + * + * This value is valid with formats: + * HAL_PIXEL_FORMAT_BLOB: A HEIC image encoded by HEIC or HEVC encoder + * according to ISO/IEC 23008-12. + */ + HEIF = 0x1004, +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/ExtendableType.aidl b/graphics/common/aidl/android/hardware/graphics/common/ExtendableType.aidl new file mode 100644 index 0000000000..495693c9bd --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/ExtendableType.aidl @@ -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.graphics.common; + +/** + * This struct is used for types that are commonly extended by vendors. For example, buffer + * compression is typically SoC specific. It is not possible for Android to define every possible + * proprietary vendor compression strategy. Instead, compression is represented using this + * ExtendableType that can support standard compression strategies while still allowing + * every vendor to easily add their own non-standard definitions. + */ +@VintfStability +parcelable ExtendableType { + /** + * Name of the stable aidl interface whose value is stored in this structure. + * + * For standard types, the "name" field will be set to the stable aidl name of the type such as + * "android.hardware.graphics.common.Compression". + * + * For custom vendor types, the "name" field will be set to the name of the custom + * @VendorStability vendor AIDL interface such as + * "vendor.mycompanyname.graphics.common.Compression". The name of the vendor extension should + * contain the name of the owner of the extension. Including the company + * name in the "name" field prevents type collisions between different vendors. + */ + @utf8InCpp String name; + + /** + * Enum value of the from the stable aidl interface + * + * For standard types, the "value" field will be set to an enum value from that stable aidl + * type such as "NONE". + * + * For vendor types, the "value" field should be set to the enum value from the custom + * @VendorStability vendor AIDL interface extended type such as "MY_COMPRESSION_TYPE1". + */ + long value = 0; +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/Interlaced.aidl b/graphics/common/aidl/android/hardware/graphics/common/Interlaced.aidl new file mode 100644 index 0000000000..a3f1baa53a --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/Interlaced.aidl @@ -0,0 +1,35 @@ +/** + * 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.graphics.common; + +/** + * Used by IAllocator/IMapper (gralloc) to describe standard interlaced strategies + */ +@VintfStability +@Backing(type="long") +enum Interlaced { + /* The buffer is not interlaced. */ + NONE = 0, + + /* The buffer's planes are interlaced horizontally. The height of each interlaced plane is + * 1/2 the height of the buffer's height. */ + TOP_BOTTOM = 1, + + /* The buffer's planes are interlaced vertically. The width of each interlaced plane is + * 1/2 the width of the buffer's width. */ + RIGHT_LEFT = 2, +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl new file mode 100644 index 0000000000..168028ddcc --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl @@ -0,0 +1,125 @@ +/** + * 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.graphics.common; + +import android.hardware.graphics.common.PlaneLayoutComponent; +import android.hardware.graphics.common.Rect; + +/** + * Used by IAllocator/IMapper (gralloc) to describe the plane layout of a buffer. + * + * PlaneLayout uses the following definitions: + * + * - Component - a component is one channel of a pixel. For example, an RGBA format has + * four components: R, G, B and A. + * - Sample - a sample is comprised of all the components in a given plane. For example, + * a buffer with one Y plane and one CbCr plane has one plane with a sample of Y + * and one plane with a sample of CbCr. + * - Pixel - a pixel is comprised of all the (non-metadata/raw) components in buffer across + * all planes. For example, a buffer with a plane of Y and a plane of CbCr has a pixel + * of YCbCr. + */ + +@VintfStability +parcelable PlaneLayout { + /** + * An list of plane layout components. This list of components should include + * every component in this plane. For example, a CbCr plane should return a + * vector of size two with one PlaneLayoutComponent for Cb and one for Cr. + */ + PlaneLayoutComponent[] components; + + /** + * Offset to the first byte of the plane (in bytes), from the start of the allocation. + */ + long offsetInBytes; + + /** + * Bits per sample increment (aka column increment): describes the distance + * in bits from one sample to the next sample (to the right) on the same row for the + * the component plane. + * + * The default value is 0. Return the default value if the increment is undefined, unknown, + * or variable. + * + * This can be negative. A negative increment indicates that the samples are read from + * right to left. + */ + long sampleIncrementInBits; + + /** + * Horizontal stride: number of bytes between two vertically adjacent + * samples in given plane. This can be mathematically described by: + * + * strideInBytes = ALIGN(widthInSamples * bps / 8, alignment) + * + * where, + * + * bps: average bits per sample + * alignment (in bytes): dependent upon pixel format and usage + * + * strideInBytes can contain additional padding beyond the widthInSamples. + * + * The default value is 0. Return the default value if the stride is undefined, unknown, + * or variable. + * + * This can be negative. A negative stride indicates that the rows are read from + * bottom to top. + */ + long strideInBytes; + + /** + * Dimensions of plane (in samples). + * + * This is the number of samples in the plane, even if subsampled. + * + * See 'strideInBytes' for relationship between strideInBytes and widthInSamples. + */ + long widthInSamples; + long heightInSamples; + + /** + * Can be used to get the total size in bytes of any memory used by the plane + * including extra padding. This should not include any extra metadata used to describe the + * plane. + */ + long totalSizeInBytes; + + /** + * Horizontal and vertical subsampling. Must be a positive power of 2. + * + * These fields indicate the number of horizontally or vertically adjacent pixels that use + * the same pixel data. A value of 1 indicates no subsampling. + */ + long horizontalSubsampling; + long verticalSubsampling; + + /** + * Some buffer producers require extra padding to their output buffer; therefore the + * physical size of the native buffer will be larger than its logical size. + * The crop rectangle determines the offset and logical size of the buffer that should be + * read by consumers. + * + * The crop rectangle is measured in samples and is relative to the offset of the + * plane. Valid crop rectangles are within the boundaries of the plane: + * [0, 0, widthInSamples, heightInSamples]. + * + * The default crop rectangle is a rectangle the same size as the plane: + * [0, 0, widthInSamples, heightInSamples]. + */ + Rect crop; +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/PlaneLayoutComponent.aidl b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayoutComponent.aidl new file mode 100644 index 0000000000..3fca53b937 --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayoutComponent.aidl @@ -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. + */ + +package android.hardware.graphics.common; + +import android.hardware.graphics.common.ExtendableType; + +/** + * Used by IAllocator/IMapper (gralloc) to describe the type and location of a component in a + * buffer's plane. + * + * PlaneLayoutComponent uses the following definitions: + * + * - Component - a component is one channel of a pixel. For example, an RGBA format has + * four components: R, G, B and A. + * - Sample - a sample is comprised of all the components in a given plane. For example, + * a buffer with one Y plane and one CbCr plane has one plane with a sample of Y + * and one plane with a sample of CbCr. + * - Pixel - a pixel is comprised of all the (non-metadata/raw) components in buffer across + * all planes. For example, a buffer with a plane of Y and a plane of CbCr has a pixel + * of YCbCr. + */ + +@VintfStability +parcelable PlaneLayoutComponent { + /** + * The type of this plane layout component. + * + * android.hardware.graphics.common.PlaneLayoutComponent defines the standard + * plane layout component types. Vendors may extend this type to include any + * non-standard plane layout component types. For instructions on how to + * create a vendor extension, refer to ExtendableType.aidl. + */ + ExtendableType type; + + /** + * Offset in bits to the first instance of this component in the plane. + * This is relative to the plane's offset (PlaneLayout::offset). + * + * If the offset cannot be described using a int64_t, this should be set to -1. + * For example, if the plane is compressed and the offset is not defined or + * relevant, return -1. + */ + long offsetInBits; + + /** + * The number of bits used per component in the plane. + * + * If the plane layout component cannot be described using componentSizeInBits, this + * should be set to -1. For example, if the component varies in size throughout + * the plane, return -1. + */ + long sizeInBits; +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/PlaneLayoutComponentType.aidl b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayoutComponentType.aidl new file mode 100644 index 0000000000..18c4a2e154 --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayoutComponentType.aidl @@ -0,0 +1,46 @@ +/** + * 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.graphics.common; + +/** + * Used by IAllocator/IMapper (gralloc) to describe standard plane layout component types + * + * The enum values have been taken directly from gralloc1's android_flex_component for compatiblity + * reasons. However, unlike gralloc1's android_flex_component, this field is NOT a bit field. + * A plane's components should NOT be expressed by bitwise OR-ing different + * PlaneLayoutComponentTypes together. + */ +@VintfStability +@Backing(type="long") +enum PlaneLayoutComponentType { + /* Luma */ + Y = 1 << 0, + /* Chroma blue */ + CB = 1 << 1, + /* Chroma red */ + CR = 1 << 2, + + /* Red */ + R = 1 << 10, + /* Green */ + G = 1 << 11, + /* Blue */ + B = 1 << 12, + + /* Alpha */ + A = 1 << 30, +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/Rect.aidl b/graphics/common/aidl/android/hardware/graphics/common/Rect.aidl new file mode 100644 index 0000000000..1a3bc11e4f --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/Rect.aidl @@ -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. + */ + +package android.hardware.graphics.common; + +/** + * General purpose definition of a rectangle. + */ + +@VintfStability +parcelable Rect { + int left; + int top; + int right; + int bottom; +} diff --git a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl new file mode 100644 index 0000000000..060d12c46e --- /dev/null +++ b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl @@ -0,0 +1,282 @@ +/** + * Copyright (c) 2019,libgralloctypes_helper 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.graphics.common; + +/** + * Used by IAllocator/IMapper (gralloc) to describe standard metadata types. + * + * This is an enum that defines the common types of gralloc 4 buffer metadata. The comments for + * each enum include a description of the metadata that is associated with the type. + * + * IMapper@4.x must support getting the following standard buffer metadata types. IMapper@4.x may + * support setting these standard buffer metadata types as well. + */ +@VintfStability +@Backing(type="long") +enum StandardMetadataType { + INVALID = 0, + + /** + * Can be used to get the random ID of the buffer. This ID should be psuedorandom with + * sufficient entropy. + * + * This ID should only be used for debugging purposes. It cannot be used as a basis for any + * control flows. + * + * The buffer ID is determined at allocation time and should not change during the lifetime + * of the buffer. + * + * The buffer ID is a uint64_t. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + BUFFER_ID = 1, + + /** + * Can be used to get the name passed in by the client at allocation time in the + * BufferDescriptorInfo. + * + * The buffer name is determined at allocation time and should not change during the lifetime + * of the buffer. + * + * The buffer name is a string. + * + * When it is encoded into a byte stream, the length of the string is written using 8 bytes in + * little endian. It is followed by a char array of the string's + * characters. The array is not null-terminated. + */ + NAME = 2, + + /** + * Can be used to get the number of elements per buffer row requested at allocation time in + * the BufferDescriptorInfo. + * + * The width is determined at allocation time and should not change during the lifetime + * of the buffer. + * + * The width is a uint64_t. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + WIDTH = 3, + + /** + * Can be used to get the number of elements per buffer column requested at allocation time in + * the BufferDescriptorInfo. + * + * The height is determined at allocation time and should not change during the lifetime + * of the buffer. + * + * The height is a uint64_t. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + HEIGHT = 4, + + /** + * Can be used to get the number of layers requested at allocation time in the + * BufferDescriptorInfo. + * + * The layer count is determined at allocation time and should not change during the lifetime + * of the buffer. + * + * The layer count is a uint64_t. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + LAYER_COUNT = 5, + + /** + * Can be used to get the buffer format requested at allocation time in the + * BufferDescriptorInfo. + * + * The requested pixel format is determined at allocation time and should not change during + * the lifetime of the buffer. + * + * The requested pixel format is a android.hardware.graphics.common@1.2::PixelFormat. + * + * When it is encoded into a byte stream, it is first cast to a int32_t and then represented in + * the byte stream by 4 bytes written in little endian. + */ + PIXEL_FORMAT_REQUESTED = 6, + + /** + * Can be used to get the fourcc code for the format. Fourcc codes are standard across all + * devices of the same kernel version. Fourcc codes must follow the Linux definition of a + * fourcc format found in: include/uapi/drm/drm_fourcc.h. + * + * The pixel format fourcc code is represented by a uint32_t. + * + * When it is encoded into a byte stream, it is represented by 4 bytes written in little endian. + */ + PIXEL_FORMAT_FOURCC = 7, + + /** + * Can be used to get the modifier for the format. Together fourcc and modifier describe the + * real pixel format. Each fourcc and modifier pair is unique and must fully define the format + * and layout of the buffer. Modifiers can change any property of the buffer. Modifiers must + * follow the Linux definition of a modifier found in: include/uapi/drm/drm_fourcc.h. + * + * The pixel format modifier is represented by a uint64_t. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + PIXEL_FORMAT_MODIFIER = 8, + + /** + * Can be used to get the usage requested at allocation time in the BufferDescriptorInfo. + * + * The usage is determined at allocation time and should not change during the lifetime + * of the buffer. + * + * The usage is a uint64_t bit field of android.hardware.graphics.common@1.2::BufferUsage's. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + USAGE = 9, + + /** + * Can be used to get the total size in bytes of any memory used by the buffer including its + * metadata and extra padding. This is the total number of bytes used by the buffer allocation. + * + * The allocation size is a uint64_t. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + ALLOCATION_SIZE = 10, + + /** + * Can be used to get if a buffer has protected content. If the buffer does not have protected + * content, this should return 0. If a buffer has protected content, this should return 1. + * + * In future versions, this field will be extended to expose more information about the type + * of protected content in the buffer. + * + * The protected content is a uint64_t. + * + * When it is encoded into a byte stream, it is represented by 8 bytes written in little endian. + */ + PROTECTED_CONTENT = 11, + + /** + * Can be used to get the compression strategy of the buffer. If the device has more than one + * compression strategy, it should have different unique values for each compression + * strategy. + * + * Compression is a stable aidl android.hardware.graphics.common.ExtendableType. + * + * android.hardware.graphics.common.Compression defines the standard compression + * strategies. Vendors may extend this type to include any compression strategies. + * + * When it is encoded into a byte stream, the length of the name field string is written using + * 8 bytes in little endian. It is followed by a char array of the string's + * characters. The array is not null-terminated. Finally the value field is written as 8 bytes + * in little endian. + */ + COMPRESSION = 12, + + /** + * Can be used to get how the buffer's planes are interlaced. + * + * Interlaced is a stable aidl android.hardware.graphics.common.ExtendableType. + * + * android.hardware.graphics.common.Interlaced defines the standard interlaced + * strategies. Vendors may extend this type to include any non-standard interlaced + * strategies. + * + * When it is encoded into a byte stream, the length of the name field string is written using + * 8 bytes in little endian. It is followed by a char array of the string's + * characters. The array is not null-terminated. Finally the value field is written as 8 bytes + * in little endian. + */ + INTERLACED = 13, + + /** + * Can be used to get the chroma siting of a buffer. + * + * Chroma siting is a stable aidl android.hardware.graphics.common.ExtendableType. + * + * android.hardware.graphics.common.ChromaSiting defines the standard chroma + * sitings. Vendors may extend this type to include any non-standard chroma sitings. + * + * When it is encoded into a byte stream, the length of the name field string is written using + * 8 bytes in little endian. It is followed by a char array of the string's + * characters. The array is not null-terminated. Finally the value field is written as 8 bytes + * in little endian. + */ + CHROMA_SITING = 14, + + /** + * Can be used to get the PlaneLayout(s) of the buffer. There should be one PlaneLayout per + * plane in the buffer. For example if the buffer only has one plane, only one PlaneLayout + * should be returned. + * + * If the buffer has planes interlaced through time, the returned PlaneLayout structs should be + * ordered by time. The nth PlaneLayout should be from the same time or earlier than the + * n+1 PlaneLayout. + * + * The plane layout is a list of stable aidl android.hardware.graphics.common.PlaneLayout's. + * + * When it is encoded into a byte stream, the total number of PlaneLayouts is written using + * 8 bytes in little endian. It is followed by each PlaneLayout. + * + * To encode a PlaneLayout, write the length of its PlaneLayoutComponent[] components + * field as 8 bytes in little endian and then encode each of its components. Finally, write the + * following fields in this order each as 8 bytes in little endian: offsetInBytes, + * sampleIncrementInBits, strideInBytes, widthInSamples, totalSizeInBytes, + * horizontalSubsampling, verticalSubsampling. + * + * To encode a PlaneLayoutComponent, encode its PlaneLayoutComponentType type field. Next + * encode offsetInBits followed by sizeInBits each as 8 bytes in little endian. + * + * To encode a PlaneLayoutComponentType, write the length of the name field string as + * 8 bytes in little endian. It is followed by a char array of the string's + * characters. The array is not null-terminated. Finally the value field is written as 8 bytes + * in little endian. + */ + PLANE_LAYOUTS = 15, + + /** + * Can be used to get or set the dataspace of the buffer. The framework may attempt to set + * this value. + * + * The default dataspace is Dataspace::UNKNOWN. If this dataspace is set to any valid value + * other than Dataspace::UNKNOWN, this dataspace overrides all other dataspaces. For example, + * if the buffer has Dataspace::DISPLAY_P3 and it is being displayed on a composer Layer that + * is Dataspace::sRGB, the buffer should be treated as a DISPLAY_P3 buffer. + * + * The dataspace is a stable aidl android.hardware.graphics.common.Dataspace. + * + * When it is encoded into a byte stream, it is first cast to a int32_t and then represented in + * the byte stream by 4 bytes written in little endian. + */ + DATASPACE = 16, + + /** + * Can be used to get or set the BlendMode. The framework may attempt to set this value. + * + * The default blend mode is INVALID. If the BlendMode is set to any + * valid value other than INVALID, this BlendMode overrides all other + * dataspaces. For a longer description of this behavior see MetadataType::DATASPACE. + * + * The blend mode is a stable aidl android.hardware.graphics.common.BlendMode. + * + * When it is encoded into a byte stream, it is first cast to a int32_t and then represented by + * 4 bytes written in little endian. + */ + BLEND_MODE = 17, +} diff --git a/graphics/composer/2.1/default/Android.bp b/graphics/composer/2.1/default/Android.bp index 6157719926..533687bb7d 100644 --- a/graphics/composer/2.1/default/Android.bp +++ b/graphics/composer/2.1/default/Android.bp @@ -9,8 +9,7 @@ cc_library_shared { ], 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 7a501fcc88..ea3666dd28 100644 --- a/graphics/composer/2.1/utils/hal/Android.bp +++ b/graphics/composer/2.1/utils/hal/Android.bp @@ -19,14 +19,12 @@ cc_library_headers { 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/Composer.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/Composer.h index 90d9b982a7..4b8c6bbf26 100644 --- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/Composer.h +++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/Composer.h @@ -67,6 +67,14 @@ class ComposerImpl : public Interface { } } + // we do not have HWC2_CAPABILITY_SKIP_VALIDATE defined in + // IComposer::Capability. However, this is defined in hwcomposer2.h, + // so if the device returns it, add it manually to be returned to the + // client + if (mHal->hasCapability(HWC2_CAPABILITY_SKIP_VALIDATE)) { + caps.push_back(static_cast<IComposer::Capability>(HWC2_CAPABILITY_SKIP_VALIDATE)); + } + hidl_vec<IComposer::Capability> caps_reply; caps_reply.setToExternal(caps.data(), caps.size()); hidl_cb(caps_reply); @@ -80,8 +88,7 @@ class ComposerImpl : public Interface { Return<void> createClient(IComposer::createClient_cb hidl_cb) override { std::unique_lock<std::mutex> lock(mClientMutex); - bool destroyed = waitForClientDestroyedLocked(lock); - if (!destroyed) { + if (!waitForClientDestroyedLocked(lock)) { hidl_cb(Error::NO_RESOURCES, nullptr); return Void(); } 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 095189f91b..47ead41fb0 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 d87110a014..53b9202b97 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 @@ class ComposerCommandEngine : protected CommandReaderBase { 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 @@ class ComposerCommandEngine : protected CommandReaderBase { 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 @@ class ComposerCommandEngine : protected CommandReaderBase { 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 @@ class ComposerCommandEngine : protected CommandReaderBase { 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 18d184ecb4..0000000000 --- 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 0000000000..ed827feb66 --- /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 0000000000..21f60355f8 --- /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 0000000000..3738278559 --- /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 fcb327f365..cdc0f35a41 100644 --- a/graphics/composer/2.1/utils/vts/Android.bp +++ b/graphics/composer/2.1/utils/vts/Android.bp @@ -27,10 +27,21 @@ cc_library_static { "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", ], + export_header_lib_headers: [ + "android.hardware.graphics.composer@2.1-command-buffer", + ], cflags: [ "-O0", "-g", diff --git a/graphics/composer/2.1/utils/vts/ComposerVts.cpp b/graphics/composer/2.1/utils/vts/ComposerVts.cpp index c5d5823398..a8e1480d4f 100644 --- a/graphics/composer/2.1/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.1/utils/vts/ComposerVts.cpp @@ -317,11 +317,16 @@ void ComposerClient::execute(TestCommandReader* reader, CommandWriterBase* write 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 @@ Gralloc::Gralloc() { 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,14 @@ const native_handle_t* Gralloc::allocate(uint32_t width, uint32_t height, uint32 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; + return mGralloc4->lock(bufferHandle, cpuUsage, accessRegion, acquireFence); + } else if (mGralloc3) { IMapper3::Rect accessRegion; accessRegion.left = accessRegionRect.left; accessRegion.top = accessRegionRect.top; @@ -371,7 +391,9 @@ void* Gralloc::lock(const native_handle_t* bufferHandle, uint64_t cpuUsage, } 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 +401,9 @@ int Gralloc::unlock(const native_handle_t* bufferHandle) { } 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 7811048270..63aa713e54 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::Hdr; 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; @@ -54,6 +57,7 @@ class Composer { public: Composer(); explicit Composer(const std::string& name); + explicit Composer(const sp<IComposer>& composer); sp<IComposer> getRaw() const; @@ -64,9 +68,6 @@ class Composer { std::string dumpDebugInfo(); std::unique_ptr<ComposerClient> createClient(); - protected: - explicit Composer(const sp<IComposer>& composer); - private: const sp<IComposer> mComposer; @@ -153,6 +154,7 @@ class Gralloc { 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 d54da60f6d..5f1ed63936 100644 --- a/graphics/composer/2.1/vts/functional/Android.bp +++ b/graphics/composer/2.1/vts/functional/Android.bp @@ -27,15 +27,21 @@ cc_test { 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", ], - test_suites: ["general-tests"], + disable_framework: true, + test_suites: ["general-tests", "vts-core"], } diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp index fa5ace65b1..b92279d45f 100644 --- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp +++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp @@ -20,11 +20,14 @@ #include <composer-vts/2.1/ComposerVts.h> #include <composer-vts/2.1/GraphicsComposerCallback.h> #include <composer-vts/2.1/TestCommandReader.h> +#include <gtest/gtest.h> +#include <hardware/hwcomposer2.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.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> #include <unistd.h> #include <algorithm> @@ -50,30 +53,11 @@ using android::hardware::graphics::common::V1_0::PixelFormat; using android::hardware::graphics::common::V1_0::Transform; using GrallocError = android::hardware::graphics::mapper::V2_0::Error; -// 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 GraphicsComposerHidlTest : public ::testing::VtsHalHidlTargetTestBase { - protected: +class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> { + protected: void SetUp() override { - VtsHalHidlTargetTestBase::SetUp(); ASSERT_NO_FATAL_FAILURE( - mComposer = std::make_unique<Composer>( - GraphicsComposerHidlEnvironment::Instance()->getServiceName<IComposer>())); + mComposer = std::make_unique<Composer>(IComposer::getService(GetParam()))); ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient()); mComposerCallback = new GraphicsComposerCallback; @@ -100,7 +84,6 @@ class GraphicsComposerHidlTest : public ::testing::VtsHalHidlTargetTestBase { EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount()); EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount()); } - VtsHalHidlTargetTestBase::TearDown(); } // returns an invalid display id (one that has not been registered to a @@ -149,7 +132,7 @@ class GraphicsComposerHidlTest : public ::testing::VtsHalHidlTargetTestBase { * * Test that IComposer::getCapabilities returns no invalid capabilities. */ -TEST_F(GraphicsComposerHidlTest, GetCapabilities) { +TEST_P(GraphicsComposerHidlTest, GetCapabilities) { auto capabilities = mComposer->getCapabilities(); ASSERT_EQ(capabilities.end(), std::find(capabilities.begin(), capabilities.end(), IComposer::Capability::INVALID)); @@ -158,7 +141,7 @@ TEST_F(GraphicsComposerHidlTest, GetCapabilities) { /** * Test IComposer::dumpDebugInfo. */ -TEST_F(GraphicsComposerHidlTest, DumpDebugInfo) { +TEST_P(GraphicsComposerHidlTest, DumpDebugInfo) { mComposer->dumpDebugInfo(); } @@ -167,7 +150,7 @@ TEST_F(GraphicsComposerHidlTest, DumpDebugInfo) { * * Test that IComposerClient is a singleton. */ -TEST_F(GraphicsComposerHidlTest, CreateClientSingleton) { +TEST_P(GraphicsComposerHidlTest, CreateClientSingleton) { mComposer->getRaw()->createClient( [&](const auto& tmpError, const auto&) { EXPECT_EQ(Error::NO_RESOURCES, tmpError); }); } @@ -178,7 +161,7 @@ TEST_F(GraphicsComposerHidlTest, CreateClientSingleton) { * * Test that virtual displays can be created and has the correct display type. */ -TEST_F(GraphicsComposerHidlTest, CreateVirtualDisplay) { +TEST_P(GraphicsComposerHidlTest, CreateVirtualDisplay) { if (mComposerClient->getMaxVirtualDisplayCount() == 0) { GTEST_SUCCEED() << "no virtual display support"; return; @@ -203,7 +186,7 @@ TEST_F(GraphicsComposerHidlTest, CreateVirtualDisplay) { * Test that passing a bad display handle to destroyVirtualDisplay * returns a BAD_DISPLAY error */ -TEST_F(GraphicsComposerHidlTest, DestroyVirtualDisplayBadDisplay) { +TEST_P(GraphicsComposerHidlTest, DestroyVirtualDisplayBadDisplay) { if (mComposerClient->getMaxVirtualDisplayCount() == 0) { GTEST_SUCCEED() << "no virtual display support"; return; @@ -218,7 +201,7 @@ TEST_F(GraphicsComposerHidlTest, DestroyVirtualDisplayBadDisplay) { * * Test that layers can be created and destroyed. */ -TEST_F(GraphicsComposerHidlTest, CreateLayer) { +TEST_P(GraphicsComposerHidlTest, CreateLayer) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -232,7 +215,7 @@ TEST_F(GraphicsComposerHidlTest, CreateLayer) { * Test that passing in an invalid display handle to createLayer returns * BAD_DISPLAY. */ -TEST_F(GraphicsComposerHidlTest, CreateLayerBadDisplay) { +TEST_P(GraphicsComposerHidlTest, CreateLayerBadDisplay) { Error error; mComposerClient->getRaw()->createLayer( mInvalidDisplayId, kBufferSlotCount, @@ -246,7 +229,7 @@ TEST_F(GraphicsComposerHidlTest, CreateLayerBadDisplay) { * Test that passing in an invalid display handle to destroyLayer returns * BAD_DISPLAY */ -TEST_F(GraphicsComposerHidlTest, DestroyLayerBadDisplay) { +TEST_P(GraphicsComposerHidlTest, DestroyLayerBadDisplay) { Error error; Layer layer; ASSERT_NO_FATAL_FAILURE(layer = @@ -265,7 +248,7 @@ TEST_F(GraphicsComposerHidlTest, DestroyLayerBadDisplay) { * Test that passing in an invalid layer handle to destroyLayer returns * BAD_LAYER */ -TEST_F(GraphicsComposerHidlTest, DestroyLayerBadLayerError) { +TEST_P(GraphicsComposerHidlTest, DestroyLayerBadLayerError) { // We haven't created any layers yet, so any id should be invalid Error error = mComposerClient->getRaw()->destroyLayer(mPrimaryDisplay, 1); @@ -278,7 +261,7 @@ TEST_F(GraphicsComposerHidlTest, DestroyLayerBadLayerError) { * Test that passing in a bad display handle to getActiveConfig generates a * BAD_DISPLAY error */ -TEST_F(GraphicsComposerHidlTest, GetActiveConfigBadDisplay) { +TEST_P(GraphicsComposerHidlTest, GetActiveConfigBadDisplay) { Error error; mComposerClient->getRaw()->getActiveConfig( mInvalidDisplayId, [&](const auto& tmpOutError, const auto&) { error = tmpOutError; }); @@ -291,7 +274,7 @@ TEST_F(GraphicsComposerHidlTest, GetActiveConfigBadDisplay) { * Test IComposerClient::getDisplayConfigs returns no error * when passed in a valid display */ -TEST_F(GraphicsComposerHidlTest, GetDisplayConfig) { +TEST_P(GraphicsComposerHidlTest, GetDisplayConfig) { std::vector<Config> configs; ASSERT_NO_FATAL_FAILURE(configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay)); } @@ -302,7 +285,7 @@ TEST_F(GraphicsComposerHidlTest, GetDisplayConfig) { * Test IComposerClient::getDisplayConfigs returns BAD_DISPLAY * when passed in an invalid display handle */ -TEST_F(GraphicsComposerHidlTest, GetDisplayConfigBadDisplay) { +TEST_P(GraphicsComposerHidlTest, GetDisplayConfigBadDisplay) { Error error; mComposerClient->getRaw()->getDisplayConfigs( mInvalidDisplayId, [&](const auto& tmpOutError, const auto&) { error = tmpOutError; }); @@ -312,7 +295,7 @@ TEST_F(GraphicsComposerHidlTest, GetDisplayConfigBadDisplay) { /** * Test IComposerClient::getDisplayName. */ -TEST_F(GraphicsComposerHidlTest, GetDisplayName) { +TEST_P(GraphicsComposerHidlTest, GetDisplayName) { mComposerClient->getDisplayName(mPrimaryDisplay); } @@ -322,7 +305,7 @@ TEST_F(GraphicsComposerHidlTest, GetDisplayName) { * Test that IComposerClient::getDisplayType returns the correct display type * for the primary display. */ -TEST_F(GraphicsComposerHidlTest, GetDisplayType) { +TEST_P(GraphicsComposerHidlTest, GetDisplayType) { ASSERT_EQ(IComposerClient::DisplayType::PHYSICAL, mComposerClient->getDisplayType(mPrimaryDisplay)); } @@ -333,7 +316,7 @@ TEST_F(GraphicsComposerHidlTest, GetDisplayType) { * Test that IComposerClient::getClientTargetSupport returns true for the * required client targets. */ -TEST_F(GraphicsComposerHidlTest, GetClientTargetSupport) { +TEST_P(GraphicsComposerHidlTest, GetClientTargetSupport) { std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay); for (auto config : configs) { int32_t width = mComposerClient->getDisplayAttribute(mPrimaryDisplay, config, @@ -356,7 +339,7 @@ TEST_F(GraphicsComposerHidlTest, GetClientTargetSupport) { * Test that IComposerClient::getClientTargetSupport returns BAD_DISPLAY when * passed an invalid display handle */ -TEST_F(GraphicsComposerHidlTest, GetClientTargetSupportBadDisplay) { +TEST_P(GraphicsComposerHidlTest, GetClientTargetSupportBadDisplay) { std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay); for (auto config : configs) { int32_t width = mComposerClient->getDisplayAttribute(mPrimaryDisplay, config, @@ -380,7 +363,7 @@ TEST_F(GraphicsComposerHidlTest, GetClientTargetSupportBadDisplay) { * Test that IComposerClient::getDisplayAttribute succeeds for the required * formats, and succeeds or fails correctly for optional attributes. */ -TEST_F(GraphicsComposerHidlTest, GetDisplayAttribute) { +TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute) { std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay); for (auto config : configs) { const std::array<IComposerClient::Attribute, 3> requiredAttributes = {{ @@ -406,7 +389,7 @@ TEST_F(GraphicsComposerHidlTest, GetDisplayAttribute) { /** * Test IComposerClient::getHdrCapabilities. */ -TEST_F(GraphicsComposerHidlTest, GetHdrCapabilities) { +TEST_P(GraphicsComposerHidlTest, GetHdrCapabilities) { float maxLuminance; float maxAverageLuminance; float minLuminance; @@ -417,7 +400,7 @@ TEST_F(GraphicsComposerHidlTest, GetHdrCapabilities) { /** * Test IComposerClient::setClientTargetSlotCount. */ -TEST_F(GraphicsComposerHidlTest, SetClientTargetSlotCount) { +TEST_P(GraphicsComposerHidlTest, SetClientTargetSlotCount) { mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kBufferSlotCount); } @@ -427,7 +410,7 @@ TEST_F(GraphicsComposerHidlTest, SetClientTargetSlotCount) { * Test that IComposerClient::setActiveConfig succeeds for all display * configs. */ -TEST_F(GraphicsComposerHidlTest, SetActiveConfig) { +TEST_P(GraphicsComposerHidlTest, SetActiveConfig) { std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay); for (auto config : configs) { mComposerClient->setActiveConfig(mPrimaryDisplay, config); @@ -441,7 +424,7 @@ TEST_F(GraphicsComposerHidlTest, SetActiveConfig) { * Test that config set during IComposerClient::setActiveConfig is maintained * during a display on/off power cycle */ -TEST_F(GraphicsComposerHidlTest, SetActiveConfigPowerCycle) { +TEST_P(GraphicsComposerHidlTest, SetActiveConfigPowerCycle) { ASSERT_NO_FATAL_FAILURE( mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::OFF)); ASSERT_NO_FATAL_FAILURE( @@ -465,7 +448,7 @@ TEST_F(GraphicsComposerHidlTest, SetActiveConfigPowerCycle) { * * Test that IComposerClient::getColorMode always returns ColorMode::NATIVE */ -TEST_F(GraphicsComposerHidlTest, GetColorModes) { +TEST_P(GraphicsComposerHidlTest, GetColorModes) { std::vector<ColorMode> modes = mComposerClient->getColorModes(mPrimaryDisplay); auto nativeModeLocation = std::find(modes.begin(), modes.end(), ColorMode::NATIVE); @@ -477,7 +460,7 @@ TEST_F(GraphicsComposerHidlTest, GetColorModes) { * * Test that IComposerClient::setColorMode succeeds for all color modes. */ -TEST_F(GraphicsComposerHidlTest, SetColorMode) { +TEST_P(GraphicsComposerHidlTest, SetColorMode) { std::unordered_set<ColorMode> validModes; for (auto mode : hidl_enum_range<ColorMode>()) { validModes.insert(mode); @@ -497,7 +480,7 @@ TEST_F(GraphicsComposerHidlTest, SetColorMode) { * Test that IComposerClient::setColorMode returns BAD_DISPLAY for * an invalid display handle */ -TEST_F(GraphicsComposerHidlTest, SetColorModeBadDisplay) { +TEST_P(GraphicsComposerHidlTest, SetColorModeBadDisplay) { std::vector<ColorMode> modes = mComposerClient->getColorModes(mPrimaryDisplay); for (auto mode : modes) { Error error = mComposerClient->getRaw()->setColorMode(mInvalidDisplayId, mode); @@ -511,7 +494,7 @@ TEST_F(GraphicsComposerHidlTest, SetColorModeBadDisplay) { * Test that IComposerClient::setColorMode returns BAD_PARAMETER when passed in * an invalid color mode */ -TEST_F(GraphicsComposerHidlTest, SetColorModeBadParameter) { +TEST_P(GraphicsComposerHidlTest, SetColorModeBadParameter) { Error error = mComposerClient->getRaw()->setColorMode(mPrimaryDisplay, static_cast<ColorMode>(-1)); ASSERT_EQ(Error::BAD_PARAMETER, error); @@ -523,7 +506,7 @@ TEST_F(GraphicsComposerHidlTest, SetColorModeBadParameter) { * Test that IComposerClient::getDozeSupport returns * BAD_DISPLAY when passed an invalid display handle */ -TEST_F(GraphicsComposerHidlTest, GetDozeSupportBadDisplay) { +TEST_P(GraphicsComposerHidlTest, GetDozeSupportBadDisplay) { Error error; mComposerClient->getRaw()->getDozeSupport( mInvalidDisplayId, [&](const auto& tmpOutError, const auto&) { error = tmpOutError; }); @@ -535,7 +518,7 @@ TEST_F(GraphicsComposerHidlTest, GetDozeSupportBadDisplay) { * * Test that IComposerClient::setPowerMode succeeds for all power modes. */ -TEST_F(GraphicsComposerHidlTest, SetPowerMode) { +TEST_P(GraphicsComposerHidlTest, SetPowerMode) { std::vector<IComposerClient::PowerMode> modes; modes.push_back(IComposerClient::PowerMode::OFF); @@ -558,7 +541,7 @@ TEST_F(GraphicsComposerHidlTest, SetPowerMode) { * Test IComposerClient::setPowerMode succeeds with different * orderings of power modes */ -TEST_F(GraphicsComposerHidlTest, SetPowerModeVariations) { +TEST_P(GraphicsComposerHidlTest, SetPowerModeVariations) { std::vector<IComposerClient::PowerMode> modes; modes.push_back(IComposerClient::PowerMode::OFF); modes.push_back(IComposerClient::PowerMode::ON); @@ -609,7 +592,7 @@ TEST_F(GraphicsComposerHidlTest, SetPowerModeVariations) { * Test IComposerClient::setPowerMode returns BAD_DISPLAY when passed an invalid * display handle */ -TEST_F(GraphicsComposerHidlTest, SetPowerModeBadDisplay) { +TEST_P(GraphicsComposerHidlTest, SetPowerModeBadDisplay) { Error error = mComposerClient->getRaw()->setPowerMode(mInvalidDisplayId, IComposerClient::PowerMode::ON); ASSERT_EQ(Error::BAD_DISPLAY, error); @@ -621,7 +604,7 @@ TEST_F(GraphicsComposerHidlTest, SetPowerModeBadDisplay) { * Test that IComposerClient::setPowerMode returns UNSUPPORTED when passed DOZE * or DOZE_SUSPEND on devices that do not support DOZE/DOZE_SUSPEND */ -TEST_F(GraphicsComposerHidlTest, SetPowerModeUnsupported) { +TEST_P(GraphicsComposerHidlTest, SetPowerModeUnsupported) { if (!mComposerClient->getDozeSupport(mPrimaryDisplay)) { Error error = mComposerClient->getRaw()->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::DOZE); @@ -639,7 +622,7 @@ TEST_F(GraphicsComposerHidlTest, SetPowerModeUnsupported) { * Tests that IComposerClient::setPowerMode returns BAD_PARAMETER when passed an invalid * PowerMode */ -TEST_F(GraphicsComposerHidlTest, SetPowerModeBadParameter) { +TEST_P(GraphicsComposerHidlTest, SetPowerModeBadParameter) { Error error = mComposerClient->getRaw()->setPowerMode( mPrimaryDisplay, static_cast<IComposerClient::PowerMode>(-1)); ASSERT_EQ(Error::BAD_PARAMETER, error); @@ -651,7 +634,7 @@ TEST_F(GraphicsComposerHidlTest, SetPowerModeBadParameter) { * Test that IComposerClient::setVsyncEnabled succeeds and there is no * spurious vsync events. */ -TEST_F(GraphicsComposerHidlTest, SetVsyncEnabled) { +TEST_P(GraphicsComposerHidlTest, SetVsyncEnabled) { mComposerCallback->setVsyncAllowed(true); mComposerClient->setVsyncEnabled(mPrimaryDisplay, true); @@ -703,7 +686,7 @@ class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest { /** * Test IComposerClient::Command::SET_COLOR_TRANSFORM. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_COLOR_TRANSFORM) { +TEST_P(GraphicsComposerHidlCommandTest, SET_COLOR_TRANSFORM) { const std::array<float, 16> identity = {{ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, @@ -718,7 +701,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_COLOR_TRANSFORM) { /** * Test IComposerClient::Command::SET_CLIENT_TARGET. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_CLIENT_TARGET) { +TEST_P(GraphicsComposerHidlCommandTest, SET_CLIENT_TARGET) { mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kBufferSlotCount); mWriter->selectDisplay(mPrimaryDisplay); @@ -731,7 +714,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_CLIENT_TARGET) { /** * Test IComposerClient::Command::SET_OUTPUT_BUFFER. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_OUTPUT_BUFFER) { +TEST_P(GraphicsComposerHidlCommandTest, SET_OUTPUT_BUFFER) { if (mComposerClient->getMaxVirtualDisplayCount() == 0) { GTEST_SUCCEED() << "no virtual display support"; return; @@ -754,7 +737,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_OUTPUT_BUFFER) { /** * Test IComposerClient::Command::VALIDATE_DISPLAY. */ -TEST_F(GraphicsComposerHidlCommandTest, VALIDATE_DISPLAY) { +TEST_P(GraphicsComposerHidlCommandTest, VALIDATE_DISPLAY) { mWriter->selectDisplay(mPrimaryDisplay); mWriter->validateDisplay(); execute(); @@ -763,7 +746,7 @@ TEST_F(GraphicsComposerHidlCommandTest, VALIDATE_DISPLAY) { /** * Test IComposerClient::Command::ACCEPT_DISPLAY_CHANGES. */ -TEST_F(GraphicsComposerHidlCommandTest, ACCEPT_DISPLAY_CHANGES) { +TEST_P(GraphicsComposerHidlCommandTest, ACCEPT_DISPLAY_CHANGES) { mWriter->selectDisplay(mPrimaryDisplay); mWriter->validateDisplay(); mWriter->acceptDisplayChanges(); @@ -773,7 +756,7 @@ TEST_F(GraphicsComposerHidlCommandTest, ACCEPT_DISPLAY_CHANGES) { /** * Test IComposerClient::Command::PRESENT_DISPLAY. */ -TEST_F(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY) { +TEST_P(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY) { mWriter->selectDisplay(mPrimaryDisplay); mWriter->validateDisplay(); mWriter->presentDisplay(); @@ -787,7 +770,13 @@ TEST_F(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY) { * additional call to validateDisplay when only the layer buffer handle and * surface damage have been set */ -TEST_F(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY_NO_LAYER_STATE_CHANGES) { +TEST_P(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY_NO_LAYER_STATE_CHANGES) { + if (!mComposer->hasCapability( + static_cast<IComposer::Capability>(HWC2_CAPABILITY_SKIP_VALIDATE))) { + std::cout << "Device does not have skip validate capability, skipping" << std::endl; + GTEST_SUCCEED(); + return; + } mWriter->selectDisplay(mPrimaryDisplay); mComposerClient->setPowerMode(mPrimaryDisplay, IComposerClient::PowerMode::ON); mComposerClient->setColorMode(mPrimaryDisplay, ColorMode::NATIVE); @@ -837,7 +826,7 @@ TEST_F(GraphicsComposerHidlCommandTest, PRESENT_DISPLAY_NO_LAYER_STATE_CHANGES) /** * Test IComposerClient::Command::SET_LAYER_CURSOR_POSITION. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_CURSOR_POSITION) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_CURSOR_POSITION) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -879,7 +868,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_CURSOR_POSITION) { /** * Test IComposerClient::Command::SET_LAYER_BUFFER. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_BUFFER) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_BUFFER) { auto handle = allocate(); ASSERT_NE(nullptr, handle); @@ -896,7 +885,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_BUFFER) { /** * Test IComposerClient::Command::SET_LAYER_SURFACE_DAMAGE. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_SURFACE_DAMAGE) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SURFACE_DAMAGE) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -915,7 +904,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_SURFACE_DAMAGE) { /** * Test IComposerClient::Command::SET_LAYER_BLEND_MODE. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_BLEND_MODE) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_BLEND_MODE) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -931,7 +920,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_BLEND_MODE) { /** * Test IComposerClient::Command::SET_LAYER_COLOR. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_COLOR) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_COLOR) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -946,7 +935,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_COLOR) { /** * Test IComposerClient::Command::SET_LAYER_COMPOSITION_TYPE. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_COMPOSITION_TYPE) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_COMPOSITION_TYPE) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -963,7 +952,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_COMPOSITION_TYPE) { /** * Test IComposerClient::Command::SET_LAYER_DATASPACE. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_DATASPACE) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_DATASPACE) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -977,7 +966,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_DATASPACE) { /** * Test IComposerClient::Command::SET_LAYER_DISPLAY_FRAME. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_DISPLAY_FRAME) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_DISPLAY_FRAME) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -991,7 +980,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_DISPLAY_FRAME) { /** * Test IComposerClient::Command::SET_LAYER_PLANE_ALPHA. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_PLANE_ALPHA) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_PLANE_ALPHA) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -1006,7 +995,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_PLANE_ALPHA) { /** * Test IComposerClient::Command::SET_LAYER_SIDEBAND_STREAM. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_SIDEBAND_STREAM) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SIDEBAND_STREAM) { if (!mComposer->hasCapability(IComposer::Capability::SIDEBAND_STREAM)) { GTEST_SUCCEED() << "no sideband stream support"; return; @@ -1028,7 +1017,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_SIDEBAND_STREAM) { /** * Test IComposerClient::Command::SET_LAYER_SOURCE_CROP. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_SOURCE_CROP) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_SOURCE_CROP) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -1042,7 +1031,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_SOURCE_CROP) { /** * Test IComposerClient::Command::SET_LAYER_TRANSFORM. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_TRANSFORM) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_TRANSFORM) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -1063,7 +1052,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_TRANSFORM) { /** * Test IComposerClient::Command::SET_LAYER_VISIBLE_REGION. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_VISIBLE_REGION) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_VISIBLE_REGION) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -1082,7 +1071,7 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_VISIBLE_REGION) { /** * Test IComposerClient::Command::SET_LAYER_Z_ORDER. */ -TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_Z_ORDER) { +TEST_P(GraphicsComposerHidlCommandTest, SET_LAYER_Z_ORDER) { Layer layer; ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount)); @@ -1094,6 +1083,16 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_Z_ORDER) { execute(); } +INSTANTIATE_TEST_SUITE_P( + PerInstance, GraphicsComposerHidlCommandTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)), + android::hardware::PrintInstanceNameToString); + +INSTANTIATE_TEST_SUITE_P( + PerInstance, GraphicsComposerHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)), + android::hardware::PrintInstanceNameToString); + } // namespace } // namespace vts } // namespace V2_1 @@ -1101,13 +1100,3 @@ TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_Z_ORDER) { } // namespace graphics } // namespace hardware } // namespace android - -int main(int argc, char** argv) { - using android::hardware::graphics::composer::V2_1::vts::GraphicsComposerHidlEnvironment; - ::testing::AddGlobalTestEnvironment(GraphicsComposerHidlEnvironment::Instance()); - ::testing::InitGoogleTest(&argc, argv); - GraphicsComposerHidlEnvironment::Instance()->init(&argc, argv); - int status = RUN_ALL_TESTS(); - ALOGI("Test result = %d", status); - return status; -} diff --git a/graphics/composer/2.2/default/Android.mk b/graphics/composer/2.2/default/Android.mk index 4916557f63..156ecb6bad 100644 --- a/graphics/composer/2.2/default/Android.mk +++ b/graphics/composer/2.2/default/Android.mk @@ -11,8 +11,8 @@ LOCAL_HEADER_LIBRARIES := android.hardware.graphics.composer@2.2-passthrough 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/OWNERS b/graphics/composer/2.2/utils/OWNERS index a17a50c1b9..3f1e82c85a 100644 --- a/graphics/composer/2.2/utils/OWNERS +++ b/graphics/composer/2.2/utils/OWNERS @@ -3,7 +3,3 @@ courtneygo@google.com lpy@google.com stoza@google.com vhau@google.com - -# VTS team -yim@google.com -zhuoyao@google.com diff --git a/graphics/composer/2.2/utils/command-buffer/Android.bp b/graphics/composer/2.2/utils/command-buffer/Android.bp index efaabd46f4..c4ceaabe9f 100644 --- a/graphics/composer/2.2/utils/command-buffer/Android.bp +++ b/graphics/composer/2.2/utils/command-buffer/Android.bp @@ -3,11 +3,16 @@ cc_library_headers { defaults: ["hidl_defaults"], vendor_available: true, shared_libs: [ - "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.composer@2.2", + ], + export_shared_lib_headers: [ "android.hardware.graphics.composer@2.2", ], 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"], } diff --git a/graphics/composer/2.2/utils/hal/Android.bp b/graphics/composer/2.2/utils/hal/Android.bp index 10dcae4dd7..f334a11c7c 100644 --- a/graphics/composer/2.2/utils/hal/Android.bp +++ b/graphics/composer/2.2/utils/hal/Android.bp @@ -19,9 +19,11 @@ cc_library_headers { 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 c760d0a421..512d39d2b0 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 @@ class ComposerClientImpl : public V2_1::hal::detail::ComposerClientImpl<Interfac 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 97e3a9ece6..d9f6226ad9 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 0000000000..752eb8170d --- /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 0000000000..a0610a3fda --- /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 index 85b665179c..33012e92c8 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::ComposerHandleCache; 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 @@ class ComposerDisplayResource : public V2_1::hal::ComposerDisplayResource { 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 @@ class ComposerResources : public V2_1::hal::ComposerResources { 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 @@ class ComposerResources : public V2_1::hal::ComposerResources { 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 @@ class ComposerResources : public V2_1::hal::ComposerResources { 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 dd979cb08c..5432882bea 100644 --- a/graphics/composer/2.2/utils/vts/Android.bp +++ b/graphics/composer/2.2/utils/vts/Android.bp @@ -19,26 +19,36 @@ cc_library_static { defaults: ["hidl_defaults"], srcs: [ "ComposerVts.cpp", + "ReadbackVts.cpp", + "RenderEngineVts.cpp", + ], + shared_libs: [ + "libui", ], static_libs: [ "VtsHalHidlTargetTestBase", - "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.1-vts", "android.hardware.graphics.composer@2.2", - "android.hardware.graphics.mapper@2.1", "android.hardware.graphics.mapper@2.1-vts", + "libarect", + "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", "android.hardware.graphics.composer@2.1-vts", + "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.mapper@2.1-vts", ], header_libs: [ - "android.hardware.graphics.composer@2.1-command-buffer", "android.hardware.graphics.composer@2.2-command-buffer", ], export_header_lib_headers: [ - "android.hardware.graphics.composer@2.1-command-buffer", "android.hardware.graphics.composer@2.2-command-buffer", ], cflags: [ diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp index cd6772a485..93b67f0fcc 100644 --- a/graphics/composer/2.2/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.2/utils/vts/ComposerVts.cpp @@ -182,17 +182,23 @@ std::array<float, 16> ComposerClient::getDataspaceSaturationMatrix(Dataspace dat 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 create gralloc3, initializing gralloc2_1"); - mGralloc2_1 = std::make_shared<Gralloc2_1>(/*errOnFailure*/ false); - if (!mGralloc2_1->getMapper()) { - mGralloc2_1 = nullptr; - ALOGD("Failed to create 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 @@ Gralloc::Gralloc() { 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 new file mode 100644 index 0000000000..7bb9121cba --- /dev/null +++ b/graphics/composer/2.2/utils/vts/ReadbackVts.cpp @@ -0,0 +1,355 @@ +/* + * 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. + */ + +#include <composer-vts/2.2/ReadbackVts.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_2 { +namespace vts { + +void TestLayer::write(const std::shared_ptr<CommandWriterBase>& writer) { + writer->selectLayer(mLayer); + writer->setLayerDisplayFrame(mDisplayFrame); + writer->setLayerSourceCrop(mSourceCrop); + writer->setLayerZOrder(mZOrder); + writer->setLayerSurfaceDamage(mSurfaceDamage); + writer->setLayerTransform(mTransform); + writer->setLayerPlaneAlpha(mAlpha); + writer->setLayerBlendMode(mBlendMode); +} + +const std::vector<ColorMode> ReadbackHelper::colorModes = {ColorMode::SRGB, ColorMode::DISPLAY_P3}; +const std::vector<Dataspace> ReadbackHelper::dataspaces = {Dataspace::V0_SRGB, + Dataspace::DISPLAY_P3}; + +std::string ReadbackHelper::getColorModeString(ColorMode mode) { + switch (mode) { + case ColorMode::SRGB: + return std::string("SRGB"); + case ColorMode::DISPLAY_P3: + return std::string("DISPLAY_P3"); + default: + return std::string("Unsupported color mode for readback"); + } +} + +std::string ReadbackHelper::getDataspaceString(Dataspace dataspace) { + switch (dataspace) { + case Dataspace::V0_SRGB: + return std::string("V0_SRGB"); + case Dataspace::DISPLAY_P3: + return std::string("DISPLAY_P3"); + case Dataspace::UNKNOWN: + return std::string("UNKNOWN"); + default: + return std::string("Unsupported dataspace for readback"); + } +} + +Dataspace ReadbackHelper::getDataspaceForColorMode(ColorMode mode) { + switch (mode) { + case ColorMode::DISPLAY_P3: + return Dataspace::DISPLAY_P3; + case ColorMode::SRGB: + default: + return Dataspace::UNKNOWN; + } +} + +LayerSettings TestLayer::toRenderEngineLayerSettings() { + LayerSettings layerSettings; + + layerSettings.alpha = half(mAlpha); + layerSettings.disableBlending = mBlendMode == IComposerClient::BlendMode::NONE; + layerSettings.geometry.boundaries = FloatRect( + 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 ? -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 = scale * translation; + + return layerSettings; +} + +int32_t ReadbackHelper::GetBytesPerPixel(PixelFormat pixelFormat) { + switch (pixelFormat) { + case PixelFormat::RGBA_8888: + return 4; + case PixelFormat::RGB_888: + return 3; + default: + return -1; + } +} + +void ReadbackHelper::fillBuffer(int32_t width, int32_t height, uint32_t stride, void* bufferData, + PixelFormat pixelFormat, + std::vector<IComposerClient::Color> desiredPixelColors) { + ASSERT_TRUE(pixelFormat == PixelFormat::RGB_888 || pixelFormat == PixelFormat::RGBA_8888); + int32_t bytesPerPixel = GetBytesPerPixel(pixelFormat); + ASSERT_NE(-1, bytesPerPixel); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + int pixel = row * width + col; + IComposerClient::Color srcColor = desiredPixelColors[pixel]; + + int offset = (row * stride + col) * bytesPerPixel; + uint8_t* pixelColor = (uint8_t*)bufferData + offset; + pixelColor[0] = srcColor.r; + pixelColor[1] = srcColor.g; + pixelColor[2] = srcColor.b; + + if (bytesPerPixel == 4) { + pixelColor[3] = srcColor.a; + } + } + } +} + +void ReadbackHelper::clearColors(std::vector<IComposerClient::Color>& expectedColors, int32_t width, + int32_t height, int32_t displayWidth) { + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + int pixel = row * displayWidth + col; + expectedColors[pixel] = BLACK; + } + } +} + +void ReadbackHelper::fillColorsArea(std::vector<IComposerClient::Color>& expectedColors, + int32_t stride, IComposerClient::Rect area, + IComposerClient::Color color) { + for (int row = area.top; row < area.bottom; row++) { + for (int col = area.left; col < area.right; col++) { + int pixel = row * stride + col; + expectedColors[pixel] = color; + } + } +} + +bool ReadbackHelper::readbackSupported(const PixelFormat& pixelFormat, const Dataspace& dataspace, + const Error error) { + if (error != Error::NONE) { + return false; + } + // TODO: add support for RGBA_1010102 + if (pixelFormat != PixelFormat::RGB_888 && pixelFormat != PixelFormat::RGBA_8888) { + return false; + } + if (std::find(dataspaces.begin(), dataspaces.end(), dataspace) == dataspaces.end()) { + return false; + } + return true; +} + +void ReadbackHelper::compareColorBuffers(std::vector<IComposerClient::Color>& expectedColors, + void* bufferData, const uint32_t stride, + const uint32_t width, const uint32_t height, + const PixelFormat pixelFormat) { + const int32_t bytesPerPixel = ReadbackHelper::GetBytesPerPixel(pixelFormat); + ASSERT_NE(-1, bytesPerPixel); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + int pixel = row * width + col; + int offset = (row * stride + col) * bytesPerPixel; + uint8_t* pixelColor = (uint8_t*)bufferData + offset; + + ASSERT_EQ(expectedColors[pixel].r, pixelColor[0]); + ASSERT_EQ(expectedColors[pixel].g, pixelColor[1]); + ASSERT_EQ(expectedColors[pixel].b, pixelColor[2]); + } + } +} + +ReadbackBuffer::ReadbackBuffer(Display display, const std::shared_ptr<ComposerClient>& client, + const std::shared_ptr<Gralloc>& gralloc, uint32_t width, + uint32_t height, PixelFormat pixelFormat, Dataspace dataspace) { + mDisplay = display; + + mComposerClient = client; + mGralloc = gralloc; + + mPixelFormat = pixelFormat; + mDataspace = dataspace; + + mWidth = width; + mHeight = height; + mLayerCount = 1; + mFormat = mPixelFormat; + mUsage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::GPU_TEXTURE); + + mAccessRegion.top = 0; + mAccessRegion.left = 0; + mAccessRegion.width = width; + mAccessRegion.height = height; +} + +ReadbackBuffer::~ReadbackBuffer() { + if (mBufferHandle != nullptr) { + mGralloc->freeBuffer(mBufferHandle); + } +} + +void ReadbackBuffer::setReadbackBuffer() { + if (mBufferHandle != nullptr) { + mGralloc->freeBuffer(mBufferHandle); + mBufferHandle = nullptr; + } + mBufferHandle = mGralloc->allocate(mWidth, mHeight, mLayerCount, mFormat, mUsage, + /*import*/ true, &mStride); + ASSERT_NE(false, mGralloc->validateBufferSize(mBufferHandle, mWidth, mHeight, mLayerCount, + mFormat, mUsage, mStride)); + ASSERT_NO_FATAL_FAILURE(mComposerClient->setReadbackBuffer(mDisplay, mBufferHandle, -1)); +} + +void ReadbackBuffer::checkReadbackBuffer(std::vector<IComposerClient::Color> expectedColors) { + // lock buffer for reading + int32_t fenceHandle; + ASSERT_NO_FATAL_FAILURE(mComposerClient->getReadbackBufferFence(mDisplay, &fenceHandle)); + + void* bufData = mGralloc->lock(mBufferHandle, mUsage, mAccessRegion, fenceHandle); + ASSERT_TRUE(mPixelFormat == PixelFormat::RGB_888 || mPixelFormat == PixelFormat::RGBA_8888); + ReadbackHelper::compareColorBuffers(expectedColors, bufData, mStride, mWidth, mHeight, + mPixelFormat); + int32_t unlockFence = mGralloc->unlock(mBufferHandle); + if (unlockFence != -1) { + sync_wait(unlockFence, -1); + close(unlockFence); + } +} + +void TestColorLayer::write(const std::shared_ptr<CommandWriterBase>& writer) { + TestLayer::write(writer); + writer->setLayerCompositionType(IComposerClient::Composition::SOLID_COLOR); + writer->setLayerColor(mColor); +} + +LayerSettings TestColorLayer::toRenderEngineLayerSettings() { + LayerSettings layerSettings = TestLayer::toRenderEngineLayerSettings(); + + layerSettings.source.solidColor = + half3(static_cast<half>(mColor.r) / 255.0, static_cast<half>(mColor.g) / 255.0, + static_cast<half>(mColor.b) / 255.0); + layerSettings.alpha = mAlpha * (static_cast<half>(mColor.a) / 255.0); + return layerSettings; +} + +TestBufferLayer::TestBufferLayer(const std::shared_ptr<ComposerClient>& client, + const std::shared_ptr<Gralloc>& gralloc, Display display, + int32_t width, int32_t height, PixelFormat format, + IComposerClient::Composition composition) + : TestLayer{client, display} { + mGralloc = gralloc; + mComposition = composition; + mWidth = width; + mHeight = height; + mLayerCount = 1; + mFormat = format; + mUsage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY); + + mAccessRegion.top = 0; + mAccessRegion.left = 0; + mAccessRegion.width = width; + mAccessRegion.height = height; + + setSourceCrop({0, 0, (float)width, (float)height}); +} + +TestBufferLayer::~TestBufferLayer() { + if (mBufferHandle != nullptr) { + mGralloc->freeBuffer(mBufferHandle); + } +} + +void TestBufferLayer::write(const std::shared_ptr<CommandWriterBase>& writer) { + TestLayer::write(writer); + writer->setLayerCompositionType(mComposition); + writer->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, mDisplayFrame)); + if (mBufferHandle != nullptr) writer->setLayerBuffer(0, mBufferHandle, mFillFence); +} + +LayerSettings TestBufferLayer::toRenderEngineLayerSettings() { + LayerSettings layerSettings = TestLayer::toRenderEngineLayerSettings(); + layerSettings.source.buffer.buffer = + new GraphicBuffer(mBufferHandle, GraphicBuffer::CLONE_HANDLE, mWidth, mHeight, + static_cast<int32_t>(mFormat), 1, mUsage, mStride); + + layerSettings.source.buffer.usePremultipliedAlpha = + mBlendMode == IComposerClient::BlendMode::PREMULTIPLIED; + + const float scaleX = (mSourceCrop.right - mSourceCrop.left) / (mWidth); + const float scaleY = (mSourceCrop.bottom - mSourceCrop.top) / (mHeight); + const float translateX = mSourceCrop.left / (mWidth); + const float translateY = mSourceCrop.top / (mHeight); + + layerSettings.source.buffer.textureTransform = + mat4::translate(vec4(translateX, translateY, 0, 1)) * + mat4::scale(vec4(scaleX, scaleY, 1.0, 1.0)); + + return layerSettings; +} + +void TestBufferLayer::fillBuffer(std::vector<IComposerClient::Color> expectedColors) { + void* bufData = mGralloc->lock(mBufferHandle, mUsage, mAccessRegion, -1); + ASSERT_NO_FATAL_FAILURE( + ReadbackHelper::fillBuffer(mWidth, mHeight, mStride, bufData, mFormat, expectedColors)); + mFillFence = mGralloc->unlock(mBufferHandle); + if (mFillFence != -1) { + sync_wait(mFillFence, -1); + close(mFillFence); + } +} + +void TestBufferLayer::setBuffer(std::vector<IComposerClient::Color> colors) { + if (mBufferHandle != nullptr) { + mGralloc->freeBuffer(mBufferHandle); + mBufferHandle = nullptr; + } + mBufferHandle = mGralloc->allocate(mWidth, mHeight, mLayerCount, mFormat, mUsage, + /*import*/ true, &mStride); + ASSERT_NE(nullptr, mBufferHandle); + ASSERT_NO_FATAL_FAILURE(fillBuffer(colors)); + ASSERT_NE(false, mGralloc->validateBufferSize(mBufferHandle, mWidth, mHeight, mLayerCount, + mFormat, mUsage, mStride)); +} + +void TestBufferLayer::setDataspace(Dataspace dataspace, + const std::shared_ptr<CommandWriterBase>& writer) { + writer->selectLayer(mLayer); + writer->setLayerDataspace(dataspace); +} + +void TestBufferLayer::setToClientComposition(const std::shared_ptr<CommandWriterBase>& writer) { + writer->selectLayer(mLayer); + writer->setLayerCompositionType(IComposerClient::Composition::CLIENT); +} + +} // namespace vts +} // namespace V2_2 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp new file mode 100644 index 0000000000..e2f267012f --- /dev/null +++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.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. + */ + +#include <composer-vts/2.2/RenderEngineVts.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_2 { +namespace vts { + +using mapper::V2_1::IMapper; +using renderengine::DisplaySettings; +using renderengine::LayerSettings; +using renderengine::RenderEngineCreationArgs; + +TestRenderEngine::TestRenderEngine(const RenderEngineCreationArgs& args) { + mFormat = static_cast<common::V1_1::PixelFormat>(args.pixelFormat); + mRenderEngine = renderengine::RenderEngine::create(args); +} + +void TestRenderEngine::setRenderLayers(std::vector<std::shared_ptr<TestLayer>> layers) { + sort(layers.begin(), layers.end(), + [](const std::shared_ptr<TestLayer>& lhs, const std::shared_ptr<TestLayer>& rhs) -> bool { + return lhs->mZOrder < rhs->mZOrder; + }); + + if (!mCompositionLayers.empty()) { + mCompositionLayers.clear(); + } + for (auto& layer : layers) { + LayerSettings settings = layer->toRenderEngineLayerSettings(); + mCompositionLayers.push_back(settings); + } +} + +void TestRenderEngine::initGraphicBuffer(uint32_t width, uint32_t height, uint32_t layerCount, + uint64_t usage) { + mGraphicBuffer = + new GraphicBuffer(width, height, static_cast<int32_t>(mFormat), layerCount, usage); +} + +void TestRenderEngine::drawLayers() { + base::unique_fd bufferFence; + base::unique_fd readyFence; + mRenderEngine->drawLayers(mDisplaySettings, mCompositionLayers, + mGraphicBuffer->getNativeBuffer(), true, std::move(bufferFence), + &readyFence); + int fd = readyFence.release(); + if (fd != -1) { + ASSERT_EQ(0, sync_wait(fd, -1)); + ASSERT_EQ(0, close(fd)); + } +} + +void TestRenderEngine::checkColorBuffer(std::vector<V2_2::IComposerClient::Color>& expectedColors) { + void* bufferData; + ASSERT_EQ(0, mGraphicBuffer->lock(mGraphicBuffer->getUsage(), &bufferData)); + ReadbackHelper::compareColorBuffers(expectedColors, bufferData, mGraphicBuffer->getStride(), + mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight(), + mFormat); + ASSERT_EQ(0, mGraphicBuffer->unlock()); +} + +} // namespace vts +} // namespace V2_2 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android 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 8fa9b7b3fe..5d22305020 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::PixelFormat; 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/utils/vts/include/composer-vts/2.2/ReadbackVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h new file mode 100644 index 0000000000..7519a64b4f --- /dev/null +++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h @@ -0,0 +1,208 @@ +/* + * 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. + */ + +#pragma once + +#include <android-base/unique_fd.h> +#include <android/hardware/graphics/composer/2.2/IComposerClient.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 <mapper-vts/2.1/MapperVts.h> +#include <renderengine/RenderEngine.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_2 { +namespace vts { + +using android::hardware::hidl_handle; +using common::V1_1::BufferUsage; +using common::V1_1::Dataspace; +using common::V1_1::PixelFormat; +using IMapper2_1 = mapper::V2_1::IMapper; +using Gralloc2_1 = mapper::V2_1::vts::Gralloc; +using renderengine::LayerSettings; +using V2_1::Display; +using V2_1::Layer; +using V2_1::vts::AccessRegion; +using V2_1::vts::TestCommandReader; + +static const IComposerClient::Color BLACK = {0, 0, 0, 0xff}; +static const IComposerClient::Color RED = {0xff, 0, 0, 0xff}; +static const IComposerClient::Color TRANSLUCENT_RED = {0xff, 0, 0, 0x33}; +static const IComposerClient::Color GREEN = {0, 0xff, 0, 0xff}; +static const IComposerClient::Color BLUE = {0, 0, 0xff, 0xff}; + +class TestLayer { + public: + TestLayer(const std::shared_ptr<ComposerClient>& client, Display display) + : mLayer(client->createLayer(display, kBufferSlotCount)), mComposerClient(client) {} + + // ComposerClient will take care of destroying layers, no need to explicitly + // call destroyLayers here + virtual ~TestLayer(){}; + + virtual void write(const std::shared_ptr<CommandWriterBase>& writer); + virtual LayerSettings toRenderEngineLayerSettings(); + + void setDisplayFrame(IComposerClient::Rect frame) { mDisplayFrame = frame; } + void setSourceCrop(IComposerClient::FRect crop) { mSourceCrop = crop; } + void setZOrder(uint32_t z) { mZOrder = z; } + + void setSurfaceDamage(std::vector<IComposerClient::Rect> surfaceDamage) { + mSurfaceDamage = surfaceDamage; + } + + void setTransform(Transform transform) { mTransform = transform; } + void setAlpha(float alpha) { mAlpha = alpha; } + void setBlendMode(IComposerClient::BlendMode blendMode) { mBlendMode = blendMode; } + + static constexpr uint32_t kBufferSlotCount = 64; + + IComposerClient::Rect mDisplayFrame = {0, 0, 0, 0}; + uint32_t mZOrder = 0; + std::vector<IComposerClient::Rect> mSurfaceDamage; + Transform mTransform = static_cast<Transform>(0); + IComposerClient::FRect mSourceCrop = {0, 0, 0, 0}; + float mAlpha = 1.0; + IComposerClient::BlendMode mBlendMode = IComposerClient::BlendMode::NONE; + + protected: + Layer mLayer; + + private: + std::shared_ptr<ComposerClient> const mComposerClient; +}; + +class TestColorLayer : public TestLayer { + public: + TestColorLayer(const std::shared_ptr<ComposerClient>& client, Display display) + : TestLayer{client, display} {} + + void write(const std::shared_ptr<CommandWriterBase>& writer) override; + + LayerSettings toRenderEngineLayerSettings() override; + + void setColor(IComposerClient::Color color) { mColor = color; } + + private: + IComposerClient::Color mColor = {0xff, 0xff, 0xff, 0xff}; +}; + +class TestBufferLayer : public TestLayer { + public: + TestBufferLayer( + const std::shared_ptr<ComposerClient>& client, const std::shared_ptr<Gralloc>& gralloc, + Display display, int32_t width, int32_t height, PixelFormat format, + IComposerClient::Composition composition = IComposerClient::Composition::DEVICE); + + ~TestBufferLayer(); + + void write(const std::shared_ptr<CommandWriterBase>& writer) override; + + LayerSettings toRenderEngineLayerSettings() override; + + void fillBuffer(std::vector<IComposerClient::Color> expectedColors); + + void setBuffer(std::vector<IComposerClient::Color> colors); + + void setDataspace(Dataspace dataspace, const std::shared_ptr<CommandWriterBase>& writer); + + void setToClientComposition(const std::shared_ptr<CommandWriterBase>& writer); + + uint32_t mWidth; + uint32_t mHeight; + uint32_t mLayerCount; + PixelFormat mFormat; + uint64_t mUsage; + AccessRegion mAccessRegion; + uint32_t mStride; + + protected: + IComposerClient::Composition mComposition; + std::shared_ptr<Gralloc> mGralloc; + int32_t mFillFence; + const native_handle_t* mBufferHandle = nullptr; +}; + +class ReadbackHelper : public ::testing::VtsHalHidlTargetTestBase { + public: + static std::string getColorModeString(ColorMode mode); + + static std::string getDataspaceString(Dataspace dataspace); + + static Dataspace getDataspaceForColorMode(ColorMode mode); + + static int32_t GetBytesPerPixel(PixelFormat pixelFormat); + + static void fillBuffer(int32_t width, int32_t height, uint32_t stride, void* bufferData, + PixelFormat pixelFormat, + std::vector<IComposerClient::Color> desiredPixelColors); + + static void clearColors(std::vector<IComposerClient::Color>& expectedColors, int32_t width, + int32_t height, int32_t displayWidth); + + static void fillColorsArea(std::vector<IComposerClient::Color>& expectedColors, int32_t stride, + IComposerClient::Rect area, IComposerClient::Color color); + + static bool readbackSupported(const PixelFormat& pixelFormat, const Dataspace& dataspace, + const Error error); + + static const std::vector<ColorMode> colorModes; + static const std::vector<Dataspace> dataspaces; + + static void compareColorBuffers(std::vector<IComposerClient::Color>& expectedColors, + void* bufferData, const uint32_t stride, const uint32_t width, + const uint32_t height, const PixelFormat pixelFormat); +}; + +class ReadbackBuffer { + public: + ReadbackBuffer(Display display, const std::shared_ptr<ComposerClient>& client, + const std::shared_ptr<Gralloc>& gralloc, uint32_t width, uint32_t height, + PixelFormat pixelFormat, Dataspace dataspace); + ~ReadbackBuffer(); + + void setReadbackBuffer(); + + void checkReadbackBuffer(std::vector<IComposerClient::Color> expectedColors); + + protected: + uint32_t mWidth; + uint32_t mHeight; + uint32_t mLayerCount; + PixelFormat mFormat; + uint64_t mUsage; + AccessRegion mAccessRegion; + uint32_t mStride; + const native_handle_t* mBufferHandle = nullptr; + PixelFormat mPixelFormat; + Dataspace mDataspace; + Display mDisplay; + std::shared_ptr<Gralloc> mGralloc; + std::shared_ptr<ComposerClient> mComposerClient; +}; + +} // namespace vts +} // namespace V2_2 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h new file mode 100644 index 0000000000..b936cabb1e --- /dev/null +++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#include <composer-vts/2.2/ReadbackVts.h> +#include <math/half.h> +#include <math/vec3.h> +#include <renderengine/RenderEngine.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferAllocator.h> +#include <ui/PixelFormat.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +#include <VtsHalHidlTargetTestBase.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_2 { +namespace vts { + +using mapper::V2_1::IMapper; +using renderengine::DisplaySettings; +using renderengine::RenderEngineCreationArgs; +using vts::Gralloc; + +class TestRenderEngine { + public: + static constexpr uint32_t sMaxFrameBufferAcquireBuffers = 2; + + TestRenderEngine(const RenderEngineCreationArgs& args); + ~TestRenderEngine() = default; + + void setRenderLayers(std::vector<std::shared_ptr<TestLayer>> layers); + void initGraphicBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint64_t usage); + void setDisplaySettings(DisplaySettings& displaySettings) { + mDisplaySettings = displaySettings; + }; + void drawLayers(); + void checkColorBuffer(std::vector<V2_2::IComposerClient::Color>& expectedColors); + + private: + common::V1_1::PixelFormat mFormat; + std::vector<renderengine::LayerSettings> mCompositionLayers; + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; + std::vector<renderengine::LayerSettings> mRenderLayers; + sp<GraphicBuffer> mGraphicBuffer; + DisplaySettings mDisplaySettings; +}; + +} // namespace vts +} // namespace V2_2 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp index a8e70ae155..21ba9f3d7d 100644 --- a/graphics/composer/2.2/vts/functional/Android.bp +++ b/graphics/composer/2.2/vts/functional/Android.bp @@ -19,18 +19,25 @@ cc_test { defaults: ["VtsHalTargetTestDefaults"], srcs: [ "VtsHalGraphicsComposerV2_2ReadbackTest.cpp", - "VtsHalGraphicsComposerV2_2TargetTest.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", + "libprocessgroup", "libsync", + "libui", ], static_libs: [ "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", @@ -42,6 +49,9 @@ cc_test { "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_2ReadbackTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp index 02c4c9cec9..6a6f7de6ac 100644 --- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp +++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * 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. @@ -18,13 +18,17 @@ #include <VtsHalHidlTargetTestBase.h> #include <VtsHalHidlTargetTestEnvBase.h> -#include <android-base/unique_fd.h> -#include <android/hardware/graphics/composer/2.2/IComposerClient.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 <mapper-vts/2.1/MapperVts.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 { @@ -34,21 +38,17 @@ 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::Layer; -using V2_1::vts::AccessRegion; using V2_1::vts::TestCommandReader; - -static const IComposerClient::Color BLACK = {0, 0, 0, 0xff}; -static const IComposerClient::Color RED = {0xff, 0, 0, 0xff}; -static const IComposerClient::Color TRANSLUCENT_RED = {0xff, 0, 0, 0x33}; -static const IComposerClient::Color GREEN = {0, 0xff, 0, 0xff}; -static const IComposerClient::Color BLUE = {0, 0, 0xff, 0xff}; +using vts::Gralloc; // Test environment for graphics.composer class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { @@ -65,93 +65,8 @@ class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEn GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment); }; -class TestLayer { - public: - TestLayer(const std::shared_ptr<ComposerClient>& client, Display display) - : mLayer(client->createLayer(display, kBufferSlotCount)), mComposerClient(client) {} - - // ComposerClient will take care of destroying layers, no need to explicitly - // call destroyLayers here - virtual ~TestLayer(){}; - - virtual void write(const std::shared_ptr<CommandWriterBase>& writer) { - writer->selectLayer(mLayer); - writer->setLayerDisplayFrame(mDisplayFrame); - writer->setLayerSourceCrop(mSourceCrop); - writer->setLayerZOrder(mZOrder); - writer->setLayerSurfaceDamage(mSurfaceDamage); - writer->setLayerTransform(mTransform); - writer->setLayerPlaneAlpha(mAlpha); - writer->setLayerBlendMode(mBlendMode); - } - - void setDisplayFrame(IComposerClient::Rect frame) { mDisplayFrame = frame; } - void setSourceCrop(IComposerClient::FRect crop) { mSourceCrop = crop; } - void setZOrder(uint32_t z) { mZOrder = z; } - - void setSurfaceDamage(std::vector<IComposerClient::Rect> surfaceDamage) { - mSurfaceDamage = surfaceDamage; - } - - void setTransform(Transform transform) { mTransform = transform; } - void setAlpha(float alpha) { mAlpha = alpha; } - void setBlendMode(IComposerClient::BlendMode blendMode) { mBlendMode = blendMode; } - - static constexpr uint32_t kBufferSlotCount = 64; - - IComposerClient::Rect mDisplayFrame = {0, 0, 0, 0}; - uint32_t mZOrder = 0; - std::vector<IComposerClient::Rect> mSurfaceDamage; - Transform mTransform = static_cast<Transform>(0); - IComposerClient::FRect mSourceCrop = {0, 0, 0, 0}; - float mAlpha = 1.0; - IComposerClient::BlendMode mBlendMode = IComposerClient::BlendMode::NONE; - - protected: - Layer mLayer; - - private: - std::shared_ptr<ComposerClient> const mComposerClient; -}; - -class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase { - public: - static int32_t GetBytesPerPixel(PixelFormat pixelFormat) { - switch (pixelFormat) { - case PixelFormat::RGBA_8888: - return 4; - case PixelFormat::RGB_888: - return 3; - default: - return -1; - } - } - - static void fillBuffer(int32_t width, int32_t height, uint32_t stride, void* bufferData, - PixelFormat pixelFormat, - std::vector<IComposerClient::Color> desiredPixelColors) { - ASSERT_TRUE(pixelFormat == PixelFormat::RGB_888 || pixelFormat == PixelFormat::RGBA_8888); - int32_t bytesPerPixel = GetBytesPerPixel(pixelFormat); - ASSERT_NE(-1, bytesPerPixel); - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - int pixel = row * width + col; - IComposerClient::Color srcColor = desiredPixelColors[pixel]; - - int offset = (row * stride + col) * bytesPerPixel; - uint8_t* pixelColor = (uint8_t*)bufferData + offset; - pixelColor[0] = srcColor.r; - pixelColor[1] = srcColor.g; - pixelColor[2] = srcColor.b; - - if (bytesPerPixel == 4) { - pixelColor[3] = srcColor.a; - } - } - } - } - - protected: +class GraphicsCompositionTest : public ::testing::VtsHalHidlTargetTestBase { + protected: using PowerMode = V2_1::IComposerClient::PowerMode; void SetUp() override { VtsHalHidlTargetTestBase::SetUp(); @@ -173,6 +88,8 @@ class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase mDisplayHeight = mComposerClient->getDisplayAttribute( mPrimaryDisplay, activeConfig, IComposerClient::Attribute::HEIGHT)); + setTestColorModes(); + // explicitly disable vsync ASSERT_NO_FATAL_FAILURE(mComposerClient->setVsyncEnabled(mPrimaryDisplay, false)); mComposerCallback->setVsyncAllowed(false); @@ -182,22 +99,28 @@ class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase mReader = std::make_unique<TestCommandReader>(); mGralloc = std::make_shared<Gralloc>(); - std::vector<ColorMode> colorModes = mComposerClient->getColorModes(mPrimaryDisplay); - if (std::find(colorModes.begin(), colorModes.end(), ColorMode::SRGB) == colorModes.end()) { - mHasReadbackBuffer = false; - return; - } - mWriter->selectDisplay(mPrimaryDisplay); - ASSERT_NO_FATAL_FAILURE(mComposerClient->setColorMode(mPrimaryDisplay, ColorMode::SRGB, - RenderIntent::COLORIMETRIC)); - mComposerClient->getRaw()->getReadbackBufferAttributes( - mPrimaryDisplay, - [&](const auto& tmpError, const auto& tmpPixelFormat, const auto& tmpDataspace) { - mHasReadbackBuffer = readbackSupported(tmpPixelFormat, tmpDataspace, tmpError); - mPixelFormat = tmpPixelFormat; - mDataspace = tmpDataspace; - }); ASSERT_NO_FATAL_FAILURE(mComposerClient->setPowerMode(mPrimaryDisplay, PowerMode::ON)); + + ASSERT_NO_FATAL_FAILURE( + mTestRenderEngine = std::unique_ptr<TestRenderEngine>(new TestRenderEngine( + renderengine::RenderEngineCreationArgs::Builder() + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(TestRenderEngine::sMaxFrameBufferAcquireBuffers) + .setUseColorManagerment(true) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setContextPriority(renderengine::RenderEngine::ContextPriority::HIGH) + .build()))); + + 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 { @@ -217,10 +140,6 @@ class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase mReader->mErrors.clear(); } - void execute() { - ASSERT_NO_FATAL_FAILURE(mComposerClient->execute(mReader.get(), mWriter.get())); - } - void writeLayers(const std::vector<std::shared_ptr<TestLayer>>& layers) { for (auto layer : layers) { layer->write(mWriter); @@ -228,42 +147,10 @@ class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase execute(); } - void clearColors(std::vector<IComposerClient::Color>& expectedColors, int32_t width, - int32_t height) { - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - int pixel = row * mDisplayWidth + col; - expectedColors[pixel] = BLACK; - } - } - } - - void fillColorsArea(std::vector<IComposerClient::Color>& expectedColors, int32_t stride, - IComposerClient::Rect area, IComposerClient::Color color) { - for (int row = area.top; row < area.bottom; row++) { - for (int col = area.left; col < area.right; col++) { - int pixel = row * stride + col; - expectedColors[pixel] = color; - } - } - } - - bool readbackSupported(const PixelFormat& pixelFormat, const Dataspace& dataspace, - const Error error) { - if (error != Error::NONE) { - return false; - } - // TODO: add support for RGBA_1010102 - if (pixelFormat != PixelFormat::RGB_888 && pixelFormat != PixelFormat::RGBA_8888) { - return false; - } - if (dataspace != Dataspace::V0_SRGB) { - return false; - } - return true; + void execute() { + ASSERT_NO_FATAL_FAILURE(mComposerClient->execute(mReader.get(), mWriter.get())); } - std::unique_ptr<Composer> mComposer; std::shared_ptr<ComposerClient> mComposerClient; @@ -272,9 +159,11 @@ class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase 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; @@ -293,734 +182,776 @@ class GraphicsComposerReadbackTest : public ::testing::VtsHalHidlTargetTestBase return displays[0]; } } -}; -class ReadbackBuffer { - public: - ReadbackBuffer(Display display, const std::shared_ptr<ComposerClient>& client, - const std::shared_ptr<Gralloc>& gralloc, uint32_t width, uint32_t height, - PixelFormat pixelFormat, Dataspace dataspace) { - mDisplay = display; - - mComposerClient = client; - mGralloc = gralloc; - - mFormat = pixelFormat; - mDataspace = dataspace; - - mWidth = width; - mHeight = height; - mLayerCount = 1; - mUsage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::GPU_TEXTURE); - - mAccessRegion.top = 0; - mAccessRegion.left = 0; - mAccessRegion.width = width; - mAccessRegion.height = height; - }; - - ~ReadbackBuffer() { - if (mBufferHandle != nullptr) { - mGralloc->freeBuffer(mBufferHandle); - } - } - void setReadbackBuffer() { - if (mBufferHandle != nullptr) { - mGralloc->freeBuffer(mBufferHandle); - mBufferHandle = nullptr; - } - mBufferHandle = mGralloc->allocate(mWidth, mHeight, mLayerCount, mFormat, mUsage, - /*import*/ true, &mStride); - ASSERT_NE(false, mGralloc->validateBufferSize(mBufferHandle, mWidth, mHeight, mLayerCount, - mFormat, mUsage, mStride)); - ASSERT_NO_FATAL_FAILURE(mComposerClient->setReadbackBuffer(mDisplay, mBufferHandle, -1)); + 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); + } + } + }); } +}; - void checkReadbackBuffer(std::vector<IComposerClient::Color> expectedColors) { - // lock buffer for reading - int32_t fenceHandle; - ASSERT_NO_FATAL_FAILURE(mComposerClient->getReadbackBufferFence(mDisplay, &fenceHandle)); - - void* bufData = mGralloc->lock(mBufferHandle, mUsage, mAccessRegion, fenceHandle); - ASSERT_TRUE(mFormat == PixelFormat::RGB_888 || mFormat == PixelFormat::RGBA_8888); - int32_t bytesPerPixel = GraphicsComposerReadbackTest::GetBytesPerPixel(mFormat); - ASSERT_NE(-1, bytesPerPixel); - for (int row = 0; row < mHeight; row++) { - for (int col = 0; col < mWidth; col++) { - int pixel = row * mWidth + col; - int offset = (row * mStride + col) * bytesPerPixel; - uint8_t* pixelColor = (uint8_t*)bufData + offset; - - ASSERT_EQ(expectedColors[pixel].r, pixelColor[0]); - ASSERT_EQ(expectedColors[pixel].g, pixelColor[1]); - ASSERT_EQ(expectedColors[pixel].b, pixelColor[2]); - } +TEST_F(GraphicsCompositionTest, 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; } - int32_t unlockFence = mGralloc->unlock(mBufferHandle); - if (unlockFence != -1) { - sync_wait(unlockFence, -1); - close(unlockFence); + + 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)); } +} - uint32_t mWidth; - uint32_t mHeight; - uint32_t mLayerCount; - PixelFormat mFormat; - uint64_t mUsage; - AccessRegion mAccessRegion; +TEST_F(GraphicsCompositionTest, SetLayerBuffer) { + 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)); - protected: - uint32_t mStride; - const native_handle_t* mBufferHandle = nullptr; - Dataspace mDataspace; - Display mDisplay; - std::shared_ptr<Gralloc> mGralloc; - std::shared_ptr<ComposerClient> mComposerClient; -}; + 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; + } -class TestColorLayer : public TestLayer { - public: - TestColorLayer(const std::shared_ptr<ComposerClient>& client, Display display) - : TestLayer{client, display} {} + mWriter->selectDisplay(mPrimaryDisplay); - void write(const std::shared_ptr<CommandWriterBase>& writer) override { - TestLayer::write(writer); - writer->setLayerCompositionType(IComposerClient::Composition::SOLID_COLOR); - writer->setLayerColor(mColor); - } + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, 0, mDisplayWidth, mDisplayHeight / 4}, RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2}, + GREEN); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, + BLUE); - void setColor(IComposerClient::Color color) { mColor = color; } + auto layer = std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, + mDisplayWidth, mDisplayHeight, + PixelFormat::RGBA_8888); + layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); + layer->setZOrder(10); + layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); + ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors)); - private: - IComposerClient::Color mColor = {0xff, 0xff, 0xff, 0xff}; -}; + std::vector<std::shared_ptr<TestLayer>> layers = {layer}; -class TestBufferLayer : public TestLayer { - public: - TestBufferLayer(const std::shared_ptr<ComposerClient>& client, - const std::shared_ptr<Gralloc>& gralloc, Display display, int32_t width, - int32_t height, PixelFormat format, - IComposerClient::Composition composition = IComposerClient::Composition::DEVICE) - : TestLayer{client, display} { - mGralloc = gralloc; - mComposition = composition; - mWidth = width; - mHeight = height; - mLayerCount = 1; - mFormat = format; - mUsage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY); - - mAccessRegion.top = 0; - mAccessRegion.left = 0; - mAccessRegion.width = width; - mAccessRegion.height = height; - - setSourceCrop({0, 0, (float)width, (float)height}); - } + writeLayers(layers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); - ~TestBufferLayer() { - if (mBufferHandle != nullptr) { - mGralloc->freeBuffer(mBufferHandle); + if (mReader->mCompositionChanges.size() != 0) { + clearCommandReaderState(); + GTEST_SUCCEED(); + return; } - } + ASSERT_EQ(0, mReader->mErrors.size()); - void write(const std::shared_ptr<CommandWriterBase>& writer) override { - TestLayer::write(writer); - writer->setLayerCompositionType(mComposition); - writer->setLayerDataspace(Dataspace::UNKNOWN); - writer->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, mDisplayFrame)); - if (mBufferHandle != nullptr) writer->setLayerBuffer(0, mBufferHandle, mFillFence); - } + mWriter->presentDisplay(); + execute(); - void fillBuffer(std::vector<IComposerClient::Color> expectedColors) { - void* bufData = mGralloc->lock(mBufferHandle, mUsage, mAccessRegion, -1); - ASSERT_NO_FATAL_FAILURE(GraphicsComposerReadbackTest::fillBuffer( - mWidth, mHeight, mStride, bufData, mFormat, expectedColors)); - mFillFence = mGralloc->unlock(mBufferHandle); - if (mFillFence != -1) { - sync_wait(mFillFence, -1); - close(mFillFence); - } + 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)); } - void setBuffer(std::vector<IComposerClient::Color> colors) { - if (mBufferHandle != nullptr) { - mGralloc->freeBuffer(mBufferHandle); - mBufferHandle = nullptr; +} + +TEST_F(GraphicsCompositionTest, SetLayerBufferNoEffect) { + 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; } - mBufferHandle = mGralloc->allocate(mWidth, mHeight, mLayerCount, mFormat, mUsage, - /*import*/ true, &mStride); - ASSERT_NE(nullptr, mBufferHandle); - ASSERT_NO_FATAL_FAILURE(fillBuffer(colors)); - ASSERT_NE(false, mGralloc->validateBufferSize(mBufferHandle, mWidth, mHeight, mLayerCount, - mFormat, mUsage, mStride)); - } - void setToClientComposition(const std::shared_ptr<CommandWriterBase>& writer) { - writer->selectLayer(mLayer); - writer->setLayerCompositionType(IComposerClient::Composition::CLIENT); - } + auto layer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); + IComposerClient::Rect coloredSquare({0, 0, mDisplayWidth, mDisplayHeight}); + layer->setColor(BLUE); + layer->setDisplayFrame(coloredSquare); + layer->setZOrder(10); + layer->write(mWriter); - AccessRegion mAccessRegion; - uint32_t mStride; - uint32_t mWidth; - uint32_t mHeight; - uint32_t mLayerCount; - PixelFormat mFormat; + // This following buffer call should have no effect + uint64_t usage = + static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN); + const native_handle_t* bufferHandle = + mGralloc->allocate(mDisplayWidth, mDisplayHeight, 1, PixelFormat::RGBA_8888, usage); + mWriter->setLayerBuffer(0, bufferHandle, -1); - protected: - uint64_t mUsage; - IComposerClient::Composition mComposition; - std::shared_ptr<Gralloc> mGralloc; - int32_t mFillFence; - const native_handle_t* mBufferHandle = nullptr; -}; + // expected color for each pixel + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, coloredSquare, BLUE); -TEST_F(GraphicsComposerReadbackTest, SingleSolidColorLayer) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" - << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - 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); - 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()); + mWriter->validateDisplay(); + execute(); + + 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)); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); + } } -TEST_F(GraphicsComposerReadbackTest, SetLayerBuffer) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_F(GraphicsCompositionTest, ClientComposition) { + ASSERT_NO_FATAL_FAILURE( + mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kClientTargetSlotCount)); + + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + mWriter->selectDisplay(mPrimaryDisplay); + ASSERT_NO_FATAL_FAILURE( + mComposerClient->setColorMode(mPrimaryDisplay, mode, RenderIntent::COLORIMETRIC)); - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, {0, 0, mDisplayWidth, mDisplayHeight / 4}, RED); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2}, GREEN); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, BLUE); - - auto layer = - std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, mDisplayWidth, - mDisplayHeight, PixelFormat::RGBA_8888); - layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); - layer->setZOrder(10); - ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors)); - - std::vector<std::shared_ptr<TestLayer>> layers = {layer}; - - writeLayers(layers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; - } - ASSERT_EQ(0, mReader->mErrors.size()); + 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; + } + + mWriter->selectDisplay(mPrimaryDisplay); - mWriter->presentDisplay(); - execute(); + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, 0, mDisplayWidth, mDisplayHeight / 4}, RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2}, + GREEN); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, + BLUE); - ASSERT_EQ(0, mReader->mErrors.size()); + auto layer = std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, + mDisplayWidth, mDisplayHeight, + PixelFormat::RGBA_FP16); + layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); + layer->setZOrder(10); + layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); -} + std::vector<std::shared_ptr<TestLayer>> layers = {layer}; -TEST_F(GraphicsComposerReadbackTest, SetLayerBufferNoEffect) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" - << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + 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 (mReader->mCompositionChanges.size() != 0) { + ASSERT_EQ(1, mReader->mCompositionChanges.size()); + ASSERT_EQ(1, mReader->mCompositionChanges[0].second); + + PixelFormat clientFormat = PixelFormat::RGBA_8888; + uint64_t clientUsage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | + BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_CLIENT_TARGET); + Dataspace clientDataspace = ReadbackHelper::getDataspaceForColorMode(mode); + IComposerClient::Rect damage{0, 0, mDisplayWidth, mDisplayHeight}; + + bool clientTargetSupported = mComposerClient->getClientTargetSupport_2_2( + mPrimaryDisplay, layer->mWidth, layer->mHeight, clientFormat, clientDataspace); + // if the client target format is not supported, skip this + // configuration + if (!clientTargetSupported) { + std::cout << "Client target configuration width: " << layer->mWidth + << " height: " << layer->mHeight + << " pixel format: PixelFormat::RGBA_8888 dataspace: " + << ReadbackHelper::getDataspaceString(clientDataspace) + << " unsupported for display" << std::endl; + continue; + } + // create client target buffer + uint32_t clientStride; + const native_handle_t* clientBufferHandle = + mGralloc->allocate(layer->mWidth, layer->mHeight, layer->mLayerCount, + clientFormat, clientUsage, /*import*/ true, &clientStride); + ASSERT_NE(nullptr, clientBufferHandle); + + void* clientBufData = + mGralloc->lock(clientBufferHandle, clientUsage, layer->mAccessRegion, -1); + + ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(layer->mWidth, layer->mHeight, + clientStride, clientBufData, + clientFormat, expectedColors)); + int clientFence = mGralloc->unlock(clientBufferHandle); + if (clientFence != -1) { + sync_wait(clientFence, -1); + close(clientFence); + } - auto layer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); - IComposerClient::Rect coloredSquare({0, 0, mDisplayWidth, mDisplayHeight}); - layer->setColor(BLUE); - layer->setDisplayFrame(coloredSquare); - layer->setZOrder(10); - layer->write(mWriter); - - // This following buffer call should have no effect - PixelFormat format = PixelFormat::RGBA_8888; - uint64_t usage = - static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN); - const native_handle_t* bufferHandle = - mGralloc->allocate(mDisplayWidth, mDisplayHeight, 1, format, usage); - mWriter->setLayerBuffer(0, bufferHandle, -1); - - // expected color for each pixel - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, coloredSquare, BLUE); - - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - - mWriter->validateDisplay(); - execute(); - - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; - } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->setClientTarget(0, clientBufferHandle, clientFence, clientDataspace, + std::vector<IComposerClient::Rect>(1, damage)); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); -} + layer->setToClientComposition(mWriter); + mWriter->validateDisplay(); + execute(); + ASSERT_EQ(0, mReader->mCompositionChanges.size()); + } + ASSERT_EQ(0, mReader->mErrors.size()); -TEST_F(GraphicsComposerReadbackTest, ClientComposition) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" - << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; + mWriter->presentDisplay(); + execute(); + + ASSERT_EQ(0, mReader->mErrors.size()); + + ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } +} - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, {0, 0, mDisplayWidth, mDisplayHeight / 4}, RED); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2}, GREEN); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, BLUE); - - auto layer = - std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, mDisplayWidth, - mDisplayHeight, PixelFormat::RGBA_FP16); - layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); - layer->setZOrder(10); - - std::vector<std::shared_ptr<TestLayer>> layers = {layer}; - - 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 (mReader->mCompositionChanges.size() != 0) { - ASSERT_EQ(1, mReader->mCompositionChanges.size()); - ASSERT_EQ(1, mReader->mCompositionChanges[0].second); +TEST_F(GraphicsCompositionTest, DeviceAndClientComposition) { + ASSERT_NO_FATAL_FAILURE( + mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kClientTargetSlotCount)); + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" + << std::endl; + mWriter->selectDisplay(mPrimaryDisplay); ASSERT_NO_FATAL_FAILURE( - mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kClientTargetSlotCount)); + 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; + } + + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, 0, mDisplayWidth, mDisplayHeight / 2}, GREEN); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, RED); + + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + + auto deviceLayer = std::make_shared<TestBufferLayer>( + mComposerClient, mGralloc, mPrimaryDisplay, mDisplayWidth, mDisplayHeight / 2, + PixelFormat::RGBA_8888); + std::vector<IComposerClient::Color> deviceColors(deviceLayer->mWidth * + deviceLayer->mHeight); + ReadbackHelper::fillColorsArea(deviceColors, deviceLayer->mWidth, + {0, 0, static_cast<int32_t>(deviceLayer->mWidth), + static_cast<int32_t>(deviceLayer->mHeight)}, + GREEN); + deviceLayer->setDisplayFrame({0, 0, static_cast<int32_t>(deviceLayer->mWidth), + static_cast<int32_t>(deviceLayer->mHeight)}); + deviceLayer->setZOrder(10); + deviceLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); + ASSERT_NO_FATAL_FAILURE(deviceLayer->setBuffer(deviceColors)); + deviceLayer->write(mWriter); - // create client target buffer - uint32_t clientStride; PixelFormat clientFormat = PixelFormat::RGBA_8888; uint64_t clientUsage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_CLIENT_TARGET); + Dataspace clientDataspace = ReadbackHelper::getDataspaceForColorMode(mode); + int32_t clientWidth = mDisplayWidth; + int32_t clientHeight = mDisplayHeight / 2; + + bool clientTargetSupported = mComposerClient->getClientTargetSupport_2_2( + mPrimaryDisplay, clientWidth, clientHeight, clientFormat, clientDataspace); + // if the client target format is not supported, skip this + // configuration + if (!clientTargetSupported) { + std::cout << "Client target configuration width: " << clientWidth + << " height: " << clientHeight + << " pixel format: PixelFormat::RGBA_8888 dataspace: " + << ReadbackHelper::getDataspaceString(clientDataspace) + << " unsupported for display" << std::endl; + continue; + } + + auto clientLayer = std::make_shared<TestBufferLayer>( + mComposerClient, mGralloc, mPrimaryDisplay, clientWidth, clientHeight, + PixelFormat::RGBA_FP16, IComposerClient::Composition::DEVICE); + IComposerClient::Rect clientFrame = {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}; + clientLayer->setDisplayFrame(clientFrame); + clientLayer->setZOrder(0); + clientLayer->write(mWriter); + mWriter->validateDisplay(); + execute(); + + if (mReader->mCompositionChanges.size() != 1) { + std::cout << "HWC asked for none or more than 1 composition change, skipping" + << std::endl; + mReader->mCompositionChanges.clear(); + continue; + } + // create client target buffer + ASSERT_EQ(1, mReader->mCompositionChanges[0].second); + uint32_t clientStride; const native_handle_t* clientBufferHandle = - mGralloc->allocate(layer->mWidth, layer->mHeight, layer->mLayerCount, clientFormat, - clientUsage, /*import*/ true, &clientStride); + mGralloc->allocate(mDisplayWidth, mDisplayHeight, clientLayer->mLayerCount, + clientFormat, clientUsage, /*import*/ true, &clientStride); ASSERT_NE(nullptr, clientBufferHandle); - void* clientBufData = - mGralloc->lock(clientBufferHandle, clientUsage, layer->mAccessRegion, -1); + void* clientBufData = mGralloc->lock(clientBufferHandle, clientUsage, + {0, 0, mDisplayWidth, mDisplayHeight}, -1); - ASSERT_NO_FATAL_FAILURE(fillBuffer(layer->mWidth, layer->mHeight, clientStride, - clientBufData, clientFormat, expectedColors)); + std::vector<IComposerClient::Color> clientColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(clientColors, mDisplayWidth, clientFrame, RED); + ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(mDisplayWidth, mDisplayHeight, + clientStride, clientBufData, + clientFormat, clientColors)); int clientFence = mGralloc->unlock(clientBufferHandle); if (clientFence != -1) { sync_wait(clientFence, -1); close(clientFence); } - IComposerClient::Rect damage{0, 0, mDisplayWidth, mDisplayHeight}; - mWriter->setClientTarget(0, clientBufferHandle, clientFence, Dataspace::UNKNOWN, - std::vector<IComposerClient::Rect>(1, damage)); - - layer->setToClientComposition(mWriter); + mWriter->setClientTarget(0, clientBufferHandle, clientFence, clientDataspace, + std::vector<IComposerClient::Rect>(1, clientFrame)); + clientLayer->setToClientComposition(mWriter); mWriter->validateDisplay(); execute(); ASSERT_EQ(0, mReader->mCompositionChanges.size()); + ASSERT_EQ(0, mReader->mErrors.size()); + + mWriter->presentDisplay(); + execute(); + ASSERT_EQ(0, mReader->mErrors.size()); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } - ASSERT_EQ(0, mReader->mErrors.size()); +} - mWriter->presentDisplay(); - execute(); +TEST_F(GraphicsCompositionTest, SetLayerDamage) { + 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)); - ASSERT_EQ(0, mReader->mErrors.size()); + 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; + } - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); -} + mWriter->selectDisplay(mPrimaryDisplay); -TEST_F(GraphicsComposerReadbackTest, DeviceAndClientComposition) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" - << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + IComposerClient::Rect redRect = {0, 0, mDisplayWidth / 4, mDisplayHeight / 4}; - ASSERT_NO_FATAL_FAILURE( - mComposerClient->setClientTargetSlotCount(mPrimaryDisplay, kClientTargetSlotCount)); - - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, {0, 0, mDisplayWidth, mDisplayHeight / 2}, GREEN); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, RED); - - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - - auto deviceLayer = - std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, mDisplayWidth, - mDisplayHeight / 2, PixelFormat::RGBA_8888); - std::vector<IComposerClient::Color> deviceColors(deviceLayer->mWidth * deviceLayer->mHeight); - fillColorsArea(deviceColors, deviceLayer->mWidth, - {0, 0, static_cast<int32_t>(deviceLayer->mWidth), - static_cast<int32_t>(deviceLayer->mHeight)}, - GREEN); - deviceLayer->setDisplayFrame({0, 0, static_cast<int32_t>(deviceLayer->mWidth), - static_cast<int32_t>(deviceLayer->mHeight)}); - deviceLayer->setZOrder(10); - ASSERT_NO_FATAL_FAILURE(deviceLayer->setBuffer(deviceColors)); - deviceLayer->write(mWriter); - - auto clientLayer = std::make_shared<TestBufferLayer>( - mComposerClient, mGralloc, mPrimaryDisplay, mDisplayWidth, mDisplayHeight / 2, - PixelFormat::RGBA_8888, IComposerClient::Composition::CLIENT); - IComposerClient::Rect clientFrame = {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}; - clientLayer->setDisplayFrame(clientFrame); - clientLayer->setZOrder(0); - clientLayer->write(mWriter); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - - uint64_t clientUsage = - static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_CLIENT_TARGET); - uint32_t clientStride; - const native_handle_t* clientBufferHandle = - mGralloc->allocate(mDisplayWidth, mDisplayHeight, 1, PixelFormat::RGBA_8888, - clientUsage, /*import*/ true, &clientStride); - ASSERT_NE(nullptr, clientBufferHandle); - - AccessRegion clientAccessRegion; - clientAccessRegion.left = 0; - clientAccessRegion.top = 0; - clientAccessRegion.width = mDisplayWidth; - clientAccessRegion.height = mDisplayHeight; - void* clientData = mGralloc->lock(clientBufferHandle, clientUsage, clientAccessRegion, -1); - std::vector<IComposerClient::Color> clientColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(clientColors, mDisplayWidth, clientFrame, RED); - ASSERT_NO_FATAL_FAILURE(fillBuffer(mDisplayWidth, mDisplayHeight, clientStride, clientData, - PixelFormat::RGBA_8888, clientColors)); - int clientFence = mGralloc->unlock(clientBufferHandle); - if (clientFence != -1) { - sync_wait(clientFence, -1); - close(clientFence); - } + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); - mWriter->setClientTarget(0, clientBufferHandle, clientFence, Dataspace::UNKNOWN, - std::vector<IComposerClient::Rect>(1, clientFrame)); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - 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)); -} + auto layer = std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, + mDisplayWidth, mDisplayHeight, + PixelFormat::RGBA_8888); + layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); + layer->setZOrder(10); + layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); + ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors)); -TEST_F(GraphicsComposerReadbackTest, SetLayerDamage) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" - << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelformat/dataspace"; - return; - } + std::vector<std::shared_ptr<TestLayer>> layers = {layer}; + + 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 (mReader->mCompositionChanges.size() != 0) { + clearCommandReaderState(); + GTEST_SUCCEED(); + return; + } + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->presentDisplay(); + execute(); + ASSERT_EQ(0, mReader->mErrors.size()); - IComposerClient::Rect redRect = {0, 0, mDisplayWidth / 4, mDisplayHeight / 4}; + ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); + // update surface damage and recheck + redRect = {mDisplayWidth / 4, mDisplayHeight / 4, mDisplayWidth / 2, mDisplayHeight / 2}; + ReadbackHelper::clearColors(expectedColors, mDisplayWidth, mDisplayHeight, mDisplayWidth); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); - auto layer = - std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, mDisplayWidth, - mDisplayHeight, PixelFormat::RGBA_8888); - layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); - layer->setZOrder(10); - ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors)); + ASSERT_NO_FATAL_FAILURE(layer->fillBuffer(expectedColors)); + layer->setSurfaceDamage(std::vector<IComposerClient::Rect>( + 1, {0, 0, mDisplayWidth / 2, mDisplayWidth / 2})); - std::vector<std::shared_ptr<TestLayer>> layers = {layer}; + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - 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(); + ASSERT_EQ(0, mReader->mErrors.size()); + ASSERT_EQ(0, mReader->mCompositionChanges.size()); + mWriter->presentDisplay(); + execute(); + ASSERT_EQ(0, mReader->mErrors.size()); - writeLayers(layers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; + ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); - - // update surface damage and recheck - redRect = {mDisplayWidth / 4, mDisplayHeight / 4, mDisplayWidth / 2, mDisplayHeight / 2}; - clearColors(expectedColors, mDisplayWidth, mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); - - ASSERT_NO_FATAL_FAILURE(layer->fillBuffer(expectedColors)); - layer->setSurfaceDamage( - std::vector<IComposerClient::Rect>(1, {0, 0, mDisplayWidth / 2, mDisplayWidth / 2})); - - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - - writeLayers(layers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - ASSERT_EQ(0, mReader->mCompositionChanges.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } -TEST_F(GraphicsComposerReadbackTest, SetLayerPlaneAlpha) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_F(GraphicsCompositionTest, SetLayerPlaneAlpha) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + 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); - layer->setColor(RED); - layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); - layer->setZOrder(10); - layer->setAlpha(0); - layer->setBlendMode(IComposerClient::BlendMode::PREMULTIPLIED); + auto layer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); + layer->setColor(RED); + layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); + layer->setZOrder(10); + layer->setAlpha(0); + layer->setBlendMode(IComposerClient::BlendMode::PREMULTIPLIED); - std::vector<std::shared_ptr<TestLayer>> layers = {layer}; + std::vector<std::shared_ptr<TestLayer>> layers = {layer}; - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - writeLayers(layers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; - } - ASSERT_EQ(0, mReader->mErrors.size()); + writeLayers(layers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + if (mReader->mCompositionChanges.size() != 0) { + clearCommandReaderState(); + GTEST_SUCCEED(); + return; + } + ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->presentDisplay(); + execute(); + ASSERT_EQ(0, mReader->mErrors.size()); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); + 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) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_F(GraphicsCompositionTest, SetLayerSourceCrop) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + 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; + } + + mWriter->selectDisplay(mPrimaryDisplay); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, {0, 0, mDisplayWidth, mDisplayHeight / 4}, RED); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, BLUE); - - auto layer = - std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, mDisplayWidth, - mDisplayHeight, PixelFormat::RGBA_8888); - layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); - layer->setZOrder(10); - layer->setSourceCrop({0, static_cast<float>(mDisplayHeight / 2), - static_cast<float>(mDisplayWidth), static_cast<float>(mDisplayHeight)}); - ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors)); - - std::vector<std::shared_ptr<TestLayer>> layers = {layer}; - - // update expected colors to match crop - fillColorsArea(expectedColors, mDisplayWidth, {0, 0, mDisplayWidth, mDisplayHeight}, 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 (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, 0, mDisplayWidth, mDisplayHeight / 4}, RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mDisplayHeight / 2, mDisplayWidth, mDisplayHeight}, + BLUE); + + auto layer = std::make_shared<TestBufferLayer>(mComposerClient, mGralloc, mPrimaryDisplay, + mDisplayWidth, mDisplayHeight, + PixelFormat::RGBA_8888); + layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); + layer->setZOrder(10); + layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); + layer->setSourceCrop({0, static_cast<float>(mDisplayHeight / 2), + static_cast<float>(mDisplayWidth), + static_cast<float>(mDisplayHeight)}); + ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors)); + + std::vector<std::shared_ptr<TestLayer>> layers = {layer}; + + // update expected colors to match crop + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, 0, mDisplayWidth, mDisplayHeight}, 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 (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)); } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } -TEST_F(GraphicsComposerReadbackTest, SetLayerZOrder) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_F(GraphicsCompositionTest, SetLayerZOrder) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + 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; + } + + IComposerClient::Rect redRect = {0, 0, mDisplayWidth, mDisplayHeight / 2}; + IComposerClient::Rect blueRect = {0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight}; + auto redLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); + redLayer->setColor(RED); + redLayer->setDisplayFrame(redRect); + + auto blueLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); + blueLayer->setColor(BLUE); + blueLayer->setDisplayFrame(blueRect); + blueLayer->setZOrder(5); + + std::vector<std::shared_ptr<TestLayer>> layers = {redLayer, blueLayer}; + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + + // red in front of blue + redLayer->setZOrder(10); + + // fill blue first so that red will overwrite on overlap + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, blueRect, BLUE); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); + + 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 (mReader->mCompositionChanges.size() != 0) { + clearCommandReaderState(); + GTEST_SUCCEED(); + return; + } + mWriter->presentDisplay(); + execute(); + ASSERT_EQ(0, mReader->mErrors.size()); + + ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); + + redLayer->setZOrder(1); + ReadbackHelper::clearColors(expectedColors, mDisplayWidth, mDisplayHeight, mDisplayWidth); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, blueRect, BLUE); + + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - IComposerClient::Rect redRect = {0, 0, mDisplayWidth, mDisplayHeight / 2}; - IComposerClient::Rect blueRect = {0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight}; - auto redLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); - redLayer->setColor(RED); - redLayer->setDisplayFrame(redRect); - - auto blueLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); - blueLayer->setColor(BLUE); - blueLayer->setDisplayFrame(blueRect); - blueLayer->setZOrder(5); - - std::vector<std::shared_ptr<TestLayer>> layers = {redLayer, blueLayer}; - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - - // red in front of blue - redLayer->setZOrder(10); - - // fill blue first so that red will overwrite on overlap - fillColorsArea(expectedColors, mDisplayWidth, blueRect, BLUE); - fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); - - 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 (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; + writeLayers(layers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + ASSERT_EQ(0, mReader->mCompositionChanges.size()); + 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)); } - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); - - redLayer->setZOrder(1); - clearColors(expectedColors, mDisplayWidth, mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, redRect, RED); - fillColorsArea(expectedColors, mDisplayWidth, blueRect, BLUE); - - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - - writeLayers(layers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - ASSERT_EQ(0, mReader->mCompositionChanges.size()); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(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; } @@ -1029,8 +960,8 @@ class GraphicsComposerBlendModeReadbackTest : public GraphicsComposerReadbackTes void setUpLayers(IComposerClient::BlendMode blendMode) { mLayers.clear(); std::vector<IComposerClient::Color> topLayerPixelColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(topLayerPixelColors, mDisplayWidth, {0, 0, mDisplayWidth, mDisplayHeight}, - mTopLayerColor); + ReadbackHelper::fillColorsArea(topLayerPixelColors, mDisplayWidth, + {0, 0, mDisplayWidth, mDisplayHeight}, mTopLayerColor); auto backgroundLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); backgroundLayer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); @@ -1042,6 +973,7 @@ class GraphicsComposerBlendModeReadbackTest : public GraphicsComposerReadbackTes PixelFormat::RGBA_8888); layer->setDisplayFrame({0, 0, mDisplayWidth, mDisplayHeight}); layer->setZOrder(10); + layer->setDataspace(Dataspace::UNKNOWN, mWriter); ASSERT_NO_FATAL_FAILURE(layer->setBuffer(topLayerPixelColors)); layer->setBlendMode(blendMode); @@ -1053,7 +985,7 @@ class GraphicsComposerBlendModeReadbackTest : public GraphicsComposerReadbackTes void setExpectedColors(std::vector<IComposerClient::Color>& expectedColors) { ASSERT_EQ(2, mLayers.size()); - clearColors(expectedColors, mDisplayWidth, mDisplayHeight); + ReadbackHelper::clearColors(expectedColors, mDisplayWidth, mDisplayHeight, mDisplayWidth); auto layer = mLayers[1]; IComposerClient::BlendMode blendMode = layer->mBlendMode; @@ -1091,122 +1023,180 @@ class GraphicsComposerBlendModeReadbackTest : public GraphicsComposerReadbackTes IComposerClient::Color mTopLayerColor; }; -TEST_P(GraphicsComposerBlendModeReadbackTest, None) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_P(GraphicsBlendModeCompositionTest, None) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + mWriter->selectDisplay(mPrimaryDisplay); + ASSERT_NO_FATAL_FAILURE( + mComposerClient->setColorMode(mPrimaryDisplay, mode, RenderIntent::COLORIMETRIC)); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - - setBackgroundColor(BLACK); - setTopLayerColor(TRANSLUCENT_RED); - setUpLayers(IComposerClient::BlendMode::NONE); - setExpectedColors(expectedColors); - - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - writeLayers(mLayers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; - } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); + 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; + } + + mWriter->selectDisplay(mPrimaryDisplay); + + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + + setBackgroundColor(BLACK); + setTopLayerColor(TRANSLUCENT_RED); + setUpLayers(IComposerClient::BlendMode::NONE); + setExpectedColors(expectedColors); + + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + writeLayers(mLayers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + 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)); + 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) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_P(GraphicsBlendModeCompositionTest, DISABLED_Coverage) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + 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; + } + + mWriter->selectDisplay(mPrimaryDisplay); + + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + + setBackgroundColor(BLACK); + setTopLayerColor(TRANSLUCENT_RED); + + setUpLayers(IComposerClient::BlendMode::COVERAGE); + setExpectedColors(expectedColors); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - - setBackgroundColor(BLACK); - setTopLayerColor(TRANSLUCENT_RED); - - setUpLayers(IComposerClient::BlendMode::COVERAGE); - setExpectedColors(expectedColors); - - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - writeLayers(mLayers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + writeLayers(mLayers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + 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)); } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } -TEST_P(GraphicsComposerBlendModeReadbackTest, Premultiplied) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_P(GraphicsBlendModeCompositionTest, Premultiplied) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } + 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; + } + mWriter->selectDisplay(mPrimaryDisplay); + + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + + setBackgroundColor(BLACK); + setTopLayerColor(TRANSLUCENT_RED); + setUpLayers(IComposerClient::BlendMode::PREMULTIPLIED); + setExpectedColors(expectedColors); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - - setBackgroundColor(BLACK); - setTopLayerColor(TRANSLUCENT_RED); - setUpLayers(IComposerClient::BlendMode::PREMULTIPLIED); - setExpectedColors(expectedColors); - - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - writeLayers(mLayers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + writeLayers(mLayers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + 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(mLayers); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers()); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors)); } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(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); auto backgroundLayer = std::make_shared<TestColorLayer>(mComposerClient, mPrimaryDisplay); backgroundLayer->setColor({0, 0, 0, 0}); @@ -1225,8 +1215,8 @@ class GraphicsComposerTransformReadbackTest : public GraphicsComposerReadbackTes mLayer->setZOrder(10); std::vector<IComposerClient::Color> baseColors(mSideLength * mSideLength); - fillColorsArea(baseColors, mSideLength, redRect, RED); - fillColorsArea(baseColors, mSideLength, blueRect, BLUE); + ReadbackHelper::fillColorsArea(baseColors, mSideLength, redRect, RED); + ReadbackHelper::fillColorsArea(baseColors, mSideLength, blueRect, BLUE); ASSERT_NO_FATAL_FAILURE(mLayer->setBuffer(baseColors)); mLayers = {backgroundLayer, mLayer}; @@ -1239,110 +1229,170 @@ class GraphicsComposerTransformReadbackTest : public GraphicsComposerReadbackTes int mSideLength; }; -TEST_F(GraphicsComposerTransformReadbackTest, FLIP_H) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_F(GraphicsTransformCompositionTest, FLIP_H) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - mLayer->setTransform(Transform::FLIP_H); - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, - {mSideLength / 2, 0, mSideLength, mSideLength / 2}, RED); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mSideLength / 2, mSideLength / 2, mSideLength}, BLUE); - - writeLayers(mLayers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; - } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); + 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; + } + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + mLayer->setTransform(Transform::FLIP_H); + mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); + + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {mSideLength / 2, 0, mSideLength, mSideLength / 2}, RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mSideLength / 2, mSideLength / 2, mSideLength}, BLUE); + + writeLayers(mLayers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + 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)); + 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) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_F(GraphicsTransformCompositionTest, FLIP_V) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - - mLayer->setTransform(Transform::FLIP_V); - - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, - {0, mSideLength / 2, mSideLength / 2, mSideLength}, RED); - fillColorsArea(expectedColors, mDisplayWidth, - {mSideLength / 2, 0, mSideLength, mSideLength / 2}, BLUE); - - writeLayers(mLayers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; + 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; + } + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + + mLayer->setTransform(Transform::FLIP_V); + mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); + + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, mSideLength / 2, mSideLength / 2, mSideLength}, RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {mSideLength / 2, 0, mSideLength, mSideLength / 2}, BLUE); + + writeLayers(mLayers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + 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(mLayers); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers()); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors)); } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } -TEST_F(GraphicsComposerTransformReadbackTest, ROT_180) { - if (!mHasReadbackBuffer) { - std::cout << "Readback not supported or unsuppported pixelFormat/dataspace or SRGB not a " - "valid color mode" +TEST_F(GraphicsTransformCompositionTest, ROT_180) { + for (ColorMode mode : mTestColorModes) { + std::cout << "---Testing Color Mode " << ReadbackHelper::getColorModeString(mode) << "---" << std::endl; - GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace"; - return; - } - ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, - mDisplayHeight, mPixelFormat, mDataspace); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); - - mLayer->setTransform(Transform::ROT_180); - - std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); - fillColorsArea(expectedColors, mDisplayWidth, - {mSideLength / 2, mSideLength / 2, mSideLength, mSideLength}, RED); - fillColorsArea(expectedColors, mDisplayWidth, {0, 0, mSideLength / 2, mSideLength / 2}, BLUE); - - writeLayers(mLayers); - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->validateDisplay(); - execute(); - if (mReader->mCompositionChanges.size() != 0) { - clearCommandReaderState(); - GTEST_SUCCEED(); - return; + 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; + } + ReadbackBuffer readbackBuffer(mPrimaryDisplay, mComposerClient, mGralloc, mDisplayWidth, + mDisplayHeight, mPixelFormat, mDataspace); + ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer()); + + mLayer->setTransform(Transform::ROT_180); + mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter); + + std::vector<IComposerClient::Color> expectedColors(mDisplayWidth * mDisplayHeight); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {mSideLength / 2, mSideLength / 2, mSideLength, mSideLength}, + RED); + ReadbackHelper::fillColorsArea(expectedColors, mDisplayWidth, + {0, 0, mSideLength / 2, mSideLength / 2}, BLUE); + + writeLayers(mLayers); + ASSERT_EQ(0, mReader->mErrors.size()); + mWriter->validateDisplay(); + execute(); + 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(mLayers); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers()); + ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->checkColorBuffer(expectedColors)); } - ASSERT_EQ(0, mReader->mErrors.size()); - mWriter->presentDisplay(); - execute(); - ASSERT_EQ(0, mReader->mErrors.size()); - ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors)); } } // anonymous namespace diff --git a/graphics/composer/2.3/default/Android.bp b/graphics/composer/2.3/default/Android.bp index 59b9436a89..a5696ddb95 100644 --- a/graphics/composer/2.3/default/Android.bp +++ b/graphics/composer/2.3/default/Android.bp @@ -28,8 +28,8 @@ cc_binary { "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/OWNERS b/graphics/composer/2.3/utils/OWNERS index b3ea6bef7b..cc6d937cad 100644 --- a/graphics/composer/2.3/utils/OWNERS +++ b/graphics/composer/2.3/utils/OWNERS @@ -2,7 +2,3 @@ lpy@google.com stoza@google.com vhau@google.com - -# VTS team -yim@google.com -zhuoyao@google.com diff --git a/graphics/composer/2.3/utils/command-buffer/Android.bp b/graphics/composer/2.3/utils/command-buffer/Android.bp index c48fe7a53c..36ac297588 100644 --- a/graphics/composer/2.3/utils/command-buffer/Android.bp +++ b/graphics/composer/2.3/utils/command-buffer/Android.bp @@ -3,12 +3,15 @@ cc_library_headers { defaults: ["hidl_defaults"], vendor_available: true, shared_libs: [ - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", + ], + export_shared_lib_headers: [ "android.hardware.graphics.composer@2.3", ], header_libs: [ - "android.hardware.graphics.composer@2.1-command-buffer", + "android.hardware.graphics.composer@2.2-command-buffer", + ], + export_header_lib_headers: [ "android.hardware.graphics.composer@2.2-command-buffer", ], export_include_dirs: ["include"], 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 b289b6a0f9..041fbc80cc 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 { @@ -184,18 +184,18 @@ class ComposerClientImpl : public V2_2::hal::detail::ComposerClientImpl<Interfac } protected: + using BaseType2_1 = V2_1::hal::detail::ComposerClientImpl<Interface, Hal>; + using BaseType2_1::mHal; + using BaseType2_1::mResources; std::unique_ptr<V2_1::hal::ComposerCommandEngine> createCommandEngine() override { return std::make_unique<ComposerCommandEngine>( mHal, static_cast<V2_2::hal::ComposerResources*>(mResources.get())); } - private: + private: using BaseType2_2 = V2_2::hal::detail::ComposerClientImpl<Interface, Hal>; - using BaseType2_1 = V2_1::hal::detail::ComposerClientImpl<Interface, Hal>; using BaseType2_1::mCommandEngine; using BaseType2_1::mCommandEngineMutex; - using BaseType2_1::mHal; - using BaseType2_1::mResources; }; } // namespace detail 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 1a40d962b8..329dbed51f 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/utils/vts/Android.bp b/graphics/composer/2.3/utils/vts/Android.bp index 2fe6cd6a40..f65a9c41ae 100644 --- a/graphics/composer/2.3/utils/vts/Android.bp +++ b/graphics/composer/2.3/utils/vts/Android.bp @@ -21,22 +21,17 @@ cc_library_static { "ComposerVts.cpp", ], static_libs: [ - "VtsHalHidlTargetTestBase", - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.composer@2.1-vts", - "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.2-vts", "android.hardware.graphics.composer@2.3", - "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", + ], + export_static_lib_headers: [ + "android.hardware.graphics.composer@2.2-vts", + "android.hardware.graphics.composer@2.3", ], header_libs: [ - "android.hardware.graphics.composer@2.1-command-buffer", - "android.hardware.graphics.composer@2.2-command-buffer", + "android.hardware.graphics.composer@2.3-command-buffer", + ], + export_header_lib_headers: [ "android.hardware.graphics.composer@2.3-command-buffer", ], cflags: [ diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp index e4218899c8..b729062637 100644 --- a/graphics/composer/2.3/vts/functional/Android.bp +++ b/graphics/composer/2.3/vts/functional/Android.bp @@ -28,6 +28,7 @@ cc_test { 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", @@ -40,6 +41,8 @@ cc_test { "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/Android.bp b/graphics/composer/2.4/Android.bp new file mode 100644 index 0000000000..5f700bed9b --- /dev/null +++ b/graphics/composer/2.4/Android.bp @@ -0,0 +1,25 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.graphics.composer@2.4", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IComposer.hal", + "IComposerCallback.hal", + "IComposerClient.hal", + ], + interfaces: [ + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", + "android.hidl.base@1.0", + ], + gen_java: false, +} diff --git a/graphics/composer/2.4/IComposer.hal b/graphics/composer/2.4/IComposer.hal new file mode 100644 index 0000000000..d3b3cb60a7 --- /dev/null +++ b/graphics/composer/2.4/IComposer.hal @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package android.hardware.graphics.composer@2.4; + +import IComposerClient; +import @2.1::Error; +import @2.3::IComposer; + +interface IComposer extends @2.3::IComposer { + /** + * Creates a v2.4 client of the composer. Supersedes @2.3::createClient. + * + * @return error is NONE upon success. Otherwise, + * NO_RESOURCES when the client could not be created. + * @return client is the newly created client. + */ + createClient_2_4() generates (Error error, IComposerClient client); +}; diff --git a/graphics/composer/2.4/IComposerCallback.hal b/graphics/composer/2.4/IComposerCallback.hal new file mode 100644 index 0000000000..fea24a1cbf --- /dev/null +++ b/graphics/composer/2.4/IComposerCallback.hal @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package android.hardware.graphics.composer@2.4; + +import @2.1::Display; +import @2.1::IComposerCallback; + +interface IComposerCallback extends @2.1::IComposerCallback { + /** + * Notifies the client that a vsync event has occurred. This callback must + * only be triggered when vsync is enabled for this display (through + * setVsyncEnabled). + * + * @param display is the display which has received a vsync event + * @param timestamp is the CLOCK_MONOTONIC time at which the vsync event + * occurred, in nanoseconds. + * @param vsyncPeriodNanos is the display vsync period in nanoseconds i.e. the next onVsync_2_4 + * is expected to be called vsyncPeriodNanos nanoseconds after this call. + */ + oneway onVsync_2_4(Display display, int64_t timestamp, VsyncPeriodNanos vsyncPeriodNanos); + + /** + * Notifies the client that the previously reported timing for vsync period change has been + * updated. This may occur if the composer missed the deadline for changing the vsync period + * or the client submitted a refresh frame too late. + * + * @param display is the display which vsync period change is in progress + * @param updatedTimeline is the new timeline for the vsync period change. + */ + oneway onVsyncPeriodTimingChanged(Display display, VsyncPeriodChangeTimeline updatedTimeline); +}; diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal new file mode 100644 index 0000000000..f23536cd2d --- /dev/null +++ b/graphics/composer/2.4/IComposerClient.hal @@ -0,0 +1,175 @@ +/* + * 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. + */ + +package android.hardware.graphics.composer@2.4; + +import IComposerCallback; +import @2.1::Config; +import @2.1::Display; +import @2.1::Error; +import @2.1::IComposerClient; +import @2.3::IComposerClient; + +interface IComposerClient extends @2.3::IComposerClient { + /** + * Display attributes queryable through getDisplayAttribute_2_4. + */ + enum Attribute : @2.1::IComposerClient.Attribute { + /** + * The configuration group ID (as int32_t) this config is associated to. + * Switching between configurations within the same group may be done seamlessly + * in some conditions via setActiveConfigWithConstraints. + */ + CONFIG_GROUP = 7, + }; + + /** + * Required capabilities which are supported by the display. The + * particular set of supported capabilities for a given display may be + * retrieved using getDisplayCapabilities. + */ + enum DisplayCapability : @2.3::IComposerClient.DisplayCapability { + /** + * Indicates that the display supports protected contents. + * When returned, hardware composer must be able to accept client target + * with protected buffers. + */ + PROTECTED_CONTENTS = 4, + }; + + /** + * Supersedes {@link @2.1::IComposerClient.DisplayType}. + */ + enum DisplayConnectionType : uint32_t { + /** + * Display is connected through internal port, e.g. DSI, eDP. + */ + INTERNAL = 0, + /** + * Display is connected through external port, e.g. HDMI, DisplayPort. + */ + EXTERNAL = 1, + }; + + /** + * Constraints for changing vsync period. + */ + struct VsyncPeriodChangeConstraints { + /** + * Time in CLOCK_MONOTONIC after which the vsync period may change + * (i.e., the vsync period must not change before this time). + */ + int64_t desiredTimeNanos; + + /** + * If true, requires that the vsync period change must happen seamlessly without + * a noticeable visual artifact. + */ + bool seamlessRequired; + }; + + /** + * Provides a IComposerCallback object for the device to call. + * + * This function must be called only once. + * + * @param callback is the IComposerCallback object. + */ + registerCallback_2_4(IComposerCallback callback); + + /** + * Provides a list of supported capabilities (as described in the + * definition of DisplayCapability above). This list must not change after + * initialization. + * + * @return error is NONE upon success. Otherwise, + * BAD_DISPLAY when an invalid display handle was passed in. + * @return capabilities is a list of supported capabilities. + */ + getDisplayCapabilities_2_4(Display display) + generates (Error error, vec<DisplayCapability> capabilities); + + /** + * Returns whether the given physical display is internal or external. + * + * @return error is NONE upon success. Otherwise, + * BAD_DISPLAY when the given display is invalid or virtual. + * @return type is the connection type of the display. + */ + getDisplayConnectionType(Display display) generates (Error error, DisplayConnectionType type); + + /** + * Returns a display attribute value for a particular display + * configuration. + * + * @param display is the display to query. + * @param config is the display configuration for which to return + * attribute values. + * @return error is NONE upon success. Otherwise, + * BAD_DISPLAY when an invalid display handle was passed in. + * BAD_CONFIG when config does not name a valid configuration for + * this display. + * BAD_PARAMETER when attribute is unrecognized. + * UNSUPPORTED when attribute cannot be queried for the config. + * @return value is the value of the attribute. + */ + getDisplayAttribute_2_4(Display display, Config config, Attribute attribute) + generates (Error error, int32_t value); + + /** + * Retrieves which vsync period the display is currently using. + * + * If no display configuration is currently active, this function must + * return BAD_CONFIG. If the vsync period is about to change due to a + * setActiveConfigWithConstraints call, this function must return the current vsync period + * until the change takes place. + * + * @param display is the display for which the vsync period is queried. + * @return error is NONE upon success. Otherwise, + * BAD_DISPLAY when an invalid display handle was passed in. + * BAD_CONFIG when no configuration is currently active. + * @return vsyncPeriodNanos is the current vsync period of the display. + */ + getDisplayVsyncPeriod(Display display) + generates (Error error, VsyncPeriodNanos vsyncPeriodNanos); + + /** + * Sets the active configuration and the refresh rate for this display. + * If the new config shares the same config group as the current config, + * only the vsync period shall change. + * Upon returning, the given display configuration, except vsync period, must be active and + * remain so until either this function is called again or the display is disconnected. + * When the display starts to refresh at the new vsync period, onVsync_2_4 callback must be + * called with the new vsync period. + * + * @param display is the display for which the active config is set. + * @param config is the new display configuration. + * @param vsyncPeriodChangeConstraints are the constraints required for changing vsync period. + * + * @return error is NONE upon success. Otherwise, + * BAD_DISPLAY when an invalid display handle was passed in. + * BAD_CONFIG when the configuration handle passed in is not valid + * for this display. + * SEAMLESS_NOT_ALLOWED when seamlessRequired was true but config provided doesn't + * share the same config group as the current config. + * SEAMLESS_NOT_POSSIBLE when seamlessRequired was true but the display cannot achieve + * the vsync period change without a noticeable visual artifact. + * @return timeline is the timeline for the vsync period change. + */ + setActiveConfigWithConstraints(Display display, Config config, + VsyncPeriodChangeConstraints vsyncPeriodChangeConstraints) + generates (Error error, VsyncPeriodChangeTimeline timeline); +}; diff --git a/graphics/composer/2.4/default/Android.bp b/graphics/composer/2.4/default/Android.bp new file mode 100644 index 0000000000..a30609b1a2 --- /dev/null +++ b/graphics/composer/2.4/default/Android.bp @@ -0,0 +1,46 @@ +// +// 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. +// + +cc_binary { + name: "android.hardware.graphics.composer@2.4-service", + defaults: ["hidl_defaults"], + vendor: true, + relative_install_path: "hw", + srcs: ["service.cpp"], + init_rc: ["android.hardware.graphics.composer@2.4-service.rc"], + header_libs: [ + "android.hardware.graphics.composer@2.4-passthrough", + ], + shared_libs: [ + "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.4", + "android.hardware.graphics.composer@2.1-resources", + "android.hardware.graphics.composer@2.2-resources", + "libbase", + "libbinder", + "libcutils", + "libfmq", + "libhardware", + "libhidlbase", + "libhwc2on1adapter", + "libhwc2onfbadapter", + "liblog", + "libsync", + "libutils", + ], +} diff --git a/graphics/composer/2.4/default/OWNERS b/graphics/composer/2.4/default/OWNERS new file mode 100644 index 0000000000..cc6d937cad --- /dev/null +++ b/graphics/composer/2.4/default/OWNERS @@ -0,0 +1,4 @@ +# Graphics team +lpy@google.com +stoza@google.com +vhau@google.com diff --git a/graphics/composer/2.4/default/android.hardware.graphics.composer@2.4-service.rc b/graphics/composer/2.4/default/android.hardware.graphics.composer@2.4-service.rc new file mode 100644 index 0000000000..a296b0aace --- /dev/null +++ b/graphics/composer/2.4/default/android.hardware.graphics.composer@2.4-service.rc @@ -0,0 +1,7 @@ +service vendor.hwcomposer-2-4 /vendor/bin/hw/android.hardware.graphics.composer@2.4-service + class hal animation + user system + group graphics drmrpc + capabilities SYS_NICE + onrestart restart surfaceflinger + writepid /dev/cpuset/system-background/tasks diff --git a/graphics/composer/2.4/default/service.cpp b/graphics/composer/2.4/default/service.cpp new file mode 100644 index 0000000000..98dac3e258 --- /dev/null +++ b/graphics/composer/2.4/default/service.cpp @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include <sched.h> + +#include <android/hardware/graphics/composer/2.4/IComposer.h> +#include <binder/ProcessState.h> +#include <composer-passthrough/2.4/HwcLoader.h> +#include <hidl/HidlTransportSupport.h> + +using android::hardware::graphics::composer::V2_4::IComposer; +using android::hardware::graphics::composer::V2_4::passthrough::HwcLoader; + +int main() { + // the conventional HAL might start binder services + android::ProcessState::initWithDriver("/dev/vndbinder"); + android::ProcessState::self()->setThreadPoolMaxThreadCount(4); + android::ProcessState::self()->startThreadPool(); + + // same as SF main thread + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO: %d", errno); + } + + android::hardware::configureRpcThreadpool(4, true /* will join */); + + android::sp<IComposer> composer = HwcLoader::load(); + if (composer == nullptr) { + return 1; + } + if (composer->registerAsService() != android::NO_ERROR) { + ALOGE("failed to register service"); + return 1; + } + + android::hardware::joinRpcThreadpool(); + + ALOGE("service is terminating"); + return 1; +} diff --git a/graphics/composer/2.4/types.hal b/graphics/composer/2.4/types.hal new file mode 100644 index 0000000000..065f024551 --- /dev/null +++ b/graphics/composer/2.4/types.hal @@ -0,0 +1,55 @@ +/* + * 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. + */ + +package android.hardware.graphics.composer@2.4; + +import @2.1::Error; + +enum Error : @2.1::Error { + /** + * Seamless cannot be required for configurations that don't share a config group + */ + SEAMLESS_NOT_ALLOWED = 9, + /** + * Seamless requirements cannot be met + */ + SEAMLESS_NOT_POSSIBLE = 10, +}; + +/** + * Timing for a vsync period change. + */ +struct VsyncPeriodChangeTimeline { + /** + * The time in CLOCK_MONOTONIC when the new display will start to refresh at + * the new vsync period. + */ + int64_t newVsyncAppliedTimeNanos; + + /** + * Set to true if the client is required to send a frame to be displayed before + * the change can take place. + */ + bool refreshRequired; + + /** + * The time in CLOCK_MONOTONIC when the client is expected to send the new frame. + * Should be ignored if refreshRequired is false. + */ + int64_t refreshTimeNanos; +}; + +typedef uint32_t VsyncPeriodNanos; diff --git a/graphics/composer/2.4/utils/OWNERS b/graphics/composer/2.4/utils/OWNERS new file mode 100644 index 0000000000..cc6d937cad --- /dev/null +++ b/graphics/composer/2.4/utils/OWNERS @@ -0,0 +1,4 @@ +# Graphics team +lpy@google.com +stoza@google.com +vhau@google.com diff --git a/graphics/composer/2.4/utils/command-buffer/Android.bp b/graphics/composer/2.4/utils/command-buffer/Android.bp new file mode 100644 index 0000000000..8acf0e13ca --- /dev/null +++ b/graphics/composer/2.4/utils/command-buffer/Android.bp @@ -0,0 +1,18 @@ +cc_library_headers { + name: "android.hardware.graphics.composer@2.4-command-buffer", + defaults: ["hidl_defaults"], + vendor_available: true, + shared_libs: [ + "android.hardware.graphics.composer@2.4", + ], + export_shared_lib_headers: [ + "android.hardware.graphics.composer@2.4", + ], + header_libs: [ + "android.hardware.graphics.composer@2.3-command-buffer", + ], + export_header_lib_headers: [ + "android.hardware.graphics.composer@2.3-command-buffer", + ], + export_include_dirs: ["include"], +} diff --git a/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h new file mode 100644 index 0000000000..cb391be144 --- /dev/null +++ b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h @@ -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. + */ + +#pragma once + +#ifndef LOG_TAG +#warn "ComposerCommandBuffer.h included without LOG_TAG" +#endif + +#undef LOG_NDEBUG +#define LOG_NDEBUG 0 + +#include <android/hardware/graphics/composer/2.4/IComposer.h> +#include <android/hardware/graphics/composer/2.4/IComposerClient.h> +#include <composer-command-buffer/2.3/ComposerCommandBuffer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { + +using android::hardware::MessageQueue; +using android::hardware::graphics::composer::V2_4::Error; +using android::hardware::graphics::composer::V2_4::IComposerClient; + +// This class helps build a command queue. Note that all sizes/lengths are in +// units of uint32_t's. +class CommandWriterBase : public V2_3::CommandWriterBase { + public: + CommandWriterBase(uint32_t initialMaxSize) : V2_3::CommandWriterBase(initialMaxSize) {} +}; + +// This class helps parse a command queue. Note that all sizes/lengths are in +// units of uint32_t's. +class CommandReaderBase : public V2_3::CommandReaderBase { + public: + CommandReaderBase() : V2_3::CommandReaderBase(){}; +}; + +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/utils/hal/Android.bp b/graphics/composer/2.4/utils/hal/Android.bp new file mode 100644 index 0000000000..3ee4e19a7d --- /dev/null +++ b/graphics/composer/2.4/utils/hal/Android.bp @@ -0,0 +1,36 @@ +// +// 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. +// + +cc_library_headers { + name: "android.hardware.graphics.composer@2.4-hal", + defaults: ["hidl_defaults"], + vendor_available: true, + shared_libs: [ + "android.hardware.graphics.composer@2.4", + ], + export_shared_lib_headers: [ + "android.hardware.graphics.composer@2.4", + ], + header_libs: [ + "android.hardware.graphics.composer@2.3-hal", + "android.hardware.graphics.composer@2.3-command-buffer", + ], + export_header_lib_headers: [ + "android.hardware.graphics.composer@2.3-hal", + "android.hardware.graphics.composer@2.3-command-buffer", + ], + export_include_dirs: ["include"], +} diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/Composer.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/Composer.h new file mode 100644 index 0000000000..129bae6eb9 --- /dev/null +++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/Composer.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#pragma once + +#ifndef LOG_TAG +#warning "Composer.h included without LOG_TAG" +#endif + +#include <android/hardware/graphics/composer/2.4/IComposer.h> +#include <composer-hal/2.3/Composer.h> +#include <composer-hal/2.4/ComposerClient.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace hal { + +namespace detail { + +// ComposerImpl implements V2_*::IComposer on top of V2_*::ComposerHal +template <typename Interface, typename Hal> +class ComposerImpl : public V2_3::hal::detail::ComposerImpl<Interface, Hal> { + public: + static std::unique_ptr<ComposerImpl> create(std::unique_ptr<Hal> hal) { + return std::make_unique<ComposerImpl>(std::move(hal)); + } + + explicit ComposerImpl(std::unique_ptr<Hal> hal) : BaseType2_3(std::move(hal)) {} + + // IComposer 2.4 interface + + Return<void> createClient_2_4(IComposer::createClient_2_4_cb hidl_cb) override { + std::unique_lock<std::mutex> lock(mClientMutex); + if (!waitForClientDestroyedLocked(lock)) { + hidl_cb(Error::NO_RESOURCES, nullptr); + return Void(); + } + + sp<ComposerClient> client = ComposerClient::create(mHal.get()).release(); + if (!client) { + hidl_cb(Error::NO_RESOURCES, nullptr); + return Void(); + } + + auto clientDestroyed = [this]() { onClientDestroyed(); }; + client->setOnClientDestroyed(clientDestroyed); + + mClient = client; + hidl_cb(Error::NONE, client); + return Void(); + } + + private: + using BaseType2_3 = V2_3::hal::detail::ComposerImpl<Interface, Hal>; + using BaseType2_1 = V2_1::hal::detail::ComposerImpl<Interface, Hal>; + + using BaseType2_1::mClient; + using BaseType2_1::mClientMutex; + using BaseType2_1::mHal; + using BaseType2_1::onClientDestroyed; + using BaseType2_1::waitForClientDestroyedLocked; +}; + +} // namespace detail + +using Composer = detail::ComposerImpl<IComposer, ComposerHal>; + +} // namespace hal +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h new file mode 100644 index 0000000000..4160ed97bf --- /dev/null +++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h @@ -0,0 +1,164 @@ +/* + * 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. + */ + +#pragma once + +#ifndef LOG_TAG +#warning "ComposerClient.h included without LOG_TAG" +#endif + +#include <android/hardware/graphics/composer/2.4/IComposerCallback.h> +#include <android/hardware/graphics/composer/2.4/IComposerClient.h> +#include <composer-hal/2.4/ComposerHal.h> +#include <composer-resources/2.1/ComposerResources.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace hal { + +namespace detail { + +// ComposerClientImpl implements V2_*::IComposerClient on top of V2_*::ComposerHal +template <typename Interface, typename Hal> +class ComposerClientImpl : public V2_3::hal::detail::ComposerClientImpl<Interface, Hal> { + public: + ComposerClientImpl(Hal* hal) : BaseType2_3(hal) {} + + ~ComposerClientImpl() override { mHal->unregisterEventCallback_2_4(); } + + class HalEventCallback : public Hal::EventCallback_2_4 { + public: + HalEventCallback(const sp<IComposerCallback> callback, + V2_1::hal::ComposerResources* resources) + : mCallback(callback), mResources(resources) {} + + void onHotplug(Display display, IComposerCallback::Connection connected) override { + if (connected == IComposerCallback::Connection::CONNECTED) { + mResources->addPhysicalDisplay(display); + } else if (connected == IComposerCallback::Connection::DISCONNECTED) { + mResources->removeDisplay(display); + } + + auto ret = mCallback->onHotplug(display, connected); + ALOGE_IF(!ret.isOk(), "failed to send onHotplug: %s", ret.description().c_str()); + } + + void onRefresh(Display display) override { + mResources->setDisplayMustValidateState(display, true); + auto ret = mCallback->onRefresh(display); + ALOGE_IF(!ret.isOk(), "failed to send onRefresh: %s", ret.description().c_str()); + } + + void onVsync(Display display, int64_t timestamp) override { + auto ret = mCallback->onVsync(display, timestamp); + ALOGE_IF(!ret.isOk(), "failed to send onVsync: %s", ret.description().c_str()); + } + + void onVsync_2_4(Display display, int64_t timestamp, + VsyncPeriodNanos vsyncPeriodNanos) override { + auto ret = mCallback->onVsync_2_4(display, timestamp, vsyncPeriodNanos); + ALOGE_IF(!ret.isOk(), "failed to send onVsync_2_4: %s", ret.description().c_str()); + } + + void onVsyncPeriodTimingChanged(Display display, + const VsyncPeriodChangeTimeline& updatedTimeline) override { + auto ret = mCallback->onVsyncPeriodTimingChanged(display, updatedTimeline); + ALOGE_IF(!ret.isOk(), "failed to send onVsyncPeriodTimingChanged: %s", + ret.description().c_str()); + } + + protected: + const sp<IComposerCallback> mCallback; + V2_1::hal::ComposerResources* const mResources; + }; + + Return<void> registerCallback_2_4(const sp<IComposerCallback>& callback) override { + // no locking as we require this function to be called only once + mHalEventCallback_2_4 = std::make_unique<HalEventCallback>(callback, mResources.get()); + mHal->registerEventCallback_2_4(mHalEventCallback_2_4.get()); + return Void(); + } + + Return<void> getDisplayCapabilities_2_4( + Display display, IComposerClient::getDisplayCapabilities_2_4_cb hidl_cb) override { + std::vector<IComposerClient::DisplayCapability> capabilities; + Error error = mHal->getDisplayCapabilities_2_4(display, &capabilities); + hidl_cb(error, capabilities); + return Void(); + } + + Return<void> getDisplayConnectionType( + Display display, IComposerClient::getDisplayConnectionType_cb hidl_cb) override { + IComposerClient::DisplayConnectionType type; + Error error = mHal->getDisplayConnectionType(display, &type); + hidl_cb(error, type); + return Void(); + } + + Return<void> getDisplayAttribute_2_4( + Display display, Config config, IComposerClient::Attribute attribute, + IComposerClient::getDisplayAttribute_2_4_cb hidl_cb) override { + int32_t value = 0; + Error error = mHal->getDisplayAttribute_2_4(display, config, attribute, &value); + hidl_cb(error, value); + return Void(); + } + + Return<void> getDisplayVsyncPeriod(Display display, + IComposerClient::getDisplayVsyncPeriod_cb hidl_cb) override { + VsyncPeriodNanos vsyncPeriods; + Error error = mHal->getDisplayVsyncPeriod(display, &vsyncPeriods); + hidl_cb(error, vsyncPeriods); + return Void(); + } + + Return<void> setActiveConfigWithConstraints( + Display display, Config config, + const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, + IComposerClient::setActiveConfigWithConstraints_cb hidl_cb) override { + VsyncPeriodChangeTimeline timeline = {}; + Error error = mHal->setActiveConfigWithConstraints(display, config, + vsyncPeriodChangeConstraints, &timeline); + hidl_cb(error, timeline); + return Void(); + } + + static std::unique_ptr<ComposerClientImpl> create(Hal* hal) { + auto client = std::make_unique<ComposerClientImpl>(hal); + return client->init() ? std::move(client) : nullptr; + } + + private: + using BaseType2_3 = V2_3::hal::detail::ComposerClientImpl<Interface, Hal>; + using BaseType2_1 = V2_1::hal::detail::ComposerClientImpl<Interface, Hal>; + using BaseType2_1::mHal; + std::unique_ptr<HalEventCallback> mHalEventCallback_2_4; + using BaseType2_1::mResources; +}; + +} // namespace detail + +using ComposerClient = detail::ComposerClientImpl<IComposerClient, ComposerHal>; + +} // namespace hal +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h new file mode 100644 index 0000000000..89dbe66d60 --- /dev/null +++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h @@ -0,0 +1,77 @@ +/* + * 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 + +#include <android/hardware/graphics/composer/2.4/types.h> +#include <composer-hal/2.3/ComposerHal.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace hal { + +using common::V1_1::RenderIntent; +using common::V1_2::ColorMode; +using common::V1_2::Dataspace; +using common::V1_2::Hdr; +using common::V1_2::PixelFormat; +using V2_1::Config; +using V2_1::Display; +using V2_1::Layer; +using V2_4::Error; +using V2_4::VsyncPeriodNanos; + +class ComposerHal : public V2_3::hal::ComposerHal { + public: + class EventCallback_2_4 { + public: + virtual ~EventCallback_2_4() = default; + virtual void onHotplug(Display display, IComposerCallback::Connection connected) = 0; + virtual void onRefresh(Display display) = 0; + virtual void onVsync(Display display, int64_t timestamp) = 0; + virtual void onVsync_2_4(Display display, int64_t timestamp, + VsyncPeriodNanos vsyncPeriodNanos) = 0; + virtual void onVsyncPeriodTimingChanged(Display display, + const VsyncPeriodChangeTimeline& timeline) = 0; + }; + + virtual void registerEventCallback_2_4(EventCallback_2_4* callback) = 0; + + virtual void unregisterEventCallback_2_4() = 0; + + virtual Error getDisplayCapabilities_2_4( + Display display, std::vector<IComposerClient::DisplayCapability>* outCapabilities) = 0; + virtual Error getDisplayConnectionType(Display display, + IComposerClient::DisplayConnectionType* outType) = 0; + virtual Error getDisplayAttribute_2_4(Display display, Config config, + IComposerClient::Attribute attribute, + int32_t* outValue) = 0; + virtual Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) = 0; + virtual Error setActiveConfigWithConstraints( + Display display, Config config, + const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, + VsyncPeriodChangeTimeline* timeline) = 0; +}; + +} // namespace hal +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/utils/passthrough/Android.bp b/graphics/composer/2.4/utils/passthrough/Android.bp new file mode 100644 index 0000000000..43d9aaaa78 --- /dev/null +++ b/graphics/composer/2.4/utils/passthrough/Android.bp @@ -0,0 +1,30 @@ +// +// 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. +// + +cc_library_headers { + name: "android.hardware.graphics.composer@2.4-passthrough", + defaults: ["hidl_defaults"], + vendor: true, + header_libs: [ + "android.hardware.graphics.composer@2.3-passthrough", + "android.hardware.graphics.composer@2.4-hal", + ], + export_header_lib_headers: [ + "android.hardware.graphics.composer@2.3-passthrough", + "android.hardware.graphics.composer@2.4-hal", + ], + export_include_dirs: ["include"], +} diff --git a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h new file mode 100644 index 0000000000..d59d0d5361 --- /dev/null +++ b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h @@ -0,0 +1,243 @@ +/* + * 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. + */ + +#pragma once + +#ifndef LOG_TAG +#warning "HwcHal.h included without LOG_TAG" +#endif + +#include <type_traits> + +#include <android/hardware/graphics/composer/2.4/IComposerClient.h> +#include <composer-hal/2.4/ComposerHal.h> +#include <composer-passthrough/2.3/HwcHal.h> +#include <hardware/hwcomposer_defs.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace passthrough { + +namespace detail { + +using common::V1_1::RenderIntent; +using common::V1_2::ColorMode; +using common::V1_2::Dataspace; +using common::V1_2::Hdr; +using common::V1_2::PixelFormat; +using V2_1::Config; +using V2_1::Display; +using V2_4::Error; + +// HwcHalImpl implements V2_*::hal::ComposerHal on top of hwcomposer2 +template <typename Hal> +class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> { + public: + void registerEventCallback_2_4(hal::ComposerHal::EventCallback_2_4* callback) override { + mEventCallback_2_4 = callback; + + BaseType2_1::mDispatch.registerCallback( + mDevice, HWC2_CALLBACK_HOTPLUG, this, + reinterpret_cast<hwc2_function_pointer_t>(hotplugHook)); + BaseType2_1::mDispatch.registerCallback( + mDevice, HWC2_CALLBACK_REFRESH, this, + reinterpret_cast<hwc2_function_pointer_t>(refreshHook)); + BaseType2_1::mDispatch.registerCallback( + mDevice, HWC2_CALLBACK_VSYNC, this, + reinterpret_cast<hwc2_function_pointer_t>(vsyncHook)); + BaseType2_1::mDispatch.registerCallback( + mDevice, HWC2_CALLBACK_VSYNC_2_4, this, + reinterpret_cast<hwc2_function_pointer_t>(vsync_2_4_Hook)); + + BaseType2_1::mDispatch.registerCallback( + mDevice, HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED, this, + reinterpret_cast<hwc2_function_pointer_t>(vsyncPeriodTimingChangedHook)); + } + + void unregisterEventCallback_2_4() override { + // we assume the callback functions + // + // - can be unregistered + // - can be in-flight + // - will never be called afterward + // + // which is likely incorrect + BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_HOTPLUG, this, nullptr); + BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_REFRESH, this, nullptr); + BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC, this, nullptr); + BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_2_4, this, nullptr); + BaseType2_1::mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED, + this, nullptr); + + mEventCallback_2_4 = nullptr; + } + + Error getDisplayCapabilities_2_4( + Display display, + std::vector<IComposerClient::DisplayCapability>* outCapabilities) override { + std::vector<V2_3::IComposerClient::DisplayCapability> capabilities; + V2_3::Error error_2_3 = BaseType2_3::getDisplayCapabilities(display, &capabilities); + Error error = static_cast<Error>(error_2_3); + if (error != Error::NONE) { + return error; + } + outCapabilities->clear(); + outCapabilities->reserve(capabilities.size()); + for (auto capability : capabilities) { + outCapabilities->push_back(static_cast<IComposerClient::DisplayCapability>(capability)); + } + return Error::NONE; + } + + Error getDisplayConnectionType(Display display, + IComposerClient::DisplayConnectionType* outType) override { + if (!mDispatch.getDisplayConnectionType) { + return Error::UNSUPPORTED; + } + + uint32_t type = HWC2_DISPLAY_CONNECTION_TYPE_INTERNAL; + int32_t error = mDispatch.getDisplayConnectionType(mDevice, display, &type); + *outType = static_cast<IComposerClient::DisplayConnectionType>(type); + return static_cast<Error>(error); + } + + Error getDisplayAttribute_2_4(Display display, Config config, + IComposerClient::Attribute attribute, + int32_t* outValue) override { + int32_t err = BaseType2_1::mDispatch.getDisplayAttribute( + mDevice, display, config, static_cast<int32_t>(attribute), outValue); + if (err != HWC2_ERROR_NONE && *outValue == -1) { + // Convert the error from hwcomposer2 to IComposerClient definition + return Error::BAD_PARAMETER; + } + return static_cast<Error>(err); + } + + Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override { + if (!mDispatch.getDisplayVsyncPeriod) { + return Error::UNSUPPORTED; + } + + int32_t error = mDispatch.getDisplayVsyncPeriod(mDevice, display, outVsyncPeriod); + if (error != HWC2_ERROR_NONE) { + return static_cast<Error>(error); + } + return Error::NONE; + } + + Error setActiveConfigWithConstraints( + Display display, Config config, + const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, + VsyncPeriodChangeTimeline* timeline) override { + if (!mDispatch.setActiveConfigWithConstraints) { + return Error::UNSUPPORTED; + } + + hwc_vsync_period_change_constraints_t vsync_period_change_constraints; + vsync_period_change_constraints.desiredTimeNanos = + vsyncPeriodChangeConstraints.desiredTimeNanos; + vsync_period_change_constraints.seamlessRequired = + vsyncPeriodChangeConstraints.seamlessRequired; + + hwc_vsync_period_change_timeline_t out_timeline; + int32_t error = mDispatch.setActiveConfigWithConstraints( + mDevice, display, config, &vsync_period_change_constraints, &out_timeline); + if (error != HWC2_ERROR_NONE) { + return static_cast<Error>(error); + } + timeline->newVsyncAppliedTimeNanos = out_timeline.newVsyncAppliedTimeNanos; + timeline->refreshRequired = out_timeline.refreshRequired; + timeline->refreshTimeNanos = out_timeline.refreshTimeNanos; + return Error::NONE; + } + + protected: + bool initDispatch() override { + if (!BaseType2_3::initDispatch()) { + return false; + } + + this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAY_CONNECTION_TYPE, + &mDispatch.getDisplayConnectionType); + this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAY_VSYNC_PERIOD, + &mDispatch.getDisplayVsyncPeriod); + this->initOptionalDispatch(HWC2_FUNCTION_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS, + &mDispatch.setActiveConfigWithConstraints); + return true; + } + + static void hotplugHook(hwc2_callback_data_t callbackData, hwc2_display_t display, + int32_t connected) { + auto hal = static_cast<HwcHalImpl*>(callbackData); + hal->mEventCallback_2_4->onHotplug(display, + static_cast<IComposerCallback::Connection>(connected)); + } + + static void refreshHook(hwc2_callback_data_t callbackData, hwc2_display_t display) { + auto hal = static_cast<HwcHalImpl*>(callbackData); + hal->mEventCallback_2_4->onRefresh(display); + } + + static void vsyncHook(hwc2_callback_data_t callbackData, hwc2_display_t display, + int64_t timestamp) { + auto hal = static_cast<HwcHalImpl*>(callbackData); + hal->mEventCallback_2_4->onVsync(display, timestamp); + } + + static void vsync_2_4_Hook(hwc2_callback_data_t callbackData, hwc2_display_t display, + int64_t timestamp, hwc2_vsync_period_t vsyncPeriodNanos) { + auto hal = static_cast<HwcHalImpl*>(callbackData); + hal->mEventCallback_2_4->onVsync_2_4(display, timestamp, vsyncPeriodNanos); + } + + static void vsyncPeriodTimingChangedHook(hwc2_callback_data_t callbackData, + hwc2_display_t display, + hwc_vsync_period_change_timeline_t* updated_timeline) { + auto hal = static_cast<HwcHalImpl*>(callbackData); + VsyncPeriodChangeTimeline timeline; + timeline.newVsyncAppliedTimeNanos = updated_timeline->newVsyncAppliedTimeNanos; + timeline.refreshRequired = updated_timeline->refreshRequired; + timeline.refreshTimeNanos = updated_timeline->refreshTimeNanos; + hal->mEventCallback_2_4->onVsyncPeriodTimingChanged(display, timeline); + } + + private: + struct { + HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE getDisplayConnectionType; + HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD getDisplayVsyncPeriod; + HWC2_PFN_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS setActiveConfigWithConstraints; + } mDispatch = {}; + + hal::ComposerHal::EventCallback_2_4* mEventCallback_2_4 = nullptr; + + using BaseType2_1 = V2_1::passthrough::detail::HwcHalImpl<Hal>; + using BaseType2_3 = V2_3::passthrough::detail::HwcHalImpl<Hal>; + using BaseType2_1::mDevice; +}; + +} // namespace detail + +using HwcHal = detail::HwcHalImpl<hal::ComposerHal>; + +} // namespace passthrough +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcLoader.h b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcLoader.h new file mode 100644 index 0000000000..a7cc5886e0 --- /dev/null +++ b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcLoader.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#pragma once + +#ifndef LOG_TAG +#warning "HwcLoader.h included without LOG_TAG" +#endif + +#include <composer-hal/2.4/Composer.h> +#include <composer-hal/2.4/ComposerHal.h> +#include <composer-passthrough/2.3/HwcLoader.h> +#include <composer-passthrough/2.4/HwcHal.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace passthrough { + +class HwcLoader : public V2_3::passthrough::HwcLoader { + public: + static IComposer* load() { + const hw_module_t* module = loadModule(); + if (!module) { + return nullptr; + } + + auto hal = createHalWithAdapter(module); + if (!hal) { + return nullptr; + } + + return createComposer(std::move(hal)).release(); + } + + // create a ComposerHal instance + static std::unique_ptr<hal::ComposerHal> createHal(const hw_module_t* module) { + auto hal = std::make_unique<HwcHal>(); + return hal->initWithModule(module) ? std::move(hal) : nullptr; + } + + // create a ComposerHal instance, insert an adapter if necessary + static std::unique_ptr<hal::ComposerHal> createHalWithAdapter(const hw_module_t* module) { + bool adapted; + hwc2_device_t* device = openDeviceWithAdapter(module, &adapted); + if (!device) { + return nullptr; + } + auto hal = std::make_unique<HwcHal>(); + return hal->initWithDevice(std::move(device), !adapted) ? std::move(hal) : nullptr; + } + + // create an IComposer instance + static std::unique_ptr<IComposer> createComposer(std::unique_ptr<hal::ComposerHal> hal) { + return hal::Composer::create(std::move(hal)); + } +}; + +} // namespace passthrough +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/utils/vts/Android.bp b/graphics/composer/2.4/utils/vts/Android.bp new file mode 100644 index 0000000000..b87a116865 --- /dev/null +++ b/graphics/composer/2.4/utils/vts/Android.bp @@ -0,0 +1,42 @@ +// +// 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. +// + +cc_library_static { + name: "android.hardware.graphics.composer@2.4-vts", + defaults: ["hidl_defaults"], + srcs: [ + "ComposerVts.cpp", + ], + static_libs: [ + "VtsHalHidlTargetTestBase", + "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3-vts", + "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.4", + ], + header_libs: [ + "android.hardware.graphics.composer@2.1-command-buffer", + "android.hardware.graphics.composer@2.2-command-buffer", + "android.hardware.graphics.composer@2.3-command-buffer", + ], + cflags: [ + "-O0", + "-g", + "-DLOG_TAG=\"ComposerVts\"", + ], + export_include_dirs: ["include"], +} diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp new file mode 100644 index 0000000000..35ac23f7ff --- /dev/null +++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#include <composer-vts/2.4/ComposerVts.h> + +#include <VtsHalHidlTargetTestBase.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace vts { + +using V2_4::Error; + +Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {} + +Composer::Composer(const std::string& name) + : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {} + +Composer::Composer(const sp<IComposer>& composer) + : V2_3::vts::Composer(composer), mComposer(composer) {} + +std::unique_ptr<ComposerClient> Composer::createClient() { + std::unique_ptr<ComposerClient> client; + mComposer->createClient_2_4([&client](const auto& tmpError, const auto& tmpClient) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to create client"; + client = std::make_unique<ComposerClient>(tmpClient); + }); + + return client; +} + +sp<IComposerClient> ComposerClient::getRaw() const { + return mClient; +} + +Error ComposerClient::getDisplayCapabilities( + Display display, std::vector<IComposerClient::DisplayCapability>* outCapabilities) { + Error error = Error::NONE; + mClient->getDisplayCapabilities_2_4(display, + [&](const auto& tmpError, const auto& tmpCapabilities) { + error = tmpError; + *outCapabilities = tmpCapabilities; + }); + return error; +} + +Error ComposerClient::getDisplayConnectionType(Display display, + IComposerClient::DisplayConnectionType* outType) { + Error error = Error::NONE; + mClient->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) { + error = tmpError; + *outType = tmpType; + }); + return error; +} + +int32_t ComposerClient::getDisplayAttribute_2_4( + android::hardware::graphics::composer::V2_1::Display display, + android::hardware::graphics::composer::V2_1::Config config, + IComposerClient::Attribute attribute) { + int32_t value = 0; + mClient->getDisplayAttribute_2_4( + display, config, attribute, [&](const auto& tmpError, const auto& tmpValue) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to get display attribute"; + value = tmpValue; + }); + + return value; +} + +Error ComposerClient::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) { + Error error = Error::NONE; + mClient->getDisplayVsyncPeriod(display, [&](const auto& tmpError, const auto& tmpVsyncPeriod) { + error = tmpError; + *outVsyncPeriod = tmpVsyncPeriod; + }); + return error; +} + +Error ComposerClient::setActiveConfigWithConstraints( + Display display, Config config, + const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, + VsyncPeriodChangeTimeline* timeline) { + Error error = Error::NONE; + mClient->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints, + [&](const auto& tmpError, const auto& tmpTimeline) { + error = tmpError; + *timeline = tmpTimeline; + }); + return error; +} + +} // namespace vts +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h new file mode 100644 index 0000000000..83e74ed698 --- /dev/null +++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#pragma once + +#include <memory> +#include <vector> + +#include <VtsHalHidlTargetTestBase.h> +#include <android/hardware/graphics/composer/2.4/IComposer.h> +#include <android/hardware/graphics/composer/2.4/IComposerClient.h> +#include <composer-vts/2.3/ComposerVts.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace vts { + +using common::V1_1::RenderIntent; +using common::V1_2::ColorMode; +using common::V1_2::Dataspace; +using common::V1_2::Hdr; +using common::V1_2::PixelFormat; +using V2_1::Config; +using V2_1::Display; +using V2_4::Error; +using V2_4::IComposer; +using V2_4::IComposerClient; +using V2_4::VsyncPeriodNanos; + +class ComposerClient; + +// A wrapper to IComposer. +class Composer : public V2_3::vts::Composer { + public: + Composer(); + explicit Composer(const std::string& name); + explicit Composer(const sp<IComposer>& composer); + + std::unique_ptr<ComposerClient> createClient(); + + private: + const sp<IComposer> mComposer; +}; + +// A wrapper to IComposerClient. +class ComposerClient : public V2_3::vts::ComposerClient { + public: + explicit ComposerClient(const sp<IComposerClient>& client) + : V2_3::vts::ComposerClient(client), mClient(client) {} + + sp<IComposerClient> getRaw() const; + + Error getDisplayCapabilities( + Display display, + std::vector<IComposerClient::DisplayCapability>* outDisplayCapabilities); + + Error getDisplayConnectionType(Display display, + IComposerClient::DisplayConnectionType* outType); + + int32_t getDisplayAttribute_2_4(Display display, Config config, + IComposerClient::Attribute attribute); + + Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriods); + + Error setActiveConfigWithConstraints( + Display display, Config config, + const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, + VsyncPeriodChangeTimeline* timeline); + + private: + const sp<IComposerClient> mClient; +}; + +} // namespace vts +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/composer/2.4/vts/functional/Android.bp b/graphics/composer/2.4/vts/functional/Android.bp new file mode 100644 index 0000000000..937af3d4d2 --- /dev/null +++ b/graphics/composer/2.4/vts/functional/Android.bp @@ -0,0 +1,56 @@ +// +// 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. +// + +cc_test { + name: "VtsHalGraphicsComposerV2_4TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalGraphicsComposerV2_4TargetTest.cpp"], + + // TODO(b/64437680): Assume these libs are always available on the device. + shared_libs: [ + "libfmq", + "libsync", + ], + 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", + "android.hardware.graphics.composer@2.2-vts", + "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.3-vts", + "android.hardware.graphics.composer@2.4", + "android.hardware.graphics.composer@2.4-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", + "android.hardware.graphics.composer@2.2-command-buffer", + "android.hardware.graphics.composer@2.3-command-buffer", + "android.hardware.graphics.composer@2.4-command-buffer", + ], + disable_framework: true, + test_suites: ["general-tests", "vts-core"], +} diff --git a/graphics/composer/2.4/vts/functional/OWNERS b/graphics/composer/2.4/vts/functional/OWNERS new file mode 100644 index 0000000000..b3ea6bef7b --- /dev/null +++ b/graphics/composer/2.4/vts/functional/OWNERS @@ -0,0 +1,8 @@ +# Graphics team +lpy@google.com +stoza@google.com +vhau@google.com + +# VTS team +yim@google.com +zhuoyao@google.com diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp new file mode 100644 index 0000000000..f038f551e3 --- /dev/null +++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp @@ -0,0 +1,391 @@ +/* + * 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_test@2.4" + +#include <algorithm> +#include <thread> + +#include <android-base/logging.h> +#include <android/hardware/graphics/mapper/2.0/IMapper.h> +#include <composer-command-buffer/2.4/ComposerCommandBuffer.h> +#include <composer-vts/2.1/GraphicsComposerCallback.h> +#include <composer-vts/2.1/TestCommandReader.h> +#include <composer-vts/2.4/ComposerVts.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> +#include <mapper-vts/2.0/MapperVts.h> +#include <utils/Timers.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace composer { +namespace V2_4 { +namespace vts { +namespace { + +using common::V1_0::BufferUsage; +using common::V1_1::RenderIntent; +using common::V1_2::ColorMode; +using common::V1_2::Dataspace; +using common::V1_2::PixelFormat; +using mapper::V2_0::IMapper; +using mapper::V2_0::vts::Gralloc; + +class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> { + protected: + void SetUp() override { + ASSERT_NO_FATAL_FAILURE( + mComposer = std::make_unique<Composer>(IComposer::getService(GetParam()))); + 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(); + + mInvalidDisplayId = GetInvalidDisplayId(); + + // explicitly disable vsync + mComposerClient->setVsyncEnabled(mPrimaryDisplay, false); + mComposerCallback->setVsyncAllowed(false); + + mWriter = std::make_unique<CommandWriterBase>(1024); + mReader = std::make_unique<V2_1::vts::TestCommandReader>(); + } + + void TearDown() override { + ASSERT_EQ(0, mReader->mErrors.size()); + ASSERT_EQ(0, mReader->mCompositionChanges.size()); + if (mComposerCallback != nullptr) { + EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount()); + EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount()); + EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount()); + } + } + + // returns an invalid display id (one that has not been registered to a + // display. Currently assuming that a device will never have close to + // std::numeric_limit<uint64_t>::max() displays registered while running tests + Display GetInvalidDisplayId() { + std::vector<Display> validDisplays = mComposerCallback->getDisplays(); + uint64_t id = std::numeric_limits<uint64_t>::max(); + while (id > 0) { + if (std::find(validDisplays.begin(), validDisplays.end(), id) == validDisplays.end()) { + return id; + } + id--; + } + + return 0; + } + + // returns an invalid config id (one that has not been registered to a + // display). Currently assuming that a device will never have close to + // std::numeric_limit<uint64_t>::max() configs registered while running tests + Display GetInvalidConfigId(Display display) { + std::vector<Config> validConfigs = mComposerClient->getDisplayConfigs(display); + uint64_t id = std::numeric_limits<uint64_t>::max(); + while (id > 0) { + if (std::find(validConfigs.begin(), validConfigs.end(), id) == validConfigs.end()) { + return id; + } + id--; + } + + return 0; + } + + void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); } + + void Test_setActiveConfigWithConstraints( + const IComposerClient::VsyncPeriodChangeConstraints& constraints); + + std::unique_ptr<Composer> mComposer; + std::unique_ptr<ComposerClient> mComposerClient; + sp<V2_1::vts::GraphicsComposerCallback> mComposerCallback; + // the first display and is assumed never to be removed + Display mPrimaryDisplay; + Display mInvalidDisplayId; + std::unique_ptr<CommandWriterBase> mWriter; + std::unique_ptr<V2_1::vts::TestCommandReader> mReader; + + private: + Display waitForFirstDisplay() { + while (true) { + std::vector<Display> displays = mComposerCallback->getDisplays(); + if (displays.empty()) { + usleep(5 * 1000); + continue; + } + + return displays[0]; + } + } +}; + +// Tests for IComposerClient::Command. +class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest { + protected: + void SetUp() override { + ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp()); + + ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>()); + + mWriter = std::make_unique<CommandWriterBase>(1024); + mReader = std::make_unique<V2_1::vts::TestCommandReader>(); + } + + void TearDown() override { + ASSERT_EQ(0, mReader->mErrors.size()); + ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown()); + } + + const native_handle_t* allocate() { + IMapper::BufferDescriptorInfo info{}; + info.width = 64; + info.height = 64; + info.layerCount = 1; + info.format = static_cast<common::V1_0::PixelFormat>(PixelFormat::RGBA_8888); + info.usage = + static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN); + + return mGralloc->allocate(info); + } + + void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); } + + std::unique_ptr<CommandWriterBase> mWriter; + std::unique_ptr<V2_1::vts::TestCommandReader> mReader; + + private: + std::unique_ptr<Gralloc> mGralloc; +}; + +TEST_P(GraphicsComposerHidlTest, getDisplayCapabilitiesBadDisplay) { + std::vector<IComposerClient::DisplayCapability> capabilities; + const auto error = mComposerClient->getDisplayCapabilities(mInvalidDisplayId, &capabilities); + EXPECT_EQ(Error::BAD_DISPLAY, error); +} + +TEST_P(GraphicsComposerHidlTest, getDisplayConnectionType) { + IComposerClient::DisplayConnectionType type; + EXPECT_EQ(Error::BAD_DISPLAY, + mComposerClient->getDisplayConnectionType(mInvalidDisplayId, &type)); + + for (Display display : mComposerCallback->getDisplays()) { + EXPECT_EQ(Error::NONE, mComposerClient->getDisplayConnectionType(display, &type)); + } +} + +TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4) { + std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay); + for (auto config : configs) { + const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{ + IComposerClient::Attribute::WIDTH, + IComposerClient::Attribute::HEIGHT, + IComposerClient::Attribute::VSYNC_PERIOD, + IComposerClient::Attribute::CONFIG_GROUP, + }}; + for (auto attribute : requiredAttributes) { + mComposerClient->getDisplayAttribute_2_4(mPrimaryDisplay, config, attribute); + } + + const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{ + IComposerClient::Attribute::DPI_X, + IComposerClient::Attribute::DPI_Y, + }}; + for (auto attribute : optionalAttributes) { + mComposerClient->getRaw()->getDisplayAttribute_2_4( + mPrimaryDisplay, config, attribute, [&](const auto& tmpError, const auto&) { + EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED); + }); + } + } +} + +TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod_BadDisplay) { + VsyncPeriodNanos vsyncPeriodNanos; + EXPECT_EQ(Error::BAD_DISPLAY, + mComposerClient->getDisplayVsyncPeriod(mInvalidDisplayId, &vsyncPeriodNanos)); +} + +TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) { + for (Display display : mComposerCallback->getDisplays()) { + for (Config config : mComposerClient->getDisplayConfigs(display)) { + mComposerClient->setActiveConfig(display, config); + + VsyncPeriodNanos vsyncPeriodNanos; + VsyncPeriodNanos expectedvsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4( + display, config, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD); + int retryCount = 100; + do { + std::this_thread::sleep_for(10ms); + EXPECT_EQ(Error::NONE, + mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos)); + --retryCount; + } while (retryCount > 0); + + EXPECT_EQ(vsyncPeriodNanos, expectedvsyncPeriodNanos); + } + } +} + +TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_BadDisplay) { + VsyncPeriodChangeTimeline timeline; + IComposerClient::VsyncPeriodChangeConstraints constraints; + + constraints.seamlessRequired = false; + constraints.desiredTimeNanos = systemTime(); + + EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->setActiveConfigWithConstraints( + mInvalidDisplayId, Config(0), constraints, &timeline)); +} + +TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_BadConfig) { + VsyncPeriodChangeTimeline timeline; + IComposerClient::VsyncPeriodChangeConstraints constraints; + + constraints.seamlessRequired = false; + constraints.desiredTimeNanos = systemTime(); + + for (Display display : mComposerCallback->getDisplays()) { + Config invalidConfigId = GetInvalidConfigId(display); + EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->setActiveConfigWithConstraints( + display, invalidConfigId, constraints, &timeline)); + } +} + +TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_SeamlessNotAllowed) { + VsyncPeriodChangeTimeline timeline; + IComposerClient::VsyncPeriodChangeConstraints constraints; + + constraints.seamlessRequired = true; + constraints.desiredTimeNanos = systemTime(); + + for (Display display : mComposerCallback->getDisplays()) { + for (Config config : mComposerClient->getDisplayConfigs(display)) { + int32_t configGroup = mComposerClient->getDisplayAttribute_2_4( + display, config, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP); + + for (Config otherConfig : mComposerClient->getDisplayConfigs(display)) { + int32_t otherConfigGroup = mComposerClient->getDisplayAttribute_2_4( + display, otherConfig, + IComposerClient::IComposerClient::Attribute::CONFIG_GROUP); + if (configGroup != otherConfigGroup) { + mComposerClient->setActiveConfig(display, config); + EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED, + mComposerClient->setActiveConfigWithConstraints( + display, otherConfig, constraints, &timeline)); + } + } + } + } +} + +void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints( + const IComposerClient::VsyncPeriodChangeConstraints& constraints) { + VsyncPeriodChangeTimeline timeline = {}; + + for (Display display : mComposerCallback->getDisplays()) { + for (Config config : mComposerClient->getDisplayConfigs(display)) { + mComposerClient->setActiveConfig(display, config); + + int32_t configVsyncPeriod = mComposerClient->getDisplayAttribute_2_4( + display, config, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD); + for (Config otherConfig : mComposerClient->getDisplayConfigs(display)) { + if (config == otherConfig) { + continue; + } + + int32_t otherVsyncPeriod = mComposerClient->getDisplayAttribute_2_4( + display, otherConfig, + IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD); + + if (configVsyncPeriod == otherVsyncPeriod) { + continue; + } + + EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints( + display, otherConfig, constraints, &timeline)); + + if (timeline.refreshRequired) { + // TODO(b/143775556): handle this case; + continue; + } + + EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos >= constraints.desiredTimeNanos); + + // Refresh rate should change within a reasonable time + constexpr nsecs_t kReasonableTimeForChange = 1'000'000'000; // 1 second + EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos - constraints.desiredTimeNanos <= + kReasonableTimeForChange); + + while (systemTime() <= timeline.newVsyncAppliedTimeNanos) { + VsyncPeriodNanos vsyncPeriodNanos; + EXPECT_EQ(Error::NONE, + mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos)); + + if (systemTime() <= constraints.desiredTimeNanos) { + EXPECT_NE(vsyncPeriodNanos, otherVsyncPeriod); + } + + if (vsyncPeriodNanos == otherVsyncPeriod) { + break; + } + std::this_thread::sleep_for(10ms); + } + VsyncPeriodNanos vsyncPeriodNanos; + EXPECT_EQ(Error::NONE, + mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos)); + EXPECT_EQ(vsyncPeriodNanos, otherVsyncPeriod); + } + } + } +} + +TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints) { + IComposerClient::VsyncPeriodChangeConstraints constraints; + + constraints.seamlessRequired = false; + constraints.desiredTimeNanos = systemTime(); + Test_setActiveConfigWithConstraints(constraints); +} + +TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_delayed) { + IComposerClient::VsyncPeriodChangeConstraints constraints; + + constexpr auto kDelayForChange = 300ms; + constraints.seamlessRequired = false; + constraints.desiredTimeNanos = systemTime() + kDelayForChange.count(); + Test_setActiveConfigWithConstraints(constraints); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, GraphicsComposerHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)), + android::hardware::PrintInstanceNameToString); + +} // namespace +} // namespace vts +} // namespace V2_4 +} // namespace composer +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/mapper/4.0/Android.bp b/graphics/mapper/4.0/Android.bp new file mode 100644 index 0000000000..42c4942e85 --- /dev/null +++ b/graphics/mapper/4.0/Android.bp @@ -0,0 +1,21 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.graphics.mapper@4.0", + root: "android.hardware", + vndk: { + enabled: true, + support_system_process: true, + }, + srcs: [ + "types.hal", + "IMapper.hal", + ], + interfaces: [ + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hidl.base@1.0", + ], + gen_java: false, +} diff --git a/graphics/mapper/4.0/IMapper.hal b/graphics/mapper/4.0/IMapper.hal new file mode 100644 index 0000000000..a1f07a4388 --- /dev/null +++ b/graphics/mapper/4.0/IMapper.hal @@ -0,0 +1,519 @@ +/* + * 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. + */ + +package android.hardware.graphics.mapper@4.0; + +import android.hardware.graphics.common@1.2::BufferUsage; +import android.hardware.graphics.common@1.2::PixelFormat; +import android.hardware.graphics.common@1.2::Rect; + +interface IMapper { + struct BufferDescriptorInfo { + /** + * The name of the buffer. Useful for debugging/tracing. + */ + string name; + + /** + * The width specifies how many columns of pixels must be in the + * allocated buffer, but does not necessarily represent the offset in + * columns between the same column in adjacent rows. The rows may be + * padded. + */ + uint32_t width; + + /** + * The height specifies how many rows of pixels must be in the + * allocated buffer. + */ + uint32_t height; + + /** + * The number of image layers that must be in the allocated buffer. + */ + uint32_t layerCount; + + /** + * Buffer pixel format. + */ + PixelFormat format; + + /** + * Buffer usage mask; valid flags can be found in the definition of + * BufferUsage. + */ + bitfield<BufferUsage> usage; + }; + + struct Rect { + int32_t left; + int32_t top; + int32_t width; + int32_t height; + }; + + /** + * Creates a buffer descriptor. The descriptor can be used with IAllocator + * to allocate buffers. + * + * Since the buffer descriptor fully describes a buffer, any device + * dependent or device independent checks must be performed here whenever + * possible. Specifically, when layered buffers are not supported, this + * function must return `UNSUPPORTED` if `description.layers` is great than + * 1. + * + * @param description Attributes of the descriptor. + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_VALUE` if any of the specified attributes are invalid or + * inconsistent. + * - `NO_RESOURCES` if the creation cannot be fullfilled due to + * unavailability of resources. + * - `UNSUPPORTED` when any of the specified attributes are not + * supported. + * @return descriptor Newly created buffer descriptor. + */ + createDescriptor(BufferDescriptorInfo description) + generates (Error error, + BufferDescriptor descriptor); + + /** + * Imports a raw buffer handle to create an imported buffer handle for use + * with the rest of the mapper or with other in-process libraries. + * + * A buffer handle is considered raw when it is cloned (e.g., with + * `native_handle_clone()`) from another buffer handle locally, or when it + * is received from another HAL server/client or another process. A raw + * buffer handle must not be used to access the underlying graphic + * buffer. It must be imported to create an imported handle first. + * + * This function must at least validate the raw handle before creating the + * imported handle. It must also support importing the same raw handle + * multiple times to create multiple imported handles. The imported handle + * must be considered valid everywhere in the process, including in + * another instance of the mapper. + * + * Because of passthrough HALs, a raw buffer handle received from a HAL + * may actually have been imported in the process. importBuffer() must treat + * such a handle as if it is raw and must not return `BAD_BUFFER`. The + * returned handle is independent from the input handle as usual, and + * freeBuffer() must be called on it when it is no longer needed. + * + * @param rawHandle Raw buffer handle to import. + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the raw handle is invalid. + * - `NO_RESOURCES` if the raw handle cannot be imported due to + * unavailability of resources. + * @return buffer Imported buffer handle that has the type + * `buffer_handle_t` which is a handle type. + */ + importBuffer(handle rawHandle) generates (Error error, pointer buffer); + + /** + * Frees a buffer handle. Buffer handles returned by importBuffer() must be + * freed with this function when no longer needed. + * + * This function must free up all resources allocated by importBuffer() for + * the imported handle. For example, if the imported handle was created + * with `native_handle_create()`, this function must call + * `native_handle_close()` and `native_handle_delete()`. + * + * @param buffer Imported buffer handle. + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the buffer is invalid. + */ + freeBuffer(pointer buffer) generates (Error error); + + /** + * Validates that the buffer can be safely accessed by a caller who assumes + * the specified @p description and @p stride. This must at least validate + * that the buffer size is large enough. Validating the buffer against + * individual buffer attributes is optional. + * + * @param buffer Buffer to validate against. + * @param description Attributes of the buffer. + * @param stride Stride returned by IAllocator::allocate(). + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the buffer is invalid. + * - `BAD_VALUE` if the buffer cannot be safely accessed. + */ + validateBufferSize(pointer buffer, + BufferDescriptorInfo description, + uint32_t stride) + generates (Error error); + + /** + * Calculates the transport size of a buffer. An imported buffer handle is a + * raw buffer handle with the process-local runtime data appended. This + * function, for example, allows a caller to omit the process-local runtime + * data at the tail when serializing the imported buffer handle. + * + * Note that a client might or might not omit the process-local runtime data + * when sending an imported buffer handle. The mapper must support both + * cases on the receiving end. + * + * @param buffer Buffer to get the transport size from. + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the buffer is invalid. + * @return numFds The number of file descriptors needed for transport. + * @return numInts The number of integers needed for transport. + */ + getTransportSize(pointer buffer) + generates (Error error, + uint32_t numFds, + uint32_t numInts); + + /** + * Locks the given buffer for the specified CPU usage. + * + * Locking the same buffer simultaneously from multiple threads is + * permitted, but if any of the threads attempt to lock the buffer for + * writing, the behavior is undefined, except that it must not cause + * process termination or block the client indefinitely. Leaving the + * buffer content in an indeterminate state or returning an error are both + * acceptable. + * + * 1D buffers (width = size in bytes, height = 1, pixel_format = BLOB) must + * "lock in place". The buffers must be directly accessible via mapping. + * + * The client must not modify the content of the buffer outside of + * @p accessRegion, and the device need not guarantee that content outside + * of @p accessRegion is valid for reading. The result of reading or writing + * outside of @p accessRegion is undefined, except that it must not cause + * process termination. + * + * This function can lock both single-planar and multi-planar formats. The caller + * should use get() to get information about the buffer they are locking. + * get() can be used to get information about the planes, offsets, stride, + * etc. + * + * This function must also work on buffers with + * `AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_*` if supported by the device, as well + * as with any other formats requested by multimedia codecs when they are + * configured with a flexible-YUV-compatible color format. + * + * On success, @p data must be filled with a pointer to the locked buffer + * memory. This address will represent the top-left corner of the entire + * buffer, even if @p accessRegion does not begin at the top-left corner. + * + * The locked buffer must adhere to the format requested at allocation time + * in the BufferDescriptorInfo. + * + * @param buffer Buffer to lock. + * @param cpuUsage CPU usage flags to request. See +ndk + * libnativewindow#AHardwareBuffer_UsageFlags for possible values. + * @param accessRegion Portion of the buffer that the client intends to + * access. + * @param acquireFence Handle containing a file descriptor referring to a + * sync fence object, which will be signaled when it is safe for the + * mapper to lock the buffer. @p acquireFence may be an empty fence if + * it is already safe to lock. + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the buffer is invalid or is incompatible with this + * function. + * - `BAD_VALUE` if @p cpuUsage is 0, contains non-CPU usage flags, or + * is incompatible with the buffer. Also if the @p accessRegion is + * outside the bounds of the buffer or the accessRegion is invalid. + * - `NO_RESOURCES` if the buffer cannot be locked at this time. Note + * that locking may succeed at a later time. + * @return data CPU-accessible pointer to the buffer data. + */ + lock(pointer buffer, + uint64_t cpuUsage, + Rect accessRegion, + handle acquireFence) + generates (Error error, + pointer data); + + /** + * Unlocks a buffer to indicate all CPU accesses to the buffer have + * completed. + * + * @param buffer Buffer to unlock. + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the buffer is invalid or not locked. + * @return releaseFence Handle containing a file descriptor referring to a + * sync fence object. The sync fence object will be signaled when the + * mapper has completed any pending work. @p releaseFence may be an + * empty fence. + */ + unlock(pointer buffer) generates (Error error, handle releaseFence); + + /** + * Test whether the given BufferDescriptorInfo is allocatable. + * + * If this function returns true, it means that a buffer with the given + * description can be allocated on this implementation, unless resource + * exhaustion occurs. If this function returns false, it means that the + * allocation of the given description will never succeed. + * + * @param description the description of the buffer + * @return supported whether the description is supported + */ + isSupported(BufferDescriptorInfo description) + generates (Error error, + bool supported); + + + /** + * Description for get(...), set(...) and getFromBufferDescriptorInfo(...) + * + * ------------ Overview ----------------------------------- + * Gralloc 4 adds support for getting and setting buffer metadata on a buffer. + * + * To get buffer metadata, the client passes in a buffer handle and a token that + * represents the type of buffer metadata they would like to get. IMapper returns + * a byte stream that contains the buffer metadata. To set the buffer metadata, the + * client passes in a buffer handle and a token that represents the type of buffer + * metadata they would like to set and a byte stream that contains the buffer metadata + * they are setting. + * + * Buffer metadata is global for a buffer. When the metadata is set on the buffer + * in a process, the updated metadata should be available to all other processes. + * Please see "Storing and Propagating Metadata" below for more details. + * + * The getter and setter functions have been optimized for easy vendor extension. + * They do not require a formal HIDL extension to add support for getting and setting + * vendor defined buffer metadata. In order to allow easy extension, the types used + * here are not typical HIDL types. See "Buffer Metadata Token" and + * "Buffer Metadata Stream" below for more details. + * + * ------------ Storing and Propagating Metadata ----------- + * Buffer metadata must be global. Any changes to the metadata must be propagated + * to all other processes immediately. Vendors may chose how they would like support + * this functionality. + * + * We recommend supporting this functionality by allocating an extra page of shared + * memory and storing it in the buffer's native_handle_t. The buffer metadata can + * be stored in the extra page of shared memory. Set operations are automatically + * propagated to all other processes. + * + * ------------ Buffer Metadata Synchronization ------------ + * There are no explicit buffer metadata synchronization primitives. Many devices + * before gralloc 4 already support getting and setting of global buffer metadata + * with no explicit synchronization primitives. Adding synchronization primitives + * would just add unnecessary complexity. + * + * The general rule is if a process has permission to write to a buffer, they + * have permission to write to the buffer's metadata. If a process has permission + * to read from a buffer, they have permission to read the buffer's metadata. + * + * There is one exception to this rule. Fences CANNOT be used to protect a buffer's + * metadata. A process should finish writing to a buffer's metadata before + * sending the buffer to another process that will read or write to the buffer. + * This exception is needed because sometimes userspace needs to read the + * buffer's metadata before the buffer's contents are ready. + * + * As a simple example: an app renders to a buffer and then displays the buffer. + * In this example when the app renders to the buffer, both the buffer and its + * metadata need to be updated. The app's process queues up its work on the GPU + * and gets back an acquire fence. The app's process must update the buffer's + * metadata before enqueuing the buffer to SurfaceFlinger. The app process CANNOT + * update the buffer's metadata after enqueuing the buffer. When HardwareComposer + * receives the buffer, it is immediately safe to read the buffer's metadata + * and use it to program the display driver. To read the buffer's contents, + * display driver must still wait on the acquire fence. + * + * ------------ Buffer Metadata Token ---------------------- + * In order to allow arbitrary vendor defined metadata, we could not use a + * HIDL enum as the buffer metadata token. Extending a HIDL enum requires a full + * HIDL extension. We also could not use a simple non-HIDL enum because vendor + * defined enums from different vendors could collide. Instead we have defined + * a struct that has a string representing the enum type and an int that + * represents the enum value. The string protects different enum values from + * colliding. + * + * The token struct (MetadataType) is defined as a HIDL struct since it + * is passed into a HIDL function. The standard buffer metadata types are NOT + * defined as a HIDL enum because it would have required a new IMapper version + * just to add future standard buffer metadata types. By putting the enum in the + * stable AIDL (hardware/interfaces/graphics/common/aidl/android/hardware/ + * graphics/common/StandardMetadataType.aidl), vendors will be able to optionally + * choose to support future standard buffer metadata types without upgrading + * HIDL versions. For more information see the description of "struct MetadataType". + * + * ------------ Buffer Metadata Stream --------------------- + * The buffer metadata is get and set as a byte stream (vec<uint8_t>). By getting + * and setting buffer metadata as a byte stream, vendors can use the standard + * getters and setter functions defined here. Vendors do NOT need to add their own + * getters and setter functions for each new type of buffer metadata. + * + * Converting buffer metadata into a byte stream can be non-trivial. For the standard + * buffer metadata types defined in StandardMetadataType.aidl, there are also + * support functions that will encode the buffer metadata into a byte stream + * and decode the buffer metadata from a byte stream. We STRONGLY recommend using + * these support functions. The framework will use them when getting and setting + * metadata. The support functions are defined in + * frameworks/native/libs/gralloc/types/include/gralloctypes/Gralloc4.h. + */ + + /** + * MetadataType represents the different types of buffer metadata that could be + * associated with a buffer. It is used by IMapper to help get and set buffer metadata + * on the buffer's native handle. + * + * Standard buffer metadata will have the name field set to + * "android.hardware.graphics.common.StandardMetadataType" and will contain values + * from StandardMetadataType.aidl. + * + * This struct should be "extended" by devices that use a proprietary or non-standard + * buffer metadata. To extend the struct, first create a custom @VendorStability vendor + * AIDL interface that defines the new type(s) you would like to support. Set the + * struct's name field to the custom aidl interface's name + * (eg. "vendor.mycompanyname.graphics.common.MetadataType"). Set the struct's value + * field to the custom @VendorStabilty vendor AIDL interface. + * + * Each company should create their own StandardMetadataType.aidl extension. The name + * field prevents values from different companies from colliding. + */ + struct MetadataType { + string name; + int64_t value; + }; + + /** + * Gets the buffer metadata for a given MetadataType. + * + * Buffer metadata can be changed after allocation so clients should avoid "caching" + * the buffer metadata. For example, if the video resolution changes and the buffers + * are not reallocated, several buffer metadata values may change without warning. + * Clients should not expect the values to be constant. They should requery them every + * frame. The only exception is buffer metadata that is determined at allocation + * time. For StandardMetadataType values, only BUFFER_ID, NAME, WIDTH, + * HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and USAGE are safe to cache because + * they are determined at allocation time. + * + * @param buffer Buffer containing desired metadata + * @param metadataType MetadataType for the metadata value being queried + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the raw handle is invalid. + * - `NO_RESOURCES` if the get cannot be fullfilled due to unavailability of + * resources. + * - `UNSUPPORTED` when metadataType is unknown/unsupported. + * IMapper must support getting all StandardMetadataType.aidl values defined + * at the time the device first launches. + * @return metadata Vector of bytes representing the buffer metadata associated with + * the MetadataType. + */ + get(pointer buffer, MetadataType metadataType) + generates (Error error, + vec<uint8_t> metadata); + + /** + * Sets the global value for a given MetadataType. + * + * Metadata fields are not required to be settable. This function can + * return Error::UNSUPPORTED whenever it doesn't support setting a + * particular Metadata field. + * + * The framework may attempt to set the following StandardMetadataType + * values: DATASPACE, PER_FRAME_METADATA, PER_FRAME_METADATA_BLOB and BLEND_MODE. + * We strongly encourage everyone to support setting as many of those fields as + * possible. If a device's Composer implementation supports a field, it should be + * supported here. Over time these metadata fields will be moved out of + * Composer/BufferQueue/etc. and into the buffer's Metadata fields. + * If a device's IMapper doesn't support setting those Metadata fields, + * eventually the device may not longer be able to support these fields. + * + * @param buffer Buffer receiving desired metadata + * @param metadataType MetadataType for the metadata value being set + * @param metadata Vector of bytes representing the value associated with + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_BUFFER` if the raw handle is invalid. + * - `BAD_VALUE` when the field is constant and can never be set (such as + * BUFFER_ID, NAME, WIDTH, HEIGHT, LAYER_COUNT, PIXEL_FORMAT_REQUESTED and + * USAGE) + * - `NO_RESOURCES` if the set cannot be fullfilled due to unavailability of + * resources. + * - `UNSUPPORTED` when metadataType is unknown/unsupported or setting + * it is unsupported. Unsupported should also be returned if the metadata + * is malformed. + */ + set(pointer buffer, MetadataType metadataType, vec<uint8_t> metadata) + generates (Error error); + + /** + * Given a BufferDescriptorInfo, gets the starting value of a given + * MetadataType. This can be used to query basic information about a buffer + * before the buffer is allocated. + * + * @param description Attributes of the descriptor. + * @param metadataType MetadataType for the metadata value being queried + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `BAD_VALUE` if any of the specified BufferDescriptorInfo attributes + * are invalid. + * - `NO_RESOURCES` if the get cannot be fullfilled due to unavailability of + * resources. + * - `UNSUPPORTED` when any of the description attributes are unsupported or + * if the metadataType is unknown/unsupported. This should also be + * returned if the requested metadata is not defined until a buffer has been + * allocated. + * @return metadata Vector of bytes representing the value associated with + * the MetadataType value. + */ + getFromBufferDescriptorInfo(BufferDescriptorInfo description, + MetadataType metadataType) + generates (Error error, + vec<uint8_t> metadata); + + struct MetadataTypeDescription { + MetadataType metadataType; + /** + * description should contain a string representation of the MetadataType. + * + * For example: "MyExampleMetadataType is a 64-bit timestamp in nanoseconds + * that indicates when a buffer is decoded. It is set by the media HAL after + * a buffer is decoded. It is used by the display HAL for hardware + * synchronization". + * + * This field is required for any non-StandardMetadataTypes. + */ + string description; + /** + * isGettable represents if the MetadataType can be get. + */ + bool isGettable; + /** + * isSettable represents if the MetadataType can be set. + */ + bool isSettable; + }; + + /** + * Lists all the MetadataTypes supported by IMapper as well as a description + * of each supported MetadataType. For StandardMetadataTypes, the description + * string can be left empty. + * + * @return error Error status of the call, which may be + * - `NONE` upon success. + * - `NO_RESOURCES` if the get cannot be fullfilled due to unavailability of + * resources. + * @return descriptions Vector of MetadataTypeDescriptions that represent the + * MetadataTypes supported by the device. + */ + listSupportedMetadataTypes() + generates (Error error, vec<MetadataTypeDescription> descriptions); +}; + diff --git a/graphics/mapper/4.0/types.hal b/graphics/mapper/4.0/types.hal new file mode 100644 index 0000000000..00b660743a --- /dev/null +++ b/graphics/mapper/4.0/types.hal @@ -0,0 +1,55 @@ +/* + * 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. + */ + +package android.hardware.graphics.mapper@4.0; + +/** + * Error values that may be returned by a method of IAllocator or IMapper. + */ +enum Error : int32_t { + /** + * No error. + */ + NONE = 0, + /** + * Invalid BufferDescriptor. + */ + BAD_DESCRIPTOR = 1, + /** + * Invalid buffer handle. + */ + BAD_BUFFER = 2, + /** + * Invalid HardwareBufferDescription. + */ + BAD_VALUE = 3, + /** + * Resource unavailable. + */ + NO_RESOURCES = 5, + /** + * Permanent failure. + */ + UNSUPPORTED = 7, +}; + +/** + * A buffer descriptor is an implementation-defined opaque data returned by + * createDescriptor(). It describes the properties of a buffer and is consumed + * by the allocator. + */ +typedef vec<uint8_t> BufferDescriptor; + diff --git a/graphics/mapper/4.0/utils/OWNERS b/graphics/mapper/4.0/utils/OWNERS new file mode 100644 index 0000000000..96f6d51573 --- /dev/null +++ b/graphics/mapper/4.0/utils/OWNERS @@ -0,0 +1,3 @@ +# Graphics team +marissaw@google.com +stoza@google.com diff --git a/graphics/mapper/4.0/utils/vts/Android.bp b/graphics/mapper/4.0/utils/vts/Android.bp new file mode 100644 index 0000000000..56ff116a43 --- /dev/null +++ b/graphics/mapper/4.0/utils/vts/Android.bp @@ -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. +// + +cc_library_static { + name: "android.hardware.graphics.mapper@4.0-vts", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["MapperVts.cpp"], + cflags: [ + "-O0", + "-g", + ], + static_libs: [ + "android.hardware.graphics.allocator@4.0", + "android.hardware.graphics.mapper@4.0", + ], + shared_libs: [ + "libgralloctypes", + ], + export_static_lib_headers: [ + "android.hardware.graphics.allocator@4.0", + "android.hardware.graphics.mapper@4.0", + ], + export_include_dirs: ["include"], +} diff --git a/graphics/mapper/4.0/utils/vts/MapperVts.cpp b/graphics/mapper/4.0/utils/vts/MapperVts.cpp new file mode 100644 index 0000000000..8073e6924c --- /dev/null +++ b/graphics/mapper/4.0/utils/vts/MapperVts.cpp @@ -0,0 +1,318 @@ +/* + * 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. + */ + +#include <gralloctypes/Gralloc4.h> +#include <mapper-vts/4.0/MapperVts.h> + +#include <VtsHalHidlTargetTestBase.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace mapper { +namespace V4_0 { +namespace vts { + +Gralloc::Gralloc(const std::string& allocatorServiceName, const std::string& mapperServiceName, + bool errOnFailure) { + if (errOnFailure) { + init(allocatorServiceName, mapperServiceName); + } else { + initNoErr(allocatorServiceName, mapperServiceName); + } +} + +void Gralloc::init(const std::string& allocatorServiceName, const std::string& mapperServiceName) { + mAllocator = ::testing::VtsHalHidlTargetTestBase::getService<IAllocator>(allocatorServiceName); + ASSERT_NE(nullptr, mAllocator.get()) << "failed to get allocator service"; + + mMapper = ::testing::VtsHalHidlTargetTestBase::getService<IMapper>(mapperServiceName); + ASSERT_NE(nullptr, mMapper.get()) << "failed to get mapper service"; + ASSERT_FALSE(mMapper->isRemote()) << "mapper is not in passthrough mode"; +} + +void Gralloc::initNoErr(const std::string& allocatorServiceName, + const std::string& mapperServiceName) { + mAllocator = ::testing::VtsHalHidlTargetTestBase::getService<IAllocator>(allocatorServiceName); + + mMapper = ::testing::VtsHalHidlTargetTestBase::getService<IMapper>(mapperServiceName); + if (mMapper.get()) { + ASSERT_FALSE(mMapper->isRemote()) << "mapper is not in passthrough mode"; + } +} + +Gralloc::~Gralloc() { + for (auto bufferHandle : mClonedBuffers) { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + native_handle_close(buffer); + native_handle_delete(buffer); + } + mClonedBuffers.clear(); + + for (auto bufferHandle : mImportedBuffers) { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + EXPECT_EQ(Error::NONE, mMapper->freeBuffer(buffer)) << "failed to free buffer " << buffer; + } + mImportedBuffers.clear(); +} + +sp<IAllocator> Gralloc::getAllocator() const { + return mAllocator; +} + +std::string Gralloc::dumpDebugInfo() { + std::string debugInfo; + mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); }); + + return debugInfo; +} + +const native_handle_t* Gralloc::cloneBuffer(const hidl_handle& rawHandle) { + const native_handle_t* bufferHandle = native_handle_clone(rawHandle.getNativeHandle()); + EXPECT_NE(nullptr, bufferHandle); + + if (bufferHandle) { + mClonedBuffers.insert(bufferHandle); + } + + return bufferHandle; +} + +std::vector<const native_handle_t*> Gralloc::allocate(const BufferDescriptor& descriptor, + uint32_t count, bool import, + bool allowFailure, uint32_t* outStride) { + std::vector<const native_handle_t*> bufferHandles; + bufferHandles.reserve(count); + mAllocator->allocate( + descriptor, count, + [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers"; + ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array"; + + for (uint32_t i = 0; i < count; i++) { + const native_handle_t* bufferHandle = nullptr; + if (import) { + if (allowFailure) { + bufferHandle = importBuffer(tmpBuffers[i]); + } else { + ASSERT_NO_FATAL_FAILURE(bufferHandle = importBuffer(tmpBuffers[i])); + } + } else { + if (allowFailure) { + bufferHandle = cloneBuffer(tmpBuffers[i]); + } else { + ASSERT_NO_FATAL_FAILURE(bufferHandle = cloneBuffer(tmpBuffers[i])); + } + } + if (bufferHandle) { + bufferHandles.push_back(bufferHandle); + } + } + + if (outStride) { + *outStride = tmpStride; + } + }); + + if (::testing::Test::HasFatalFailure()) { + bufferHandles.clear(); + } + + return bufferHandles; +} + +const native_handle_t* Gralloc::allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, + bool import, bool allowFailure, uint32_t* outStride) { + BufferDescriptor descriptor = createDescriptor(descriptorInfo); + if (::testing::Test::HasFatalFailure()) { + return nullptr; + } + + auto buffers = allocate(descriptor, 1, import, allowFailure, outStride); + if (::testing::Test::HasFatalFailure()) { + return nullptr; + } + + if (buffers.size() != 1) { + return nullptr; + } + return buffers[0]; +} + +sp<IMapper> Gralloc::getMapper() const { + return mMapper; +} + +BufferDescriptor Gralloc::createDescriptor(const IMapper::BufferDescriptorInfo& descriptorInfo) { + BufferDescriptor descriptor; + mMapper->createDescriptor(descriptorInfo, [&](const auto& tmpError, const auto& tmpDescriptor) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to create descriptor"; + descriptor = tmpDescriptor; + }); + + return descriptor; +} + +const native_handle_t* Gralloc::importBuffer(const hidl_handle& rawHandle) { + const native_handle_t* bufferHandle = nullptr; + mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) { + ASSERT_EQ(Error::NONE, tmpError) + << "failed to import buffer %p" << rawHandle.getNativeHandle(); + bufferHandle = static_cast<const native_handle_t*>(tmpBuffer); + }); + + if (bufferHandle) { + mImportedBuffers.insert(bufferHandle); + } + + return bufferHandle; +} + +void Gralloc::freeBuffer(const native_handle_t* bufferHandle) { + if (bufferHandle == nullptr) { + return; + } + + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + if (mImportedBuffers.erase(bufferHandle)) { + Error error = mMapper->freeBuffer(buffer); + ASSERT_EQ(Error::NONE, error) << "failed to free buffer " << buffer; + } else { + mClonedBuffers.erase(bufferHandle); + native_handle_close(buffer); + native_handle_delete(buffer); + } +} + +void* Gralloc::lock(const native_handle_t* bufferHandle, uint64_t cpuUsage, + const IMapper::Rect& accessRegion, int acquireFence) { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + hidl_handle acquireFenceHandle; + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + void* data = nullptr; + mMapper->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle, + [&](const auto& tmpError, const auto& tmpData) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to lock buffer " << buffer; + data = tmpData; + }); + + if (acquireFence >= 0) { + close(acquireFence); + } + + return data; +} + +int Gralloc::unlock(const native_handle_t* bufferHandle) { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + int releaseFence = -1; + mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to unlock buffer " << buffer; + + auto fenceHandle = tmpReleaseFence.getNativeHandle(); + if (fenceHandle) { + ASSERT_EQ(0, fenceHandle->numInts) << "invalid fence handle " << fenceHandle; + if (fenceHandle->numFds == 1) { + releaseFence = dup(fenceHandle->data[0]); + ASSERT_LT(0, releaseFence) << "failed to dup fence fd"; + } else { + ASSERT_EQ(0, fenceHandle->numFds) << " invalid fence handle " << fenceHandle; + } + } + }); + + return releaseFence; +} + +bool Gralloc::validateBufferSize(const native_handle_t* bufferHandle, + const IMapper::BufferDescriptorInfo& descriptorInfo, + uint32_t stride) { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + Error error = mMapper->validateBufferSize(buffer, descriptorInfo, stride); + return error == Error::NONE; +} + +void Gralloc::getTransportSize(const native_handle_t* bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + *outNumFds = 0; + *outNumInts = 0; + mMapper->getTransportSize(buffer, [&](const auto& tmpError, const auto& tmpNumFds, + const auto& tmpNumInts) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to get transport size"; + ASSERT_GE(bufferHandle->numFds, int(tmpNumFds)) << "invalid numFds " << tmpNumFds; + ASSERT_GE(bufferHandle->numInts, int(tmpNumInts)) << "invalid numInts " << tmpNumInts; + + *outNumFds = tmpNumFds; + *outNumInts = tmpNumInts; + }); +} + +bool Gralloc::isSupported(const IMapper::BufferDescriptorInfo& descriptorInfo) { + bool supported = false; + mMapper->isSupported(descriptorInfo, [&](const auto& tmpError, const auto& tmpSupported) { + ASSERT_EQ(Error::NONE, tmpError) << "failed to check is supported"; + supported = tmpSupported; + }); + return supported; +} + +Error Gralloc::get(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType, + hidl_vec<uint8_t>* outVec) { + Error err; + mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType, + [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) { + err = tmpError; + *outVec = tmpVec; + }); + return err; +} + +Error Gralloc::set(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType, + const hidl_vec<uint8_t>& vec) { + return mMapper->set(const_cast<native_handle_t*>(bufferHandle), metadataType, vec); +} + +Error Gralloc::getFromBufferDescriptorInfo(const IMapper::BufferDescriptorInfo& descriptorInfo, + const IMapper::MetadataType& metadataType, + hidl_vec<uint8_t>* outVec) { + Error err; + mMapper->getFromBufferDescriptorInfo( + descriptorInfo, metadataType, + [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) { + err = tmpError; + *outVec = tmpVec; + }); + return err; +} + +} // namespace vts +} // namespace V4_0 +} // namespace mapper +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h b/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h new file mode 100644 index 0000000000..6251e6649c --- /dev/null +++ b/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <unordered_set> +#include <vector> + +#include <android/hardware/graphics/allocator/4.0/IAllocator.h> +#include <android/hardware/graphics/mapper/4.0/IMapper.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace mapper { +namespace V4_0 { +namespace vts { + +using android::hardware::graphics::allocator::V4_0::IAllocator; + +// A wrapper to IAllocator and IMapper. +class Gralloc { + public: + Gralloc(const std::string& allocatorServiceName = "default", + const std::string& mapperServiceName = "default", bool errOnFailure = true); + ~Gralloc(); + + // IAllocator methods + + sp<IAllocator> getAllocator() const; + + std::string dumpDebugInfo(); + + // When import is false, this simply calls IAllocator::allocate. When import + // is true, the returned buffers are also imported into the mapper. + // + // Either case, the returned buffers must be freed with freeBuffer. + std::vector<const native_handle_t*> allocate(const BufferDescriptor& descriptor, uint32_t count, + bool import = true, bool allowFailure = false, + uint32_t* outStride = nullptr); + const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, + bool import = true, bool allowFailure = false, + uint32_t* outStride = nullptr); + + // IMapper methods + + sp<IMapper> getMapper() const; + + BufferDescriptor createDescriptor(const IMapper::BufferDescriptorInfo& descriptorInfo); + + const native_handle_t* importBuffer(const hidl_handle& rawHandle); + void freeBuffer(const native_handle_t* bufferHandle); + + // We use fd instead of hidl_handle in these functions to pass fences + // in and out of the mapper. The ownership of the fd is always transferred + // with each of these functions. + void* lock(const native_handle_t* bufferHandle, uint64_t cpuUsage, + const IMapper::Rect& accessRegion, int acquireFence); + int unlock(const native_handle_t* bufferHandle); + + bool validateBufferSize(const native_handle_t* bufferHandle, + const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t stride); + void getTransportSize(const native_handle_t* bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts); + + bool isSupported(const IMapper::BufferDescriptorInfo& descriptorInfo); + + Error get(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType, + hidl_vec<uint8_t>* outVec); + + Error set(const native_handle_t* bufferHandle, const IMapper::MetadataType& metadataType, + const hidl_vec<uint8_t>& vec); + + Error getFromBufferDescriptorInfo(const IMapper::BufferDescriptorInfo& descriptorInfo, + const IMapper::MetadataType& metadataType, + hidl_vec<uint8_t>* outVec); + + private: + void init(const std::string& allocatorServiceName, const std::string& mapperServiceName); + + // initialize without checking for failure to get service + void initNoErr(const std::string& allocatorServiceName, const std::string& mapperServiceName); + const native_handle_t* cloneBuffer(const hidl_handle& rawHandle); + + sp<IAllocator> mAllocator; + sp<IMapper> mMapper; + + // Keep track of all cloned and imported handles. When a test fails with + // ASSERT_*, the destructor will free the handles for the test. + std::unordered_set<const native_handle_t*> mClonedBuffers; + std::unordered_set<const native_handle_t*> mImportedBuffers; +}; + +} // namespace vts +} // namespace V4_0 +} // namespace mapper +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/graphics/mapper/4.0/vts/OWNERS b/graphics/mapper/4.0/vts/OWNERS new file mode 100644 index 0000000000..96f6d51573 --- /dev/null +++ b/graphics/mapper/4.0/vts/OWNERS @@ -0,0 +1,3 @@ +# Graphics team +marissaw@google.com +stoza@google.com diff --git a/graphics/mapper/4.0/vts/functional/Android.bp b/graphics/mapper/4.0/vts/functional/Android.bp new file mode 100644 index 0000000000..5a7548a4f8 --- /dev/null +++ b/graphics/mapper/4.0/vts/functional/Android.bp @@ -0,0 +1,34 @@ +// +// 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. +// + +cc_test { + name: "VtsHalGraphicsMapperV4_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalGraphicsMapperV4_0TargetTest.cpp"], + static_libs: [ + "android.hardware.graphics.mapper@4.0-vts", + ], + shared_libs: [ + "android.hardware.graphics.allocator@4.0", + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hardware.graphics.mapper@4.0", + "libgralloctypes", + "vintf-graphics-common-ndk_platform", + ], + test_suites: ["general-tests"], +} diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp new file mode 100644 index 0000000000..dd748db12b --- /dev/null +++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp @@ -0,0 +1,1608 @@ +/* + * 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 "VtsHalGraphicsMapperV4_0TargetTest" + +#include <chrono> +#include <thread> +#include <vector> + +//#include <aidl/android/hardware/graphics/common/BlendMode.h> +//#include <aidl/android/hardware/graphics/common/Compression.h> + +#include <VtsHalHidlTargetTestBase.h> +#include <android-base/logging.h> +#include <gralloctypes/Gralloc4.h> +#include <mapper-vts/4.0/MapperVts.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace mapper { +namespace V4_0 { +namespace vts { +namespace { + +using android::hardware::graphics::common::V1_2::BufferUsage; +using android::hardware::graphics::common::V1_2::PixelFormat; +using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType; +using aidl::android::hardware::graphics::common::BlendMode; +using aidl::android::hardware::graphics::common::Dataspace; +using aidl::android::hardware::graphics::common::ExtendableType; +using aidl::android::hardware::graphics::common::PlaneLayout; +using aidl::android::hardware::graphics::common::PlaneLayoutComponent; +using aidl::android::hardware::graphics::common::StandardMetadataType; + +using DecodeFunction = std::function<void(const IMapper::BufferDescriptorInfo& descriptorInfo, + const hidl_vec<uint8_t>& vec)>; + +// Test environment for graphics.mapper. +class GraphicsMapperHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { + public: + // get the test environment singleton + static GraphicsMapperHidlEnvironment* Instance() { + static GraphicsMapperHidlEnvironment* instance = new GraphicsMapperHidlEnvironment; + return instance; + } + + virtual void registerTestServices() override { + registerTestService<IAllocator>(); + registerTestService<IMapper>(); + } +}; + +class GraphicsMapperHidlTest : public ::testing::VtsHalHidlTargetTestBase { + protected: + void SetUp() override { + ASSERT_NO_FATAL_FAILURE( + mGralloc = std::make_unique<Gralloc>( + GraphicsMapperHidlEnvironment::Instance()->getServiceName<IAllocator>(), + GraphicsMapperHidlEnvironment::Instance()->getServiceName<IMapper>())); + ASSERT_NE(nullptr, mGralloc->getAllocator().get()); + ASSERT_NE(nullptr, mGralloc->getMapper().get()); + + mDummyDescriptorInfo.name = "dummy"; + mDummyDescriptorInfo.width = 64; + mDummyDescriptorInfo.height = 64; + mDummyDescriptorInfo.layerCount = 1; + mDummyDescriptorInfo.format = PixelFormat::RGBA_8888; + mDummyDescriptorInfo.usage = + static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN); + } + + void TearDown() override {} + + void testGet(const IMapper::BufferDescriptorInfo& descriptorInfo, + const MetadataType& metadataType, DecodeFunction decode) { + const native_handle_t* bufferHandle = nullptr; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(descriptorInfo, true)); + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, metadataType, &vec)); + + ASSERT_NO_FATAL_FAILURE(decode(descriptorInfo, vec)); + } + + void testSet(const IMapper::BufferDescriptorInfo& descriptorInfo, + const MetadataType& metadataType, const hidl_vec<uint8_t>& metadata, + DecodeFunction decode) { + const native_handle_t* bufferHandle = nullptr; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(descriptorInfo, true)); + + Error err = mGralloc->set(bufferHandle, metadataType, metadata); + if (err == Error::UNSUPPORTED) { + GTEST_SUCCEED() << "setting this metadata is unsupported"; + } + ASSERT_EQ(err, Error::NONE); + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, metadataType, &vec)); + + ASSERT_NO_FATAL_FAILURE(decode(descriptorInfo, vec)); + } + + void verifyDummyDescriptorInfoPlaneLayouts(const std::vector<PlaneLayout>& planeLayouts) { + ASSERT_EQ(1, planeLayouts.size()); + + const auto& planeLayout = planeLayouts.front(); + + ASSERT_EQ(4, planeLayout.components.size()); + + int64_t offsetInBitsR = -1; + int64_t offsetInBitsG = -1; + int64_t offsetInBitsB = -1; + int64_t offsetInBitsA = -1; + + for (const auto& component : planeLayout.components) { + EXPECT_EQ(GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, component.type.name); + EXPECT_EQ(8, component.sizeInBits); + if (component.type.value == gralloc4::PlaneLayoutComponentType_R.value) { + offsetInBitsR = component.offsetInBits; + } + if (component.type.value == gralloc4::PlaneLayoutComponentType_G.value) { + offsetInBitsG = component.offsetInBits; + } + if (component.type.value == gralloc4::PlaneLayoutComponentType_B.value) { + offsetInBitsB = component.offsetInBits; + } + if (component.type.value == gralloc4::PlaneLayoutComponentType_A.value) { + offsetInBitsA = component.offsetInBits; + } + } + + EXPECT_EQ(0, offsetInBitsR); + EXPECT_EQ(8, offsetInBitsG); + EXPECT_EQ(16, offsetInBitsB); + EXPECT_EQ(24, offsetInBitsA); + + EXPECT_EQ(0, planeLayout.offsetInBytes); + EXPECT_EQ(8, planeLayout.sampleIncrementInBits); + // Skip testing stride because any stride is valid + EXPECT_EQ(mDummyDescriptorInfo.width, planeLayout.widthInSamples); + EXPECT_EQ(mDummyDescriptorInfo.height, planeLayout.heightInSamples); + EXPECT_LE(planeLayout.widthInSamples * planeLayout.heightInSamples * 4, + planeLayout.totalSizeInBytes); + EXPECT_EQ(1, planeLayout.horizontalSubsampling); + EXPECT_EQ(1, planeLayout.verticalSubsampling); + + EXPECT_EQ(0, planeLayout.crop.left); + EXPECT_EQ(0, planeLayout.crop.top); + EXPECT_EQ(planeLayout.widthInSamples, planeLayout.crop.right); + EXPECT_EQ(planeLayout.heightInSamples, planeLayout.crop.bottom); + } + + std::unique_ptr<Gralloc> mGralloc; + IMapper::BufferDescriptorInfo mDummyDescriptorInfo{}; + static const std::set<StandardMetadataType> sRequiredMetadataTypes; +}; + +const std::set<StandardMetadataType> GraphicsMapperHidlTest::sRequiredMetadataTypes{ + StandardMetadataType::BUFFER_ID, + StandardMetadataType::NAME, + StandardMetadataType::WIDTH, + StandardMetadataType::HEIGHT, + StandardMetadataType::LAYER_COUNT, + StandardMetadataType::PIXEL_FORMAT_REQUESTED, + StandardMetadataType::PIXEL_FORMAT_FOURCC, + StandardMetadataType::PIXEL_FORMAT_MODIFIER, + StandardMetadataType::USAGE, + StandardMetadataType::ALLOCATION_SIZE, + StandardMetadataType::PROTECTED_CONTENT, + StandardMetadataType::COMPRESSION, + StandardMetadataType::INTERLACED, + StandardMetadataType::CHROMA_SITING, + StandardMetadataType::PLANE_LAYOUTS, + StandardMetadataType::DATASPACE, + StandardMetadataType::BLEND_MODE, +}; + +/** + * Test IAllocator::dumpDebugInfo by calling it. + */ +TEST_F(GraphicsMapperHidlTest, AllocatorDumpDebugInfo) { + mGralloc->dumpDebugInfo(); +} + +/** + * Test IAllocator::allocate with valid buffer descriptors. + */ +TEST_F(GraphicsMapperHidlTest, AllocatorAllocate) { + BufferDescriptor descriptor; + ASSERT_NO_FATAL_FAILURE(descriptor = mGralloc->createDescriptor(mDummyDescriptorInfo)); + + for (uint32_t count = 0; count < 5; count++) { + std::vector<const native_handle_t*> bufferHandles; + uint32_t stride; + ASSERT_NO_FATAL_FAILURE( + bufferHandles = mGralloc->allocate(descriptor, count, false, false, &stride)); + + if (count >= 1) { + EXPECT_LE(mDummyDescriptorInfo.width, stride) << "invalid buffer stride"; + } + + for (auto bufferHandle : bufferHandles) { + mGralloc->freeBuffer(bufferHandle); + } + } +} + +/** + * Test IAllocator::allocate with invalid buffer descriptors. + */ +TEST_F(GraphicsMapperHidlTest, AllocatorAllocateNegative) { + // this assumes any valid descriptor is non-empty + BufferDescriptor descriptor; + mGralloc->getAllocator()->allocate(descriptor, 1, + [&](const auto& tmpError, const auto&, const auto&) { + EXPECT_EQ(Error::BAD_DESCRIPTOR, tmpError); + }); +} + +/** + * Test IAllocator::allocate does not leak. + */ +TEST_F(GraphicsMapperHidlTest, AllocatorAllocateNoLeak) { + auto info = mDummyDescriptorInfo; + info.width = 1024; + info.height = 1024; + + for (int i = 0; i < 2048; i++) { + auto bufferHandle = mGralloc->allocate(info, false); + mGralloc->freeBuffer(bufferHandle); + } +} + +/** + * Test that IAllocator::allocate is thread-safe. + */ +TEST_F(GraphicsMapperHidlTest, AllocatorAllocateThreaded) { + BufferDescriptor descriptor; + ASSERT_NO_FATAL_FAILURE(descriptor = mGralloc->createDescriptor(mDummyDescriptorInfo)); + + std::atomic<bool> timeUp(false); + std::atomic<uint64_t> allocationCount(0); + auto threadLoop = [&]() { + while (!timeUp) { + mGralloc->getAllocator()->allocate( + descriptor, 1, + [&](const auto&, const auto&, const auto&) { allocationCount++; }); + } + }; + + std::vector<std::thread> threads; + for (int i = 0; i < 8; i++) { + threads.push_back(std::thread(threadLoop)); + } + + std::this_thread::sleep_for(std::chrono::seconds(3)); + timeUp = true; + LOG(VERBOSE) << "Made " << allocationCount << " threaded allocations"; + + for (auto& thread : threads) { + thread.join(); + } +} + +/** + * Test IMapper::createDescriptor with valid descriptor info. + */ +TEST_F(GraphicsMapperHidlTest, CreateDescriptorBasic) { + ASSERT_NO_FATAL_FAILURE(mGralloc->createDescriptor(mDummyDescriptorInfo)); +} + +/** + * Test IMapper::createDescriptor with invalid descriptor info. + */ +TEST_F(GraphicsMapperHidlTest, CreateDescriptorNegative) { + auto info = mDummyDescriptorInfo; + info.width = 0; + mGralloc->getMapper()->createDescriptor(info, [&](const auto& tmpError, const auto&) { + EXPECT_EQ(Error::BAD_VALUE, tmpError) << "createDescriptor did not fail with BAD_VALUE"; + }); +} + +/** + * Test IMapper::importBuffer and IMapper::freeBuffer with allocated buffers. + */ +TEST_F(GraphicsMapperHidlTest, ImportFreeBufferBasic) { + const native_handle_t* bufferHandle; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true)); + ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(bufferHandle)); +} + +/** + * Test IMapper::importBuffer and IMapper::freeBuffer with cloned buffers. + */ +TEST_F(GraphicsMapperHidlTest, ImportFreeBufferClone) { + const native_handle_t* clonedBufferHandle; + ASSERT_NO_FATAL_FAILURE(clonedBufferHandle = mGralloc->allocate(mDummyDescriptorInfo, false)); + + // A cloned handle is a raw handle. Check that we can import it multiple + // times. + const native_handle_t* importedBufferHandles[2]; + ASSERT_NO_FATAL_FAILURE(importedBufferHandles[0] = mGralloc->importBuffer(clonedBufferHandle)); + ASSERT_NO_FATAL_FAILURE(importedBufferHandles[1] = mGralloc->importBuffer(clonedBufferHandle)); + ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(importedBufferHandles[0])); + ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(importedBufferHandles[1])); + + ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(clonedBufferHandle)); +} + +/** + * Test IMapper::importBuffer and IMapper::freeBuffer cross mapper instances. + */ +TEST_F(GraphicsMapperHidlTest, ImportFreeBufferSingleton) { + const native_handle_t* rawHandle; + ASSERT_NO_FATAL_FAILURE(rawHandle = mGralloc->allocate(mDummyDescriptorInfo, false)); + + native_handle_t* importedHandle = nullptr; + mGralloc->getMapper()->importBuffer(rawHandle, [&](const auto& tmpError, const auto& buffer) { + ASSERT_EQ(Error::NONE, tmpError); + importedHandle = static_cast<native_handle_t*>(buffer); + }); + + // free the imported handle with another mapper + std::unique_ptr<Gralloc> anotherGralloc; + ASSERT_NO_FATAL_FAILURE( + anotherGralloc = std::make_unique<Gralloc>( + GraphicsMapperHidlEnvironment::Instance()->getServiceName<IAllocator>(), + GraphicsMapperHidlEnvironment::Instance()->getServiceName<IMapper>())); + Error error = mGralloc->getMapper()->freeBuffer(importedHandle); + ASSERT_EQ(Error::NONE, error); + + ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(rawHandle)); +} + +/** + * Test IMapper::importBuffer and IMapper::freeBuffer do not leak. + */ +TEST_F(GraphicsMapperHidlTest, ImportFreeBufferNoLeak) { + auto info = mDummyDescriptorInfo; + info.width = 1024; + info.height = 1024; + + for (int i = 0; i < 2048; i++) { + auto bufferHandle = mGralloc->allocate(info, true); + mGralloc->freeBuffer(bufferHandle); + } +} + +/** + * Test IMapper::importBuffer with invalid buffers. + */ +TEST_F(GraphicsMapperHidlTest, ImportBufferNegative) { + native_handle_t* invalidHandle = nullptr; + mGralloc->getMapper()->importBuffer(invalidHandle, [&](const auto& tmpError, const auto&) { + EXPECT_EQ(Error::BAD_BUFFER, tmpError) + << "importBuffer with nullptr did not fail with BAD_BUFFER"; + }); + + invalidHandle = native_handle_create(0, 0); + mGralloc->getMapper()->importBuffer(invalidHandle, [&](const auto& tmpError, const auto&) { + EXPECT_EQ(Error::BAD_BUFFER, tmpError) + << "importBuffer with invalid handle did not fail with BAD_BUFFER"; + }); + native_handle_delete(invalidHandle); +} + +/** + * Test IMapper::freeBuffer with invalid buffers. + */ +TEST_F(GraphicsMapperHidlTest, FreeBufferNegative) { + native_handle_t* invalidHandle = nullptr; + Error error = mGralloc->getMapper()->freeBuffer(invalidHandle); + EXPECT_EQ(Error::BAD_BUFFER, error) << "freeBuffer with nullptr did not fail with BAD_BUFFER"; + + invalidHandle = native_handle_create(0, 0); + error = mGralloc->getMapper()->freeBuffer(invalidHandle); + EXPECT_EQ(Error::BAD_BUFFER, error) + << "freeBuffer with invalid handle did not fail with BAD_BUFFER"; + native_handle_delete(invalidHandle); + + const native_handle_t* clonedBufferHandle; + ASSERT_NO_FATAL_FAILURE(clonedBufferHandle = mGralloc->allocate(mDummyDescriptorInfo, false)); + error = mGralloc->getMapper()->freeBuffer(invalidHandle); + EXPECT_EQ(Error::BAD_BUFFER, error) + << "freeBuffer with un-imported handle did not fail with BAD_BUFFER"; + + mGralloc->freeBuffer(clonedBufferHandle); +} + +/** + * Test IMapper::lock and IMapper::unlock. + */ +TEST_F(GraphicsMapperHidlTest, LockUnlockBasic) { + const auto& info = mDummyDescriptorInfo; + + const native_handle_t* bufferHandle; + uint32_t stride; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, false, &stride)); + + // lock buffer for writing + const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width), + static_cast<int32_t>(info.height)}; + int fence = -1; + uint8_t* data; + ASSERT_NO_FATAL_FAILURE( + data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence))); + + // RGBA_8888 + size_t strideInBytes = stride * 4; + size_t writeInBytes = info.width * 4; + + for (uint32_t y = 0; y < info.height; y++) { + memset(data, y, writeInBytes); + data += strideInBytes; + } + + ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle)); + + // lock again for reading + ASSERT_NO_FATAL_FAILURE( + data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence))); + for (uint32_t y = 0; y < info.height; y++) { + for (size_t i = 0; i < writeInBytes; i++) { + EXPECT_EQ(static_cast<uint8_t>(y), data[i]); + } + data += strideInBytes; + } + + ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle)); + if (fence >= 0) { + close(fence); + } +} + +/** + * Test IMapper::unlock with bad access region + */ +TEST_F(GraphicsMapperHidlTest, LockBadAccessRegion) { + const auto& info = mDummyDescriptorInfo; + + const native_handle_t* bufferHandle; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true)); + + const IMapper::Rect accessRegion{0, 0, static_cast<int32_t>(info.width * 2), + static_cast<int32_t>(info.height * 2)}; + int acquireFence = -1; + + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + hidl_handle acquireFenceHandle; + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + auto buffer = const_cast<native_handle_t*>(bufferHandle); + mGralloc->getMapper()->lock(buffer, info.usage, accessRegion, acquireFenceHandle, + [&](const auto& tmpError, const auto& /*tmpData*/) { + EXPECT_EQ(Error::BAD_VALUE, tmpError) + << "locking with a bad access region should fail"; + }); + + if (::testing::Test::HasFailure()) { + if (acquireFence >= 0) { + close(acquireFence); + } + + int releaseFence = -1; + ASSERT_NO_FATAL_FAILURE(releaseFence = mGralloc->unlock(bufferHandle)); + + if (releaseFence >= 0) { + close(releaseFence); + } + } +} + +/** + * Test IMapper::unlock with invalid buffers. + */ +TEST_F(GraphicsMapperHidlTest, UnlockNegative) { + native_handle_t* invalidHandle = nullptr; + mGralloc->getMapper()->unlock(invalidHandle, [&](const auto& tmpError, const auto&) { + EXPECT_EQ(Error::BAD_BUFFER, tmpError) + << "unlock with nullptr did not fail with BAD_BUFFER"; + }); + + invalidHandle = native_handle_create(0, 0); + mGralloc->getMapper()->unlock(invalidHandle, [&](const auto& tmpError, const auto&) { + EXPECT_EQ(Error::BAD_BUFFER, tmpError) + << "unlock with invalid handle did not fail with BAD_BUFFER"; + }); + native_handle_delete(invalidHandle); + + ASSERT_NO_FATAL_FAILURE(invalidHandle = const_cast<native_handle_t*>( + mGralloc->allocate(mDummyDescriptorInfo, false))); + mGralloc->getMapper()->unlock(invalidHandle, [&](const auto& tmpError, const auto&) { + EXPECT_EQ(Error::BAD_BUFFER, tmpError) + << "unlock with un-imported handle did not fail with BAD_BUFFER"; + }); + mGralloc->freeBuffer(invalidHandle); + +// disabled as it fails on many existing drivers +#if 0 + ASSERT_NO_FATAL_FAILURE(invalidHandle = const_cast<native_handle_t*>( + mGralloc->allocate(mDummyDescriptorInfo, true))); + mGralloc->getMapper()->unlock( + invalidHandle, [&](const auto& tmpError, const auto&) { + EXPECT_EQ(Error::BAD_BUFFER, tmpError) + << "unlock with unlocked handle did not fail with BAD_BUFFER"; + }); + mGralloc->freeBuffer(invalidHandle); +#endif +} + +/** + * Test IMapper::isSupported with required format RGBA_8888 + */ +TEST_F(GraphicsMapperHidlTest, IsSupportedRGBA8888) { + const auto& info = mDummyDescriptorInfo; + bool supported = false; + + ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info)); + ASSERT_TRUE(supported); +} + +/** + * Test IMapper::isSupported with required format YV12 + */ +TEST_F(GraphicsMapperHidlTest, IsSupportedYV12) { + auto info = mDummyDescriptorInfo; + info.format = PixelFormat::YV12; + bool supported = false; + + ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info)); + ASSERT_TRUE(supported); +} + +/** + * Test IMapper::isSupported with optional format Y16 + */ +TEST_F(GraphicsMapperHidlTest, IsSupportedY16) { + auto info = mDummyDescriptorInfo; + info.format = PixelFormat::Y16; + bool supported = false; + + ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info)); +} + +/** + * Test IMapper::get(BufferId) + */ +TEST_F(GraphicsMapperHidlTest, GetBufferId) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_BufferId, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint64_t bufferId = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeBufferId(vec, &bufferId)); + }); +} + +/** + * Test IMapper::get(Name) + */ +TEST_F(GraphicsMapperHidlTest, GetName) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Name, + [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) { + std::string name; + ASSERT_EQ(NO_ERROR, gralloc4::decodeName(vec, &name)); + EXPECT_EQ(info.name, name); + }); +} + +/** + * Test IMapper::get(Width) + */ +TEST_F(GraphicsMapperHidlTest, GetWidth) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Width, + [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) { + uint64_t width = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeWidth(vec, &width)); + EXPECT_EQ(info.width, width); + }); +} + +/** + * Test IMapper::get(Height) + */ +TEST_F(GraphicsMapperHidlTest, GetHeight) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Height, + [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) { + uint64_t height = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeHeight(vec, &height)); + EXPECT_EQ(info.height, height); + }); +} + +/** + * Test IMapper::get(LayerCount) + */ +TEST_F(GraphicsMapperHidlTest, GetLayerCount) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_LayerCount, + [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) { + uint64_t layerCount = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeLayerCount(vec, &layerCount)); + EXPECT_EQ(info.layerCount, layerCount); + }); +} + +/** + * Test IMapper::get(PixelFormatRequested) + */ +TEST_F(GraphicsMapperHidlTest, GetPixelFormatRequested) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatRequested, + [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) { + PixelFormat pixelFormatRequested = PixelFormat::BLOB; + ASSERT_EQ(NO_ERROR, + gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested)); + EXPECT_EQ(info.format, pixelFormatRequested); + }); +} + +/** + * Test IMapper::get(PixelFormatFourCC) + */ +TEST_F(GraphicsMapperHidlTest, GetPixelFormatFourCC) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatFourCC, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint32_t pixelFormatFourCC = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC)); + }); +} + +/** + * Test IMapper::get(PixelFormatModifier) + */ +TEST_F(GraphicsMapperHidlTest, GetPixelFormatModifier) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatModifier, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint64_t pixelFormatModifier = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier)); + }); +} + +/** + * Test IMapper::get(Usage) + */ +TEST_F(GraphicsMapperHidlTest, GetUsage) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, + [](const IMapper::BufferDescriptorInfo& info, const hidl_vec<uint8_t>& vec) { + uint64_t usage = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &usage)); + EXPECT_EQ(info.usage, usage); + }); +} + +/** + * Test IMapper::get(AllocationSize) + */ +TEST_F(GraphicsMapperHidlTest, GetAllocationSize) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_AllocationSize, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint64_t allocationSize = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize)); + }); +} + +/** + * Test IMapper::get(ProtectedContent) + */ +TEST_F(GraphicsMapperHidlTest, GetProtectedContent) { + auto info = mDummyDescriptorInfo; + info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY; + + const native_handle_t* bufferHandle = nullptr; + bufferHandle = mGralloc->allocate(info, true, true); + if (bufferHandle) { + GTEST_SUCCEED() << "unable to allocate protected content"; + } + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, + mGralloc->get(bufferHandle, gralloc4::MetadataType_ProtectedContent, &vec)); + + uint64_t protectedContent = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent)); + EXPECT_EQ(1, protectedContent); +} + +/** + * Test IMapper::get(Compression) + */ +TEST_F(GraphicsMapperHidlTest, GetCompression) { + auto info = mDummyDescriptorInfo; + info.usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN); + + testGet(info, gralloc4::MetadataType_Compression, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + ExtendableType compression = gralloc4::Compression_DisplayStreamCompression; + ASSERT_EQ(NO_ERROR, gralloc4::decodeCompression(vec, &compression)); + + EXPECT_EQ(gralloc4::Compression_None.name, compression.name); + EXPECT_EQ(gralloc4::Compression_None.value, compression.value); + }); +} + +/** + * Test IMapper::get(Interlaced) + */ +TEST_F(GraphicsMapperHidlTest, GetInterlaced) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Interlaced, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + ExtendableType interlaced = gralloc4::Interlaced_TopBottom; + ASSERT_EQ(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced)); + + EXPECT_EQ(gralloc4::Interlaced_None.name, interlaced.name); + EXPECT_EQ(gralloc4::Interlaced_None.value, interlaced.value); + }); +} + +/** + * Test IMapper::get(ChromaSiting) + */ +TEST_F(GraphicsMapperHidlTest, GetChromaSiting) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_ChromaSiting, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + ExtendableType chromaSiting = gralloc4::ChromaSiting_Unknown; + ASSERT_EQ(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting)); + + EXPECT_EQ(gralloc4::ChromaSiting_None.name, chromaSiting.name); + EXPECT_EQ(gralloc4::ChromaSiting_None.value, chromaSiting.value); + }); +} + +/** + * Test IMapper::get(PlaneLayouts) + */ +TEST_F(GraphicsMapperHidlTest, GetPlaneLayouts) { + const native_handle_t* bufferHandle = nullptr; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true)); + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec)); + + std::vector<PlaneLayout> planeLayouts; + ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts)); + + ASSERT_NO_FATAL_FAILURE(verifyDummyDescriptorInfoPlaneLayouts(planeLayouts)); +} + +/** + * Test IMapper::get(Dataspace) + */ +TEST_F(GraphicsMapperHidlTest, GetDataspace) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_Dataspace, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + Dataspace dataspace = Dataspace::DISPLAY_P3; + ASSERT_EQ(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace)); + EXPECT_EQ(Dataspace::UNKNOWN, dataspace); + }); +} + +/** + * Test IMapper::get(BlendMode) + */ +TEST_F(GraphicsMapperHidlTest, GetBlendMode) { + testGet(mDummyDescriptorInfo, gralloc4::MetadataType_BlendMode, + [](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + BlendMode blendMode = BlendMode::NONE; + ASSERT_EQ(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode)); + EXPECT_EQ(BlendMode::INVALID, blendMode); + }); +} + +/** + * Test IMapper::get(metadata) with a bad buffer + */ +TEST_F(GraphicsMapperHidlTest, GetMetadataBadValue) { + const native_handle_t* bufferHandle = nullptr; + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_BufferId, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Name, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Width, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Height, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_LayerCount, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->get(bufferHandle, gralloc4::MetadataType_Usage, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_AllocationSize, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_ProtectedContent, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_Compression, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_Interlaced, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_ChromaSiting, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_Dataspace, &vec)); + ASSERT_EQ(0, vec.size()); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->get(bufferHandle, gralloc4::MetadataType_BlendMode, &vec)); + ASSERT_EQ(0, vec.size()); +} + +/** + * Test IMapper::get(metadata) for unsupported metadata + */ +TEST_F(GraphicsMapperHidlTest, GetUnsupportedMetadata) { + const native_handle_t* bufferHandle = nullptr; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true)); + + MetadataType metadataTypeFake = {"FAKE", 1}; + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::UNSUPPORTED, mGralloc->get(bufferHandle, metadataTypeFake, &vec)); + ASSERT_EQ(0, vec.size()); +} + +/** + * Test IMapper::get(metadata) for unsupported standard metadata + */ +TEST_F(GraphicsMapperHidlTest, GetUnsupportedStandardMetadata) { + const native_handle_t* bufferHandle = nullptr; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true)); + + MetadataType metadataTypeFake = {GRALLOC4_STANDARD_METADATA_TYPE, 9999}; + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::UNSUPPORTED, mGralloc->get(bufferHandle, metadataTypeFake, &vec)); + ASSERT_EQ(0, vec.size()); +} + +/** + * Test IMapper::set(PixelFormatFourCC) + */ +TEST_F(GraphicsMapperHidlTest, SetPixelFormatFourCC) { + uint32_t pixelFormatFourCC = 0x34324142; // DRM_FORMAT_BGRA8888 + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodePixelFormatFourCC(pixelFormatFourCC, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatFourCC, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint32_t realPixelFormatFourCC = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &realPixelFormatFourCC)); + EXPECT_EQ(pixelFormatFourCC, realPixelFormatFourCC); + }); +} + +/** + * Test IMapper::set(PixelFormatModifier) + */ +TEST_F(GraphicsMapperHidlTest, SetPixelFormatModifier) { + uint64_t pixelFormatModifier = 10; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodePixelFormatModifier(pixelFormatModifier, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatModifier, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint64_t realPixelFormatModifier = 0; + ASSERT_EQ(NO_ERROR, + gralloc4::decodePixelFormatModifier(vec, &realPixelFormatModifier)); + EXPECT_EQ(pixelFormatModifier, realPixelFormatModifier); + }); +} + +/** + * Test IMapper::set(Usage) remove flag + */ +TEST_F(GraphicsMapperHidlTest, SetUsageRemoveBit) { + uint64_t usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN); + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint64_t realUsage = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage)); + EXPECT_EQ(usage, realUsage); + }); +} +/** + * Test IMapper::set(Usage) add flag + */ +TEST_F(GraphicsMapperHidlTest, SetUsageAddBit) { + uint64_t usage = mDummyDescriptorInfo.usage | static_cast<uint64_t>(BufferUsage::GPU_TEXTURE); + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint64_t realUsage = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage)); + EXPECT_EQ(usage, realUsage); + }); +} + +/** + * Test IMapper::set(Usage) to test protected content + */ +TEST_F(GraphicsMapperHidlTest, SetUsageProtected) { + const native_handle_t* bufferHandle = nullptr; + auto info = mDummyDescriptorInfo; + info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY; + + bufferHandle = mGralloc->allocate(info, true, true); + if (bufferHandle) { + GTEST_SUCCEED() << "unable to allocate protected content"; + } + + uint64_t usage = static_cast<uint64_t>(BufferUsage::COMPOSER_OVERLAY); + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec)); + + Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec); + ASSERT_EQ(err, Error::UNSUPPORTED); + vec.resize(0); + + uint64_t realUsage = 0; + ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_Usage, &vec)); + ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage)); + EXPECT_EQ(info.usage, realUsage); +} + +/** + * Test IMapper::set(AllocationSize) + */ +TEST_F(GraphicsMapperHidlTest, SetAllocationSize) { + uint64_t allocationSize = 1000000; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeAllocationSize(allocationSize, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_AllocationSize, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + uint64_t realAllocationSize = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeAllocationSize(vec, &realAllocationSize)); + EXPECT_EQ(allocationSize, realAllocationSize); + }); +} + +/** + * Test IMapper::set(ProtectedContent) + */ +TEST_F(GraphicsMapperHidlTest, SetProtectedContent) { + const native_handle_t* bufferHandle = nullptr; + auto info = mDummyDescriptorInfo; + info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY; + + bufferHandle = mGralloc->allocate(info, true, true); + if (bufferHandle) { + GTEST_SUCCEED() << "unable to allocate protected content"; + } + + uint64_t protectedContent = 0; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeProtectedContent(protectedContent, &vec)); + + Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_ProtectedContent, vec); + ASSERT_EQ(err, Error::UNSUPPORTED); + vec.resize(0); + + uint64_t realProtectedContent = 0; + ASSERT_EQ(Error::NONE, + mGralloc->get(bufferHandle, gralloc4::MetadataType_ProtectedContent, &vec)); + ASSERT_EQ(NO_ERROR, gralloc4::decodeProtectedContent(vec, &realProtectedContent)); + EXPECT_EQ(1, realProtectedContent); +} + +/** + * Test IMapper::set(Compression) + */ +TEST_F(GraphicsMapperHidlTest, SetCompression) { + auto info = mDummyDescriptorInfo; + info.usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN); + + ExtendableType compression = gralloc4::Compression_DisplayStreamCompression; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeCompression(compression, &vec)); + + testSet(info, gralloc4::MetadataType_Compression, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + ExtendableType realCompression = gralloc4::Compression_None; + ASSERT_EQ(NO_ERROR, gralloc4::decodeCompression(vec, &realCompression)); + + EXPECT_EQ(compression.name, realCompression.name); + EXPECT_EQ(compression.value, realCompression.value); + }); +} + +/** + * Test IMapper::set(Interlaced) + */ +TEST_F(GraphicsMapperHidlTest, SetInterlaced) { + ExtendableType interlaced = gralloc4::Interlaced_RightLeft; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeInterlaced(interlaced, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Interlaced, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + ExtendableType realInterlaced = gralloc4::Interlaced_None; + ASSERT_EQ(NO_ERROR, gralloc4::decodeInterlaced(vec, &realInterlaced)); + + EXPECT_EQ(interlaced.name, realInterlaced.name); + EXPECT_EQ(interlaced.value, realInterlaced.value); + }); +} + +/** + * Test IMapper::set(ChromaSiting) + */ +TEST_F(GraphicsMapperHidlTest, SetChromaSiting) { + ExtendableType chromaSiting = gralloc4::ChromaSiting_SitedInterstitial; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeChromaSiting(chromaSiting, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_ChromaSiting, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + ExtendableType realChromaSiting = gralloc4::ChromaSiting_None; + ASSERT_EQ(NO_ERROR, gralloc4::decodeChromaSiting(vec, &realChromaSiting)); + + EXPECT_EQ(chromaSiting.name, realChromaSiting.name); + EXPECT_EQ(chromaSiting.value, realChromaSiting.value); + }); +} + +/** + * Test IMapper::set(PlaneLayouts) + */ +TEST_F(GraphicsMapperHidlTest, SetPlaneLayouts) { + const native_handle_t* bufferHandle = nullptr; + auto info = mDummyDescriptorInfo; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true)); + + std::vector<PlaneLayout> planeLayouts; + PlaneLayout planeLayoutA; + PlaneLayout planeLayoutRGB; + PlaneLayoutComponent component; + + planeLayoutA.offsetInBytes = 0; + planeLayoutA.sampleIncrementInBits = 8; + planeLayoutA.strideInBytes = info.width + 20; + planeLayoutA.widthInSamples = info.width; + planeLayoutA.heightInSamples = info.height; + planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * info.height; + planeLayoutA.horizontalSubsampling = 1; + planeLayoutA.verticalSubsampling = 1; + planeLayoutA.crop.left = 0; + planeLayoutA.crop.top = 0; + planeLayoutA.crop.right = info.width; + planeLayoutA.crop.bottom = info.height; + + component.type = gralloc4::PlaneLayoutComponentType_A; + component.offsetInBits = 0; + component.sizeInBits = 8; + planeLayoutA.components.push_back(component); + + planeLayouts.push_back(planeLayoutA); + + planeLayoutRGB.offsetInBytes = 0; + planeLayoutRGB.sampleIncrementInBits = 32; + planeLayoutRGB.strideInBytes = info.width + 20; + planeLayoutRGB.widthInSamples = info.width; + planeLayoutRGB.heightInSamples = info.height; + planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * info.height; + planeLayoutRGB.horizontalSubsampling = 1; + planeLayoutRGB.verticalSubsampling = 1; + planeLayoutRGB.crop.left = 0; + planeLayoutRGB.crop.top = 0; + planeLayoutRGB.crop.right = info.width; + planeLayoutRGB.crop.bottom = info.height; + + component.type = gralloc4::PlaneLayoutComponentType_R; + planeLayoutRGB.components.push_back(component); + component.type = gralloc4::PlaneLayoutComponentType_G; + planeLayoutRGB.components.push_back(component); + component.type = gralloc4::PlaneLayoutComponentType_B; + planeLayoutRGB.components.push_back(component); + + planeLayouts.push_back(planeLayoutRGB); + + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodePlaneLayouts(planeLayouts, &vec)); + + Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_PlaneLayouts, vec); + if (err == Error::UNSUPPORTED) { + GTEST_SUCCEED() << "setting this metadata is unsupported"; + } + ASSERT_EQ(err, Error::NONE); + + std::vector<PlaneLayout> realPlaneLayouts; + ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &realPlaneLayouts)); + + ASSERT_EQ(planeLayouts.size(), realPlaneLayouts.size()); + + for (int i = 0; i < realPlaneLayouts.size(); i++) { + const auto& planeLayout = planeLayouts[i]; + const auto& realPlaneLayout = realPlaneLayouts[i]; + + EXPECT_EQ(planeLayout.offsetInBytes, realPlaneLayout.offsetInBytes); + EXPECT_EQ(planeLayout.sampleIncrementInBits, realPlaneLayout.sampleIncrementInBits); + EXPECT_EQ(planeLayout.strideInBytes, realPlaneLayout.strideInBytes); + EXPECT_EQ(planeLayout.widthInSamples, realPlaneLayout.widthInSamples); + EXPECT_EQ(planeLayout.heightInSamples, realPlaneLayout.heightInSamples); + EXPECT_LE(planeLayout.totalSizeInBytes, realPlaneLayout.totalSizeInBytes); + EXPECT_EQ(planeLayout.horizontalSubsampling, realPlaneLayout.horizontalSubsampling); + EXPECT_EQ(planeLayout.verticalSubsampling, realPlaneLayout.verticalSubsampling); + + EXPECT_EQ(planeLayout.crop.left, realPlaneLayout.crop.left); + EXPECT_EQ(planeLayout.crop.top, realPlaneLayout.crop.top); + EXPECT_EQ(planeLayout.crop.right, realPlaneLayout.crop.right); + EXPECT_EQ(planeLayout.crop.bottom, realPlaneLayout.crop.bottom); + + ASSERT_EQ(planeLayout.components.size(), realPlaneLayout.components.size()); + + for (int j = 0; j < realPlaneLayout.components.size(); j++) { + const auto& component = planeLayout.components[j]; + const auto& realComponent = realPlaneLayout.components[j]; + + EXPECT_EQ(component.type.name, realComponent.type.name); + EXPECT_EQ(component.type.value, realComponent.type.value); + EXPECT_EQ(component.sizeInBits, realComponent.sizeInBits); + EXPECT_EQ(component.offsetInBits, realComponent.offsetInBits); + } + } +} + +/** + * Test IMapper::set(Dataspace) + */ +TEST_F(GraphicsMapperHidlTest, SetDataspace) { + Dataspace dataspace = Dataspace::V0_SRGB_LINEAR; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeDataspace(dataspace, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Dataspace, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + Dataspace realDataspace = Dataspace::UNKNOWN; + ASSERT_EQ(NO_ERROR, gralloc4::decodeDataspace(vec, &realDataspace)); + EXPECT_EQ(dataspace, realDataspace); + }); +} + +/** + * Test IMapper::set(BlendMode) + */ +TEST_F(GraphicsMapperHidlTest, SetBlendMode) { + BlendMode blendMode = BlendMode::PREMULTIPLIED; + hidl_vec<uint8_t> vec; + ASSERT_EQ(NO_ERROR, gralloc4::encodeBlendMode(blendMode, &vec)); + + testSet(mDummyDescriptorInfo, gralloc4::MetadataType_BlendMode, vec, + [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) { + BlendMode realBlendMode = BlendMode::INVALID; + ASSERT_EQ(NO_ERROR, gralloc4::decodeBlendMode(vec, &realBlendMode)); + EXPECT_EQ(blendMode, realBlendMode); + }); +} + +/** + * Test IMapper::set(metadata) with a bad buffer + */ +TEST_F(GraphicsMapperHidlTest, SetMetadataNullBuffer) { + const native_handle_t* bufferHandle = nullptr; + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_BufferId, vec)); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Name, vec)); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Width, vec)); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Height, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_LayerCount, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatModifier, vec)); + ASSERT_EQ(Error::BAD_BUFFER, mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_AllocationSize, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_ProtectedContent, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_Compression, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_Interlaced, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_ChromaSiting, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PlaneLayouts, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_Dataspace, vec)); + ASSERT_EQ(Error::BAD_BUFFER, + mGralloc->set(bufferHandle, gralloc4::MetadataType_BlendMode, vec)); +} + +/** + * Test IMapper::set(metadata) for constant metadata + */ +TEST_F(GraphicsMapperHidlTest, SetConstantMetadata) { + const native_handle_t* bufferHandle = nullptr; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true)); + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_BufferId, vec)); + ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Name, vec)); + ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Width, vec)); + ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Height, vec)); + ASSERT_EQ(Error::BAD_VALUE, + mGralloc->set(bufferHandle, gralloc4::MetadataType_LayerCount, vec)); + ASSERT_EQ(Error::BAD_VALUE, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, vec)); + ASSERT_EQ(Error::BAD_VALUE, mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec)); +} + +/** + * Test IMapper::set(metadata) for bad metadata + */ +TEST_F(GraphicsMapperHidlTest, SetBadMetadata) { + const native_handle_t* bufferHandle = nullptr; + ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true)); + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_BufferId, vec)); + ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Name, vec)); + ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Width, vec)); + ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Height, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_LayerCount, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatRequested, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PixelFormatModifier, vec)); + ASSERT_EQ(Error::UNSUPPORTED, mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_AllocationSize, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_ProtectedContent, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_Compression, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_Interlaced, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_ChromaSiting, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_PlaneLayouts, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_Dataspace, vec)); + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->set(bufferHandle, gralloc4::MetadataType_BlendMode, vec)); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(BufferId) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoBufferId) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, + gralloc4::MetadataType_BufferId, &vec)); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(Name) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoName) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_Name, &vec)); + + std::string name; + ASSERT_EQ(NO_ERROR, gralloc4::decodeName(vec, &name)); + EXPECT_EQ(mDummyDescriptorInfo.name, name); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(Width) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoWidth) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_Width, &vec)); + + uint64_t width = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeWidth(vec, &width)); + EXPECT_EQ(mDummyDescriptorInfo.width, width); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(Height) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoHeight) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_Height, &vec)); + + uint64_t height = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeHeight(vec, &height)); + EXPECT_EQ(mDummyDescriptorInfo.height, height); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(PixelFormatRequested) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPixelFormatRequested) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, + mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatRequested, &vec)); + + PixelFormat pixelFormatRequested = PixelFormat::BLOB; + ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested)); + EXPECT_EQ(mDummyDescriptorInfo.format, pixelFormatRequested); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(PixelFormatFourCC) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPixelFormatFourCC) { + hidl_vec<uint8_t> vec; + Error err = mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatFourCC, &vec); + if (err == Error::UNSUPPORTED) { + GTEST_SUCCEED() << "setting this metadata is unsupported"; + } + ASSERT_EQ(err, Error::NONE); + + uint32_t pixelFormatFourCC = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC)); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(PixelFormatModifier) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPixelFormatModifier) { + hidl_vec<uint8_t> vec; + Error err = mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_PixelFormatModifier, &vec); + if (err == Error::UNSUPPORTED) { + GTEST_SUCCEED() << "setting this metadata is unsupported"; + } + ASSERT_EQ(err, Error::NONE); + + uint64_t pixelFormatModifier = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier)); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(Usage) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoUsage) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_Usage, &vec)); + + uint64_t usage = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &usage)); + EXPECT_EQ(mDummyDescriptorInfo.usage, usage); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(AllocationSize) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoAllocationSize) { + hidl_vec<uint8_t> vec; + Error err = mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, + gralloc4::MetadataType_AllocationSize, &vec); + if (err == Error::UNSUPPORTED) { + GTEST_SUCCEED() << "setting this metadata is unsupported"; + } + ASSERT_EQ(err, Error::NONE); + + uint64_t allocationSize = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize)); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(ProtectedContent) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoProtectedContent) { + auto info = mDummyDescriptorInfo; + info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY; + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + info, gralloc4::MetadataType_ProtectedContent, &vec)); + + uint64_t protectedContent = 0; + ASSERT_EQ(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent)); + EXPECT_EQ(1, protectedContent); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(Compression) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoCompression) { + auto info = mDummyDescriptorInfo; + info.usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN); + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + info, gralloc4::MetadataType_Compression, &vec)); + + ExtendableType compression = gralloc4::Compression_DisplayStreamCompression; + ASSERT_EQ(NO_ERROR, gralloc4::decodeCompression(vec, &compression)); + + EXPECT_EQ(gralloc4::Compression_None.name, compression.name); + EXPECT_EQ(gralloc4::Compression_None.value, compression.value); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(Interlaced) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoInterlaced) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_Interlaced, &vec)); + + ExtendableType interlaced = gralloc4::Interlaced_TopBottom; + ASSERT_EQ(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced)); + + EXPECT_EQ(gralloc4::Interlaced_None.name, interlaced.name); + EXPECT_EQ(gralloc4::Interlaced_None.value, interlaced.value); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(ChromaSiting) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoChromaSiting) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, + mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, + gralloc4::MetadataType_ChromaSiting, &vec)); + + ExtendableType chromaSiting = gralloc4::ChromaSiting_CositedHorizontal; + ASSERT_EQ(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting)); + + EXPECT_EQ(gralloc4::ChromaSiting_None.name, chromaSiting.name); + EXPECT_EQ(gralloc4::ChromaSiting_None.value, chromaSiting.value); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(PlaneLayouts) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoPlaneLayouts) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, + mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, + gralloc4::MetadataType_PlaneLayouts, &vec)); + + std::vector<PlaneLayout> planeLayouts; + ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts)); + ASSERT_NO_FATAL_FAILURE(verifyDummyDescriptorInfoPlaneLayouts(planeLayouts)); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(Dataspace) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoDataspace) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_Dataspace, &vec)); + + Dataspace dataspace = Dataspace::DISPLAY_P3; + ASSERT_EQ(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace)); + EXPECT_EQ(Dataspace::UNKNOWN, dataspace); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(BlendMode) + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoBlendMode) { + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::NONE, mGralloc->getFromBufferDescriptorInfo( + mDummyDescriptorInfo, gralloc4::MetadataType_BlendMode, &vec)); + + BlendMode blendMode = BlendMode::COVERAGE; + ASSERT_EQ(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode)); + EXPECT_EQ(BlendMode::INVALID, blendMode); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(metadata) for unsupported metadata + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoUnsupportedMetadata) { + MetadataType metadataTypeFake = {"FAKE", 1}; + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, metadataTypeFake, &vec)); + ASSERT_EQ(0, vec.size()); +} + +/** + * Test IMapper::getFromBufferDescriptorInfo(metadata) for unsupported standard metadata + */ +TEST_F(GraphicsMapperHidlTest, GetFromBufferDescriptorInfoUnsupportedStandardMetadata) { + MetadataType metadataTypeFake = {GRALLOC4_STANDARD_METADATA_TYPE, 9999}; + + hidl_vec<uint8_t> vec; + ASSERT_EQ(Error::UNSUPPORTED, + mGralloc->getFromBufferDescriptorInfo(mDummyDescriptorInfo, metadataTypeFake, &vec)); + ASSERT_EQ(0, vec.size()); +} + +/** + * Test IMapper::listSupportedMetadataTypes() + */ +TEST_F(GraphicsMapperHidlTest, ListSupportedMetadataTypes) { + hidl_vec<IMapper::MetadataTypeDescription> descriptions; + mGralloc->getMapper()->listSupportedMetadataTypes( + [&](const auto& tmpError, const auto& tmpDescriptions) { + ASSERT_EQ(Error::NONE, tmpError); + descriptions = tmpDescriptions; + }); + + std::set<StandardMetadataType> foundMetadataTypes; + + std::set<StandardMetadataType> notSettableMetadataTypes{ + StandardMetadataType::BUFFER_ID, StandardMetadataType::NAME, + StandardMetadataType::WIDTH, StandardMetadataType::HEIGHT, + StandardMetadataType::LAYER_COUNT, StandardMetadataType::PIXEL_FORMAT_REQUESTED, + StandardMetadataType::USAGE}; + + ASSERT_LE(sRequiredMetadataTypes.size(), descriptions.size()); + + for (const auto& description : descriptions) { + const auto& metadataType = description.metadataType; + + if (!gralloc4::isStandardMetadataType(metadataType)) { + EXPECT_GT(0, description.description.size()); + continue; + } + + StandardMetadataType type = gralloc4::getStandardMetadataTypeValue(metadataType); + + if (sRequiredMetadataTypes.find(type) == sRequiredMetadataTypes.end()) { + continue; + } + + ASSERT_EQ(foundMetadataTypes.find(type), foundMetadataTypes.end()); + foundMetadataTypes.insert(type); + + ASSERT_TRUE(description.isGettable); + + if (notSettableMetadataTypes.find(type) != notSettableMetadataTypes.end()) { + ASSERT_FALSE(description.isSettable); + } + } + + ASSERT_EQ(sRequiredMetadataTypes, foundMetadataTypes); +} + +} // namespace +} // namespace vts +} // namespace V4_0 +} // namespace mapper +} // namespace graphics +} // namespace hardware +} // namespace android + +int main(int argc, char** argv) { + using android::hardware::graphics::mapper::V4_0::vts::GraphicsMapperHidlEnvironment; + ::testing::AddGlobalTestEnvironment(GraphicsMapperHidlEnvironment::Instance()); + ::testing::InitGoogleTest(&argc, argv); + GraphicsMapperHidlEnvironment::Instance()->init(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +} diff --git a/keymaster/4.0/support/Keymaster.cpp b/keymaster/4.0/support/Keymaster.cpp index 1eb9a68547..f20f9511fc 100644 --- a/keymaster/4.0/support/Keymaster.cpp +++ b/keymaster/4.0/support/Keymaster.cpp @@ -80,8 +80,7 @@ std::ostream& operator<<(std::ostream& os, const Keymaster& keymaster) { } template <typename Wrapper> -std::vector<std::unique_ptr<Keymaster>> enumerateDevices( - const sp<IServiceManager>& serviceManager) { +Keymaster::KeymasterSet enumerateDevices(const sp<IServiceManager>& serviceManager) { Keymaster::KeymasterSet result; bool foundDefault = false; @@ -92,7 +91,7 @@ std::vector<std::unique_ptr<Keymaster>> enumerateDevices( auto device = Wrapper::WrappedIKeymasterDevice::getService(name); CHECK(device) << "Failed to get service for " << descriptor << " with interface name " << name; - result.push_back(std::unique_ptr<Keymaster>(new Wrapper(device, name))); + result.push_back(new Wrapper(device, name)); } }); @@ -100,7 +99,7 @@ std::vector<std::unique_ptr<Keymaster>> enumerateDevices( // "default" wasn't provided by listManifestByInterface. Maybe there's a passthrough // implementation. auto device = Wrapper::WrappedIKeymasterDevice::getService("default"); - if (device) result.push_back(std::unique_ptr<Keymaster>(new Wrapper(device, "default"))); + if (device) result.push_back(new Wrapper(device, "default")); } return result; diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h index 43a34b055b..ad83f17106 100644 --- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h +++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h @@ -39,8 +39,8 @@ namespace support { * while still having to use only the latest interface. */ class Keymaster : public IKeymasterDevice { - public: - using KeymasterSet = std::vector<std::unique_ptr<Keymaster>>; + public: + using KeymasterSet = std::vector<android::sp<Keymaster>>; Keymaster(const hidl_string& descriptor, const hidl_string& instanceName) : descriptor_(descriptor), instanceName_(instanceName) {} @@ -86,7 +86,7 @@ class Keymaster : public IKeymasterDevice { */ static void performHmacKeyAgreement(const KeymasterSet& keymasters); - private: + private: hidl_string descriptor_; hidl_string instanceName_; }; diff --git a/media/c2/1.1/Android.bp b/media/c2/1.1/Android.bp new file mode 100644 index 0000000000..c3e30b215f --- /dev/null +++ b/media/c2/1.1/Android.bp @@ -0,0 +1,27 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.media.c2@1.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "IComponent.hal", + "IComponentStore.hal", + ], + interfaces: [ + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hardware.media.bufferpool@2.0", + "android.hardware.media.c2@1.0", + "android.hardware.media.omx@1.0", + "android.hardware.media@1.0", + "android.hidl.base@1.0", + "android.hidl.safe_union@1.0", + ], + gen_java: false, +} diff --git a/media/c2/1.1/IComponent.hal b/media/c2/1.1/IComponent.hal new file mode 100644 index 0000000000..97382ddbac --- /dev/null +++ b/media/c2/1.1/IComponent.hal @@ -0,0 +1,72 @@ +/* + * 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. + */ + +package android.hardware.media.c2@1.1; + +import android.hardware.media.c2@1.0::IComponent; +import android.hardware.media.c2@1.0::Status; + +/** + * Interface for a Codec2 component corresponding to API level 1.0 or below. + * Components have two states: stopped and running. The running state has three + * sub-states: executing, tripped and error. + * + * All methods in `IComponent` must not block. If a method call cannot be + * completed in a timely manner, it must return `TIMED_OUT` in the return + * status. + * + * @note This is an extension of version 1.0 of `IComponent`. The purpose of the + * extension is to add support for tunneling. + */ +interface IComponent extends @1.0::IComponent { + /** + * Configures a component for a tunneled playback mode. + * + * A successful call to this method puts the component in the *tunneled* + * mode. In this mode, the output `Worklet`s returned in + * IComponentListener::onWorkDone() may not contain any buffers. The output + * buffers are passed directly to the consumer end of a buffer queue whose + * producer side is configured with the returned @p sidebandStream passed + * to IGraphicBufferProducer::setSidebandStream(). + * + * The component is initially in the non-tunneled mode by default. The + * tunneled mode can be toggled on only before the component starts + * processing. Once the component is put into the tunneled mode, it shall + * stay in the tunneled mode until and only until reset() is called. + * + * @param avSyncHwId A resource ID for hardware sync. The generator of sync + * IDs must ensure that this number is unique among all services at any + * given time. For example, if both the audio HAL and the tuner HAL + * support this feature, sync IDs from the audio HAL must not clash + * with sync IDs from the tuner HAL. + * @return status Status of the call, which may be + * - `OK` - The operation completed successfully. In this case, + * @p sidebandHandle shall not be a null handle. + * - `OMITTED` - The component does not support video tunneling. + * - `BAD_STATE` - The component is already running. + * - `TIMED_OUT` - The operation cannot be finished in a timely manner. + * - `CORRUPTED` - Some unknown error occurred. + * @return sidebandHandle Codec-allocated sideband stream handle. This can + * be passed to IGraphicBufferProducer::setSidebandStream() to + * establish a direct channel to the consumer. + */ + configureVideoTunnel( + uint32_t avSyncHwId + ) generates ( + Status status, + handle sidebandHandle + ); +}; diff --git a/media/c2/1.1/IComponentStore.hal b/media/c2/1.1/IComponentStore.hal new file mode 100644 index 0000000000..38e0fc60be --- /dev/null +++ b/media/c2/1.1/IComponentStore.hal @@ -0,0 +1,64 @@ +/* + * 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. + */ + +package android.hardware.media.c2@1.1; + +import android.hardware.media.bufferpool@2.0::IClientManager; +import android.hardware.media.c2@1.0::IComponentListener; +import android.hardware.media.c2@1.0::IComponentStore; +import android.hardware.media.c2@1.0::Status; + +import IComponent; + +/** + * Entry point for Codec2 HAL. + * + * All methods in `IComponentStore` must not block. If a method call cannot be + * completed in a timely manner, it must return `TIMED_OUT` in the return + * status. The only exceptions are getPoolClientManager() and getConfigurable(), + * which must always return immediately. + * + * @note This is an extension of version 1.0 of `IComponentStore`. The purpose + * of the extension is to add support for tunneling. + */ +interface IComponentStore extends @1.0::IComponentStore { + /** + * Creates a component by name. + * + * @param name Name of the component to create. This must match one of the + * names returned by listComponents(). + * @param listener Callback receiver. + * @param pool `IClientManager` object of the BufferPool in the client + * process. This may be null if the client does not own a BufferPool. + * @return status Status of the call, which may be + * - `OK` - The component was created successfully. + * - `NOT_FOUND` - There is no component with the given name. + * - `NO_MEMORY` - Not enough memory to create the component. + * - `TIMED_OUT` - The operation cannot be finished in a timely manner. + * - `CORRUPTED` - Some unknown error occurred. + * @return comp The created component if @p status is `OK`. + * + * @sa IComponentListener. + */ + createComponent_1_1( + string name, + IComponentListener listener, + IClientManager pool + ) generates ( + Status status, + IComponent comp + ); +}; diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal index 837ced5b48..ef71ea8712 100644 --- a/neuralnetworks/1.2/types.hal +++ b/neuralnetworks/1.2/types.hal @@ -2448,15 +2448,17 @@ enum OperationType : int32_t { * then clipping is disabled. * If all the input tensors have type {@link OperandType::TENSOR_FLOAT32}, * this scalar must be of the type {@link OperandType::FLOAT32}, - * otherwise if all the input tensors have the type {@link OperandType::TENSOR_FLOAT16}, - * this scalar must be of type {@link OperandType::FLOAT16}. + * otherwise if all the input tensors have the type + * {@link OperandType::TENSOR_FLOAT16}, this scalar must be + * of type {@link OperandType::FLOAT16}. * * 50: The clipping threshold for the output from the * projection layer, such that values are bound within * [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled. * If all the input tensors have type {@link OperandType::TENSOR_FLOAT32}, * this scalar must be of the type {@link OperandType::FLOAT32}, - * otherwise if all the input tensors have the type {@link OperandType::TENSOR_FLOAT16}, - * this scalar must be of type {@link OperandType::FLOAT16}. + * otherwise if all the input tensors have the type + * {@link OperandType::TENSOR_FLOAT16}, this scalar must be + * of type {@link OperandType::FLOAT16}. * * 51: merge_outputs * An {@link OperandType::BOOL} scalar specifying if the outputs * from forward and backward cells should be merged. @@ -4124,7 +4126,6 @@ enum OperationType : int32_t { * * 0: A tensor of the same type and shape as input1 and input2. * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, * the scale and zeroPoint can be different from inputs' scale and zeroPoint. - * */ SELECT = 84, diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp index aacb38500b..c1bf494328 100644 --- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp @@ -58,8 +58,20 @@ using V1_0::Request; using V1_1::ExecutionPreference; using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>; +namespace { + +enum class Executor { ASYNC, SYNC, BURST }; + enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT }; +struct TestConfig { + Executor executor; + MeasureTiming measureTiming; + OutputType outputType; +}; + +} // namespace + Model createModel(const TestModel& testModel) { // Model operands. hidl_vec<Operand> operands(testModel.operands.size()); @@ -194,31 +206,31 @@ static std::shared_ptr<::android::nn::ExecutionBurstController> CreateBurst( return android::nn::ExecutionBurstController::create(preparedModel, std::chrono::microseconds{0}); } -enum class Executor { ASYNC, SYNC, BURST }; void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel, - Executor executor, MeasureTiming measure, OutputType outputType) { + const TestConfig& testConfig) { // If output0 does not have size larger than one byte, we can not test with insufficient buffer. - if (outputType == OutputType::INSUFFICIENT && !isOutputSizeGreaterThanOne(testModel, 0)) { + if (testConfig.outputType == OutputType::INSUFFICIENT && + !isOutputSizeGreaterThanOne(testModel, 0)) { return; } Request request = createRequest(testModel); - if (outputType == OutputType::INSUFFICIENT) { + if (testConfig.outputType == OutputType::INSUFFICIENT) { makeOutputInsufficientSize(/*outputIndex=*/0, &request); } ErrorStatus executionStatus; hidl_vec<OutputShape> outputShapes; Timing timing; - switch (executor) { + switch (testConfig.executor) { case Executor::ASYNC: { SCOPED_TRACE("asynchronous"); // launch execution sp<ExecutionCallback> executionCallback = new ExecutionCallback(); - Return<ErrorStatus> executionLaunchStatus = - ExecutePreparedModel(preparedModel, request, measure, executionCallback); + Return<ErrorStatus> executionLaunchStatus = ExecutePreparedModel( + preparedModel, request, testConfig.measureTiming, executionCallback); ASSERT_TRUE(executionLaunchStatus.isOk()); EXPECT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(executionLaunchStatus)); @@ -234,8 +246,8 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo SCOPED_TRACE("synchronous"); // execute - Return<ErrorStatus> executionReturnStatus = - ExecutePreparedModel(preparedModel, request, measure, &outputShapes, &timing); + Return<ErrorStatus> executionReturnStatus = ExecutePreparedModel( + preparedModel, request, testConfig.measureTiming, &outputShapes, &timing); ASSERT_TRUE(executionReturnStatus.isOk()); executionStatus = static_cast<ErrorStatus>(executionReturnStatus); @@ -258,14 +270,14 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo // execute burst int n; std::tie(n, outputShapes, timing, std::ignore) = - controller->compute(request, measure, keys); + controller->compute(request, testConfig.measureTiming, keys); executionStatus = nn::convertResultCodeToErrorStatus(n); break; } } - if (outputType != OutputType::FULLY_SPECIFIED && + if (testConfig.outputType != OutputType::FULLY_SPECIFIED && executionStatus == ErrorStatus::GENERAL_FAILURE) { LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot " "execute model that it does not support."; @@ -274,7 +286,7 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo << std::endl; GTEST_SKIP(); } - if (measure == MeasureTiming::NO) { + if (testConfig.measureTiming == MeasureTiming::NO) { EXPECT_EQ(UINT64_MAX, timing.timeOnDevice); EXPECT_EQ(UINT64_MAX, timing.timeInDriver); } else { @@ -283,7 +295,7 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo } } - switch (outputType) { + switch (testConfig.outputType) { case OutputType::FULLY_SPECIFIED: // If the model output operands are fully specified, outputShapes must be either // either empty, or have the same number of elements as the number of outputs. @@ -321,44 +333,29 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel, bool testDynamicOutputShape) { + std::initializer_list<OutputType> outputTypesList; + std::initializer_list<MeasureTiming> measureTimingList; + std::initializer_list<Executor> executorList; + if (testDynamicOutputShape) { - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::NO, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::NO, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::NO, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::YES, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::YES, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::YES, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::NO, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::NO, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::NO, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::YES, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::YES, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::YES, - OutputType::INSUFFICIENT); + outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT}; + measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; + executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; } else { - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::NO, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::NO, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::NO, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::YES, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::YES, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::YES, - OutputType::FULLY_SPECIFIED); + outputTypesList = {OutputType::FULLY_SPECIFIED}; + measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; + executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + } + + for (const OutputType outputType : outputTypesList) { + for (const MeasureTiming measureTiming : measureTimingList) { + for (const Executor executor : executorList) { + const TestConfig testConfig = {.executor = executor, + .measureTiming = measureTiming, + .outputType = outputType}; + EvaluatePreparedModel(preparedModel, testModel, testConfig); + } + } } } diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp index a14b86bcf1..30530beacc 100644 --- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp +++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp @@ -216,7 +216,7 @@ static std::vector<int32_t> getInvalidZeroPoints(OperandType type) { case OperandType::TENSOR_QUANT8_ASYMM: return {-1, 256}; case OperandType::TENSOR_QUANT8_SYMM: - return {-129, -1, 1, 128}; + return {-129, -1, 1, 128}; case OperandType::TENSOR_QUANT16_ASYMM: return {-1, 65536}; case OperandType::TENSOR_QUANT16_SYMM: @@ -482,15 +482,15 @@ static bool removeOperandSkip(size_t operand, const Model& model) { } } } - // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have - // either one or two outputs depending on their mergeOutputs parameter. + // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have either one or two + // outputs depending on their mergeOutputs parameter. if (operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM || operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) { - for (const size_t outOprand : operation.outputs) { - if (operand == outOprand) { - return true; + for (const size_t outOprand : operation.outputs) { + if (operand == outOprand) { + return true; + } } - } } } return false; diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal index 7df14b18f6..3551d5762a 100644 --- a/neuralnetworks/1.3/types.hal +++ b/neuralnetworks/1.3/types.hal @@ -109,6 +109,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -116,7 +117,8 @@ enum OperationType : int32_t { * * 0: A tensor. * * 1: A tensor of the same {@link OperandType}, and compatible dimensions * as input0. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scales and zeroPoint can be different from input0 scale and zeroPoint. * * 2: An {@link OperandType::INT32} scalar, and has to be one of the * {@link FusedActivationFunc} values. Specifies the activation to @@ -124,7 +126,8 @@ enum OperationType : int32_t { * * Outputs: * * 0: The sum, a tensor of the same {@link OperandType} as input0. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, * the scale and zeroPoint can be different from inputs' scale and zeroPoint. */ ADD = @1.2::OperationType:ADD, @@ -146,6 +149,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout. * With the default data layout NHWC, the data is stored in the order of: @@ -207,7 +211,8 @@ enum OperationType : int32_t { * Outputs: * * 0: The output 4-D tensor, of shape * [batches, out_height, out_width, depth]. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scale and zeroPoint must be the same as input0. */ AVERAGE_POOL_2D = @1.2::OperationType:AVERAGE_POOL_2D, @@ -951,6 +956,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4. * @@ -962,6 +968,8 @@ enum OperationType : int32_t { * * 0: The output tensor of same shape as input0. * For {@link OperandType::TENSOR_QUANT8_ASYMM}, * the scale must be 1.f / 256 and the zeroPoint must be 0. + * For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, + * the scale must be 1.f / 256 and the zeroPoint must be -128. */ LOGISTIC = @1.2::OperationType:LOGISTIC, @@ -1256,6 +1264,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout. * With the default data layout NHWC, the data is stored in the order of: @@ -1317,7 +1326,8 @@ enum OperationType : int32_t { * Outputs: * * 0: The output 4-D tensor, of shape * [batches, out_height, out_width, depth]. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scale and zeroPoint must be the same as input0. */ MAX_POOL_2D = @1.2::OperationType:MAX_POOL_2D, @@ -1345,6 +1355,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -1358,7 +1369,8 @@ enum OperationType : int32_t { * * Outputs: * * 0: The product, a tensor of the same {@link OperandType} as input0. - * For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}, + * For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM} + * and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, * the following condition must be satisfied: * output_scale > input1_scale * input2_scale. */ @@ -1375,6 +1387,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4. * @@ -1384,7 +1397,8 @@ enum OperationType : int32_t { * * Outputs: * * 0: The output tensor of same shape as input0. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scale and zeroPoint must be the same as input0. */ RELU = @1.2::OperationType:RELU, @@ -1400,6 +1414,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4. * @@ -1409,7 +1424,8 @@ enum OperationType : int32_t { * * Outputs: * * 0: The output tensor of the same shape as input0. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scale and zeroPoint must be the same as input0. */ RELU1 = @1.2::OperationType:RELU1, @@ -1425,6 +1441,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4. * @@ -1434,7 +1451,8 @@ enum OperationType : int32_t { * * Outputs: * * 0: The output tensor of same shape as input0. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scale and zeroPoint must be the same as input0. */ RELU6 = @1.2::OperationType:RELU6, @@ -1755,6 +1773,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2) + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4. * @@ -1766,6 +1785,8 @@ enum OperationType : int32_t { * * 0: The output tensor of same shape as input0. * For {@link OperandType::TENSOR_QUANT8_ASYMM}, * the scale must be 1.f / 128 and the zeroPoint must be 128. + * For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, + * the scale must be 1.f / 128 and the zeroPoint must be 0. */ TANH = @1.2::OperationType:TANH, @@ -2083,6 +2104,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2) + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -2096,7 +2118,8 @@ enum OperationType : int32_t { * * Outputs: * * 0: A tensor of the same {@link OperandType} as input0. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} and + * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scale and zeroPoint can be different from inputs' scale and zeroPoint. */ SUB = @1.2::OperationType:SUB, @@ -2375,15 +2398,17 @@ enum OperationType : int32_t { * then clipping is disabled. * If all the input tensors have type {@link OperandType::TENSOR_FLOAT32}, * this scalar must be of the type {@link OperandType::FLOAT32}, - * otherwise if all the input tensors have the type {@link OperandType::TENSOR_FLOAT16}, - * this scalar must be of type {@link OperandType::FLOAT16}. + * otherwise if all the input tensors have the type + * {@link OperandType::TENSOR_FLOAT16}, this scalar must be + * of type {@link OperandType::FLOAT16}. * * 50: The clipping threshold for the output from the * projection layer, such that values are bound within * [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled. * If all the input tensors have type {@link OperandType::TENSOR_FLOAT32}, * this scalar must be of the type {@link OperandType::FLOAT32}, - * otherwise if all the input tensors have the type {@link OperandType::TENSOR_FLOAT16}, - * this scalar must be of type {@link OperandType::FLOAT16}. + * otherwise if all the input tensors have the type + * {@link OperandType::TENSOR_FLOAT16}, this scalar must be + * of type {@link OperandType::FLOAT16}. * * 51: merge_outputs * An {@link OperandType::BOOL} scalar specifying if the outputs * from forward and backward cells should be merged. @@ -4034,6 +4059,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_INT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) * * Supported tensor rank: from 1 * @@ -4044,14 +4070,14 @@ enum OperationType : int32_t { * true) or input2 (if false). * * 1: An input tensor of the same shape as input0. * * 2: An input tensor of the same shape and type as input1. - * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, + * For a {@link OperandType::TENSOR_QUANT8_ASYMM} + * and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor, * the scales and zeroPoint can be different from input1 scale and zeroPoint. * * Outputs: * * 0: A tensor of the same type and shape as input1 and input2. * For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor, * the scale and zeroPoint can be different from inputs' scale and zeroPoint. - * */ SELECT = @1.2::OperationType:SELECT, diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp index d8a7534461..60992d57d7 100644 --- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp +++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp @@ -456,8 +456,7 @@ TEST_P(CompilationCachingTest, CacheSavingAndRetrieval) { } // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); } TEST_P(CompilationCachingTest, CacheSavingAndRetrievalNonZeroOffset) { @@ -519,8 +518,7 @@ TEST_P(CompilationCachingTest, CacheSavingAndRetrievalNonZeroOffset) { } // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); } TEST_P(CompilationCachingTest, SaveToCacheInvalidNumCache) { @@ -541,8 +539,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumCache) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -566,8 +563,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumCache) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -590,8 +586,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumCache) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -615,8 +610,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumCache) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -727,8 +721,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumFd) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -752,8 +745,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumFd) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -776,8 +768,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumFd) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -801,8 +792,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidNumFd) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -914,8 +904,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidAccessMode) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -937,8 +926,7 @@ TEST_P(CompilationCachingTest, SaveToCacheInvalidAccessMode) { saveModelToCache(model, modelCache, dataCache, &preparedModel); ASSERT_NE(preparedModel, nullptr); // Execute and verify results. - EvaluatePreparedModel(preparedModel, testModel, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModel, /*testKind=*/TestKind::GENERAL); // Check if prepareModelFromCache fails. preparedModel = nullptr; ErrorStatus status; @@ -1082,8 +1070,7 @@ TEST_P(CompilationCachingTest, SaveToCache_TOCTOU) { ASSERT_EQ(preparedModel, nullptr); } else { ASSERT_NE(preparedModel, nullptr); - EvaluatePreparedModel(preparedModel, testModelAdd, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModelAdd, /*testKind=*/TestKind::GENERAL); } } } @@ -1144,8 +1131,7 @@ TEST_P(CompilationCachingTest, PrepareFromCache_TOCTOU) { ASSERT_EQ(preparedModel, nullptr); } else { ASSERT_NE(preparedModel, nullptr); - EvaluatePreparedModel(preparedModel, testModelAdd, - /*testDynamicOutputShape=*/false); + EvaluatePreparedModel(preparedModel, testModelAdd, /*testKind=*/TestKind::GENERAL); } } } diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp index a1e04c58de..3e947f5163 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp @@ -69,8 +69,35 @@ using V1_2::Timing; using V1_2::implementation::ExecutionCallback; using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>; +namespace { + +enum class Executor { ASYNC, SYNC, BURST }; + enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT }; +struct TestConfig { + Executor executor; + MeasureTiming measureTiming; + OutputType outputType; + // `reportSkipping` indicates if a test should print an info message in case + // it is skipped. The field is set to true by default and is set to false in + // quantization coupling tests to suppress skipping a test + bool reportSkipping; + TestConfig(Executor executor, MeasureTiming measureTiming, OutputType outputType) + : executor(executor), + measureTiming(measureTiming), + outputType(outputType), + reportSkipping(true) {} + TestConfig(Executor executor, MeasureTiming measureTiming, OutputType outputType, + bool reportSkipping) + : executor(executor), + measureTiming(measureTiming), + outputType(outputType), + reportSkipping(reportSkipping) {} +}; + +} // namespace + Model createModel(const TestModel& testModel) { // Model operands. hidl_vec<Operand> operands(testModel.operands.size()); @@ -205,31 +232,34 @@ static std::shared_ptr<::android::nn::ExecutionBurstController> CreateBurst( return android::nn::ExecutionBurstController::create(preparedModel, std::chrono::microseconds{0}); } -enum class Executor { ASYNC, SYNC, BURST }; void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel, - Executor executor, MeasureTiming measure, OutputType outputType) { + const TestConfig& testConfig, bool* skipped = nullptr) { + if (skipped != nullptr) { + *skipped = false; + } // If output0 does not have size larger than one byte, we can not test with insufficient buffer. - if (outputType == OutputType::INSUFFICIENT && !isOutputSizeGreaterThanOne(testModel, 0)) { + if (testConfig.outputType == OutputType::INSUFFICIENT && + !isOutputSizeGreaterThanOne(testModel, 0)) { return; } Request request = createRequest(testModel); - if (outputType == OutputType::INSUFFICIENT) { + if (testConfig.outputType == OutputType::INSUFFICIENT) { makeOutputInsufficientSize(/*outputIndex=*/0, &request); } ErrorStatus executionStatus; hidl_vec<OutputShape> outputShapes; Timing timing; - switch (executor) { + switch (testConfig.executor) { case Executor::ASYNC: { SCOPED_TRACE("asynchronous"); // launch execution sp<ExecutionCallback> executionCallback = new ExecutionCallback(); - Return<ErrorStatus> executionLaunchStatus = - ExecutePreparedModel(preparedModel, request, measure, executionCallback); + Return<ErrorStatus> executionLaunchStatus = ExecutePreparedModel( + preparedModel, request, testConfig.measureTiming, executionCallback); ASSERT_TRUE(executionLaunchStatus.isOk()); EXPECT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(executionLaunchStatus)); @@ -245,8 +275,8 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo SCOPED_TRACE("synchronous"); // execute - Return<ErrorStatus> executionReturnStatus = - ExecutePreparedModel(preparedModel, request, measure, &outputShapes, &timing); + Return<ErrorStatus> executionReturnStatus = ExecutePreparedModel( + preparedModel, request, testConfig.measureTiming, &outputShapes, &timing); ASSERT_TRUE(executionReturnStatus.isOk()); executionStatus = static_cast<ErrorStatus>(executionReturnStatus); @@ -269,15 +299,21 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo // execute burst int n; std::tie(n, outputShapes, timing, std::ignore) = - controller->compute(request, measure, keys); + controller->compute(request, testConfig.measureTiming, keys); executionStatus = nn::convertResultCodeToErrorStatus(n); break; } } - if (outputType != OutputType::FULLY_SPECIFIED && + if (testConfig.outputType != OutputType::FULLY_SPECIFIED && executionStatus == ErrorStatus::GENERAL_FAILURE) { + if (skipped != nullptr) { + *skipped = true; + } + if (!testConfig.reportSkipping) { + return; + } LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot " "execute model that it does not support."; std::cout << "[ ] Early termination of test because vendor service cannot " @@ -285,7 +321,7 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo << std::endl; GTEST_SKIP(); } - if (measure == MeasureTiming::NO) { + if (testConfig.measureTiming == MeasureTiming::NO) { EXPECT_EQ(UINT64_MAX, timing.timeOnDevice); EXPECT_EQ(UINT64_MAX, timing.timeInDriver); } else { @@ -294,7 +330,7 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo } } - switch (outputType) { + switch (testConfig.outputType) { case OutputType::FULLY_SPECIFIED: // If the model output operands are fully specified, outputShapes must be either // either empty, or have the same number of elements as the number of outputs. @@ -331,59 +367,117 @@ void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestMo } void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, const TestModel& testModel, - bool testDynamicOutputShape) { - if (testDynamicOutputShape) { - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::NO, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::NO, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::NO, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::YES, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::YES, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::YES, - OutputType::UNSPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::NO, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::NO, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::NO, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::YES, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::YES, - OutputType::INSUFFICIENT); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::YES, - OutputType::INSUFFICIENT); - } else { - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::NO, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::NO, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::NO, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::ASYNC, MeasureTiming::YES, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::SYNC, MeasureTiming::YES, - OutputType::FULLY_SPECIFIED); - EvaluatePreparedModel(preparedModel, testModel, Executor::BURST, MeasureTiming::YES, - OutputType::FULLY_SPECIFIED); + TestKind testKind) { + std::initializer_list<OutputType> outputTypesList; + std::initializer_list<MeasureTiming> measureTimingList; + std::initializer_list<Executor> executorList; + + switch (testKind) { + case TestKind::GENERAL: { + outputTypesList = {OutputType::FULLY_SPECIFIED}; + measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; + executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + } break; + case TestKind::DYNAMIC_SHAPE: { + outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT}; + measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; + executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + } break; + case TestKind::QUANTIZATION_COUPLING: { + LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel"; + return; + } break; + } + + for (const OutputType outputType : outputTypesList) { + for (const MeasureTiming measureTiming : measureTimingList) { + for (const Executor executor : executorList) { + const TestConfig testConfig(executor, measureTiming, outputType); + EvaluatePreparedModel(preparedModel, testModel, testConfig); + } + } } } -void Execute(const sp<IDevice>& device, const TestModel& testModel, bool testDynamicOutputShape) { +void EvaluatePreparedCoupledModels(const sp<IPreparedModel>& preparedModel, + const TestModel& testModel, + const sp<IPreparedModel>& preparedCoupledModel, + const TestModel& coupledModel) { + std::initializer_list<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED}; + std::initializer_list<MeasureTiming> measureTimingList = {MeasureTiming::NO, + MeasureTiming::YES}; + std::initializer_list<Executor> executorList = {Executor::ASYNC, Executor::SYNC, + Executor::BURST}; + + for (const OutputType outputType : outputTypesList) { + for (const MeasureTiming measureTiming : measureTimingList) { + for (const Executor executor : executorList) { + const TestConfig testConfig(executor, measureTiming, outputType, + /*reportSkipping=*/false); + bool baseSkipped = false; + EvaluatePreparedModel(preparedModel, testModel, testConfig, &baseSkipped); + bool coupledSkipped = false; + EvaluatePreparedModel(preparedCoupledModel, coupledModel, testConfig, + &coupledSkipped); + ASSERT_EQ(baseSkipped, coupledSkipped); + if (baseSkipped) { + LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot " + "execute model that it does not support."; + std::cout << "[ ] Early termination of test because vendor service " + "cannot " + "execute model that it does not support." + << std::endl; + GTEST_SKIP(); + } + } + } + } +} + +void Execute(const sp<IDevice>& device, const TestModel& testModel, TestKind testKind) { Model model = createModel(testModel); - if (testDynamicOutputShape) { + if (testKind == TestKind::DYNAMIC_SHAPE) { makeOutputDimensionsUnspecified(&model); } sp<IPreparedModel> preparedModel; - createPreparedModel(device, model, &preparedModel); - if (preparedModel == nullptr) return; - - EvaluatePreparedModel(preparedModel, testModel, testDynamicOutputShape); + switch (testKind) { + case TestKind::GENERAL: { + createPreparedModel(device, model, &preparedModel); + if (preparedModel == nullptr) return; + EvaluatePreparedModel(preparedModel, testModel, TestKind::GENERAL); + } break; + case TestKind::DYNAMIC_SHAPE: { + createPreparedModel(device, model, &preparedModel); + if (preparedModel == nullptr) return; + EvaluatePreparedModel(preparedModel, testModel, TestKind::DYNAMIC_SHAPE); + } break; + case TestKind::QUANTIZATION_COUPLING: { + ASSERT_TRUE(testModel.hasQuant8AsymmOperands()); + createPreparedModel(device, model, &preparedModel, /*reportSkipping*/ false); + TestModel signedQuantizedModel = convertQuant8AsymmOperandsToSigned(testModel); + sp<IPreparedModel> preparedCoupledModel; + createPreparedModel(device, createModel(signedQuantizedModel), &preparedCoupledModel, + /*reportSkipping*/ false); + // If we couldn't prepare a model with unsigned quantization, we must + // fail to prepare a model with signed quantization as well. + if (preparedModel == nullptr) { + ASSERT_EQ(preparedCoupledModel, nullptr); + // If we failed to prepare both of the models, we can safely skip + // the test. + LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot " + "prepare model that it does not support."; + std::cout + << "[ ] Early termination of test because vendor service cannot " + "prepare model that it does not support." + << std::endl; + GTEST_SKIP(); + } + ASSERT_NE(preparedCoupledModel, nullptr); + EvaluatePreparedCoupledModels(preparedModel, testModel, preparedCoupledModel, + signedQuantizedModel); + } break; + } } void GeneratedTestBase::SetUp() { @@ -406,12 +500,19 @@ class GeneratedTest : public GeneratedTestBase {}; // Tag for the dynamic output shape tests class DynamicOutputShapeTest : public GeneratedTest {}; +// Tag for the dynamic output shape tests +class DISABLED_QuantizationCouplingTest : public GeneratedTest {}; + TEST_P(GeneratedTest, Test) { - Execute(kDevice, kTestModel, /*testDynamicOutputShape=*/false); + Execute(kDevice, kTestModel, /*testKind=*/TestKind::GENERAL); } TEST_P(DynamicOutputShapeTest, Test) { - Execute(kDevice, kTestModel, /*testDynamicOutputShape=*/true); + Execute(kDevice, kTestModel, /*testKind=*/TestKind::DYNAMIC_SHAPE); +} + +TEST_P(DISABLED_QuantizationCouplingTest, Test) { + Execute(kDevice, kTestModel, /*testKind=*/TestKind::QUANTIZATION_COUPLING); } INSTANTIATE_GENERATED_TEST(GeneratedTest, @@ -420,4 +521,8 @@ INSTANTIATE_GENERATED_TEST(GeneratedTest, INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, [](const TestModel& testModel) { return !testModel.expectFailure; }); +INSTANTIATE_GENERATED_TEST(DISABLED_QuantizationCouplingTest, [](const TestModel& testModel) { + return testModel.hasQuant8AsymmOperands() && testModel.operations.size() == 1; +}); + } // namespace android::hardware::neuralnetworks::V1_3::vts::functional diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h index 45cff5b75b..ad6323f48c 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h @@ -57,8 +57,19 @@ Model createModel(const test_helper::TestModel& testModel); void PrepareModel(const sp<IDevice>& device, const Model& model, sp<IPreparedModel>* preparedModel); +enum class TestKind { + // Runs a test model and compares the results to a golden data + GENERAL, + // Same as GENERAL but sets dimensions for the output tensors to zeros + DYNAMIC_SHAPE, + // Tests if quantized model with TENSOR_QUANT8_ASYMM produces the same result + // (OK/SKIPPED/FAILED) as the model with all such tensors converted to + // TENSOR_QUANT8_ASYMM_SIGNED. + QUANTIZATION_COUPLING +}; + void EvaluatePreparedModel(const sp<IPreparedModel>& preparedModel, - const test_helper::TestModel& testModel, bool testDynamicOutputShape); + const test_helper::TestModel& testModel, TestKind testKind); } // namespace android::hardware::neuralnetworks::V1_3::vts::functional diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp index 625913d485..92d8fa7376 100644 --- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp +++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp @@ -37,7 +37,7 @@ using V1_1::ExecutionPreference; // internal helper function void createPreparedModel(const sp<IDevice>& device, const Model& model, - sp<IPreparedModel>* preparedModel) { + sp<IPreparedModel>* preparedModel, bool reportSkipping) { ASSERT_NE(nullptr, preparedModel); *preparedModel = nullptr; @@ -74,6 +74,9 @@ void createPreparedModel(const sp<IDevice>& device, const Model& model, // can continue. if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) { ASSERT_EQ(nullptr, preparedModel->get()); + if (!reportSkipping) { + return; + } LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot prepare " "model that it does not support."; std::cout << "[ ] Early termination of test because vendor service cannot " diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h index 8cb42d47e5..4e51052966 100644 --- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h +++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h @@ -47,7 +47,7 @@ std::string printNeuralnetworksHidlTest( // Create an IPreparedModel object. If the model cannot be prepared, // "preparedModel" will be nullptr instead. void createPreparedModel(const sp<IDevice>& device, const Model& model, - sp<IPreparedModel>* preparedModel); + sp<IPreparedModel>* preparedModel, bool reportSkipping = true); // Utility function to get PreparedModel from callback and downcast to V1_2. sp<IPreparedModel> getPreparedModel_1_3(const sp<implementation::PreparedModelCallback>& callback); diff --git a/sensors/1.0/default/OWNERS b/sensors/1.0/default/OWNERS index 2031d848cf..90c233030e 100644 --- a/sensors/1.0/default/OWNERS +++ b/sensors/1.0/default/OWNERS @@ -1,2 +1,3 @@ +arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com diff --git a/sensors/1.0/vts/functional/OWNERS b/sensors/1.0/vts/functional/OWNERS index 759d87b482..892da1548c 100644 --- a/sensors/1.0/vts/functional/OWNERS +++ b/sensors/1.0/vts/functional/OWNERS @@ -1,6 +1,7 @@ # Sensors team +arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com # VTS team trong@google.com diff --git a/sensors/2.0/default/OWNERS b/sensors/2.0/default/OWNERS index 2031d848cf..90c233030e 100644 --- a/sensors/2.0/default/OWNERS +++ b/sensors/2.0/default/OWNERS @@ -1,2 +1,3 @@ +arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com diff --git a/sensors/2.0/vts/functional/OWNERS b/sensors/2.0/vts/functional/OWNERS index 759d87b482..892da1548c 100644 --- a/sensors/2.0/vts/functional/OWNERS +++ b/sensors/2.0/vts/functional/OWNERS @@ -1,6 +1,7 @@ # Sensors team +arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com # VTS team trong@google.com diff --git a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp b/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp index 03fcc174ef..dc54f27bf3 100644 --- a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp +++ b/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp @@ -130,8 +130,8 @@ void SensorsHidlEnvironmentV2_0::HidlTearDown() { void SensorsHidlEnvironmentV2_0::startPollingThread() { mStopThread = false; - mPollThread = std::thread(pollingThread, this); mEvents.reserve(MAX_RECEIVE_BUFFER_EVENT_COUNT); + mPollThread = std::thread(pollingThread, this); } void SensorsHidlEnvironmentV2_0::readEvents() { diff --git a/sensors/common/vts/OWNERS b/sensors/common/vts/OWNERS index 759d87b482..892da1548c 100644 --- a/sensors/common/vts/OWNERS +++ b/sensors/common/vts/OWNERS @@ -1,6 +1,7 @@ # Sensors team +arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com # VTS team trong@google.com diff --git a/sensors/common/vts/utils/OWNERS b/sensors/common/vts/utils/OWNERS index 759d87b482..892da1548c 100644 --- a/sensors/common/vts/utils/OWNERS +++ b/sensors/common/vts/utils/OWNERS @@ -1,6 +1,7 @@ # Sensors team +arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com # VTS team trong@google.com diff --git a/tv/cec/1.0/config/sadConfig.xsd b/tv/cec/1.0/config/sadConfig.xsd new file mode 100644 index 0000000000..7f99311151 --- /dev/null +++ b/tv/cec/1.0/config/sadConfig.xsd @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<!-- 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. +--> +<xs:schema version="1.0" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:include schemaLocation="../../../../audio/4.0/config/audio_policy_configuration.xsd"/> + <xs:complexType name="config"> + <xs:annotation> + <xs:documentation xml:lang="en"> + List the config versions supported by Short Audio Descriptor(SAD) config. + </xs:documentation> + </xs:annotation> + <xs:sequence> + <xs:element name="device" type="device" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="version" type="version"/> + </xs:complexType> + <xs:complexType name="device"> + <xs:annotation> + <xs:documentation xml:lang="en"> + Device section: + There is a list of configurations in this SAD config for all the input audio + devices that the current Android device supports. + Each device has the following attributes: + "type": type of the audio device. + And the following element + <supportedFormat/>: the supported format info of the device. There can be + multiple formats supported by one audio device. + </xs:documentation> + </xs:annotation> + <xs:sequence> + <xs:element name="supportedFormat" type="supportedFormat" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="type" type="extendableAudioDevice" use="required"/> + </xs:complexType> + <xs:complexType name="supportedFormat"> + <xs:annotation> + <xs:documentation xml:lang="en"> + SupportedFormat section: + The details of the short audio descriptor of a specific audio format + supported by the audio device. Attributes as follows: + "format": format enum of the current supported format. + "descriptor": three-byte short audio descriptor for the given format in hex. + </xs:documentation> + </xs:annotation> + <xs:attribute name="format" type="hdmiAudioFormat" use="required"/> + <xs:attribute name="descriptor" type="descriptor" use="required"/> + </xs:complexType> + <xs:simpleType name="descriptor"> + <xs:restriction base="xs:string"> + <xs:pattern value="[a-fA-F0-9]{6}"/> + </xs:restriction> + </xs:simpleType> + <xs:simpleType name="hdmiAudioFormat"> + <xs:restriction base="xs:string"> + <xs:enumeration value="AUDIO_FORMAT_NONE"/> + <xs:enumeration value="AUDIO_FORMAT_LPCM"/> + <xs:enumeration value="AUDIO_FORMAT_DD"/> + <xs:enumeration value="AUDIO_FORMAT_MPEG1"/> + <xs:enumeration value="AUDIO_FORMAT_MP3"/> + <xs:enumeration value="AUDIO_FORMAT_MPEG2"/> + <xs:enumeration value="AUDIO_FORMAT_AAC"/> + <xs:enumeration value="AUDIO_FORMAT_DTS"/> + <xs:enumeration value="AUDIO_FORMAT_ATRAC"/> + <xs:enumeration value="AUDIO_FORMAT_ONEBITAUDIO"/> + <xs:enumeration value="AUDIO_FORMAT_DDP"/> + <xs:enumeration value="AUDIO_FORMAT_DTSHD"/> + <xs:enumeration value="AUDIO_FORMAT_TRUEHD"/> + <xs:enumeration value="AUDIO_FORMAT_DST"/> + <xs:enumeration value="AUDIO_FORMAT_WMAPRO"/> + </xs:restriction> + </xs:simpleType> + <xs:element name="config" type="config"/> +</xs:schema> diff --git a/tv/tuner/1.0/IFrontend.hal b/tv/tuner/1.0/IFrontend.hal index ceda2b32ee..756ab4650e 100644 --- a/tv/tuner/1.0/IFrontend.hal +++ b/tv/tuner/1.0/IFrontend.hal @@ -20,15 +20,16 @@ import IFrontendCallback; import ILnb; /** - * A Tuner Frontend is used to tune to a frequency and lock signal. It provide - * live data feed to Tuner Demux interface. + * A Tuner Frontend is used to tune to a frequency and lock signal. + * + * IFrontend provides a bit stream to the Tuner Demux interface. */ interface IFrontend { /** - * Set the callback + * Set the frontend callback. * - * It is used by the client to receive events from the Frontend. - * Only one callback for one Frontend instance is supported. The callback + * IFrontendCallback is used by the client to receive events from the Frontend. + * Only one callback per IFrontend instance is supported. The callback * will be replaced if it's set again. * * @param callback Callback object to pass Frontend events to the system. @@ -42,14 +43,14 @@ interface IFrontend { setCallback(IFrontendCallback callback) generates (Result result); /** - * Tuning Frontend + * Tunes the frontend to using the settings given. * - * 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. + * This locks the frontend to 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 an async call, with LOCKED or NO_SIGNAL events sent via callback. * - * @param settings Signal delivery information which frontend can use to + * @param settings Signal delivery information the frontend uses to * search and lock the signal. * * @return result Result status of the operation. @@ -60,9 +61,10 @@ interface IFrontend { tune(FrontendSettings settings) generates (Result result); /** - * Stop the tuning + * Stops a previous tuning. * - * It is used by the client to stop a previous tuning. + * If the method completes successfully the frontend is no longer tuned and no data + * will be sent to attached demuxes. * * @return result Result status of the operation. * SUCCESS if successfully stop tuning. @@ -71,10 +73,10 @@ interface IFrontend { stopTune() generates (Result result); /** - * Release the Frontend instance + * Releases 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. + * Associated resources are released. close may be called more than once. + * Calls to any other method after this will return an error * * @return result Result status of the operation. * SUCCESS if successful, @@ -148,7 +150,7 @@ interface IFrontend { setLnb(LnbId lnbId) generates (Result result); /** - * Enble or Disable Low Noise Amplifier (LNA). + * Enable or Disable Low Noise Amplifier (LNA). * * @param bEnable true if activate LNA module; false if deactivate LNA * diff --git a/tv/tuner/1.0/default/service.cpp b/tv/tuner/1.0/default/service.cpp index 581d269b9e..7bbc09e45c 100644 --- a/tv/tuner/1.0/default/service.cpp +++ b/tv/tuner/1.0/default/service.cpp @@ -21,7 +21,6 @@ #define LOG_TAG "android.hardware.tv.tuner@1.0-service" #endif -#include <binder/ProcessState.h> #include <hidl/HidlTransportSupport.h> #include <hidl/LegacySupport.h> @@ -46,8 +45,8 @@ int main() { android::sp<ITuner> service = new Tuner(); 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/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal index a0cf0d9e6e..fa00a6e923 100644 --- a/tv/tuner/1.0/types.hal +++ b/tv/tuner/1.0/types.hal @@ -63,7 +63,7 @@ enum FrontendType : uint32_t { DVBS, /** * Digital Video Broadcasting - Terrestrial - * DVB Terresttrial Frontend Standard ETSI EN 300 468 V1.15.1 and + * DVB Terrestrial Frontend Standard ETSI EN 300 468 V1.15.1 and * ETSI EN 302 755 V1.4.1. */ DVBT, @@ -255,7 +255,7 @@ enum FrontendAtscModulation : uint32_t { }; /** - * Signal Setting for ATSC Frontend. + * Signal Settings for an ATSC Frontend. */ struct FrontendAtscSettings { /** @@ -868,7 +868,7 @@ enum FrontendIsdbsRolloff : uint32_t { }; /** - * Modulaltion Type for ISDBS. + * Modulation Type for ISDBS. */ @export enum FrontendIsdbsModulation : uint32_t { diff --git a/tv/tuner/README.md b/tv/tuner/README.md new file mode 100644 index 0000000000..aa1f62d422 --- /dev/null +++ b/tv/tuner/README.md @@ -0,0 +1,12 @@ +# Tuner HALs + +## Overview + +TV specific tuners. + +See 1.0/ITuner.hal for an overview. + +*** note +**Warning:** The HALs are not (yet) frozen, as the HAL definition is +expected to evolve between Android releases. +*** diff --git a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl new file mode 100644 index 0000000000..84556b57bb --- /dev/null +++ b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl @@ -0,0 +1,28 @@ +/* + * 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.vibrator; + +import android.hardware.vibrator.CompositePrimitive; + +@VintfStability +parcelable CompositeEffect { + /* Period of silence preceding primitive. */ + int delayMs; + CompositePrimitive primitive; + /* 0.0 (exclusive) - 1.0 (inclusive) */ + float scale; +} diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl new file mode 100644 index 0000000000..2a9d0be31d --- /dev/null +++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl @@ -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. + */ + +package android.hardware.vibrator; + +@VintfStability +@Backing(type="int") +enum CompositePrimitive { + NOOP, + CLICK, + THUD, + SPIN, + QUICK_RISE, + SLOW_RISE, + QUICK_FALL, +} diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl index 8c4fd055c3..ebf5faa47a 100644 --- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl +++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl @@ -19,6 +19,8 @@ package android.hardware.vibrator; import android.hardware.vibrator.IVibratorCallback; import android.hardware.vibrator.Effect; import android.hardware.vibrator.EffectStrength; +import android.hardware.vibrator.CompositeEffect; +import android.hardware.vibrator.CompositePrimitive; @VintfStability interface IVibrator { @@ -42,6 +44,10 @@ interface IVibrator { * Whether setAmplitude is supported (when external control is enabled) */ const int CAP_EXTERNAL_AMPLITUDE_CONTROL = 1 << 4; + /** + * Whether compose is supported. + */ + const int CAP_COMPOSE_EFFECTS = 1 << 5; /** * Determine capabilities of the vibrator HAL (CAP_* mask) @@ -107,11 +113,10 @@ interface IVibrator { * CAP_EXTERNAL_AMPLITUDE_CONTROL. * * @param amplitude The unitless force setting. Note that this number must - * be between 1 and 255, inclusive. If the motor does not - * have exactly 255 steps, it must do it's best to map it - * onto the number of steps it does have. + * be between 0.0 (exclusive) and 1.0 (inclusive). It must + * do it's best to map it onto the number of steps it does have. */ - void setAmplitude(in int amplitude); + void setAmplitude(in float amplitude); /** * Enables/disables control override of vibrator to audio. @@ -128,4 +133,36 @@ interface IVibrator { * @param enabled Whether external control should be enabled or disabled. */ void setExternalControl(in boolean enabled); + + /** + * Retrieve composition delay limit. + * + * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS). + * + * @return Maximum delay for a single CompositeEffect[] entry. + */ + int getCompositionDelayMax(); + + /** + * Retrieve composition size limit. + * + * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS). + * + * @return Maximum number of entries in CompositeEffect[]. + * @param maxDelayMs Maximum delay for a single CompositeEffect[] entry. + */ + int getCompositionSizeMax(); + + /** + * Fire off a string of effect primitives, combined to perform richer effects. + * + * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS). + * + * Doing this operation while the vibrator is already on is undefined behavior. Clients should + * explicitly call off. + * + * @param composite Array of composition parameters. + */ + void compose(in CompositeEffect[] composite, in IVibratorCallback callback); + } diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp index 09cd2345bf..a77c49a0a2 100644 --- a/vibrator/aidl/default/Vibrator.cpp +++ b/vibrator/aidl/default/Vibrator.cpp @@ -24,11 +24,14 @@ namespace android { namespace hardware { namespace vibrator { +static constexpr int32_t kComposeDelayMaxMs = 1000; +static constexpr int32_t kComposeSizeMax = 256; + ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) { LOG(INFO) << "Vibrator reporting capabilities"; *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK | IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL | - IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL; + IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS; return ndk::ScopedAStatus::ok(); } @@ -84,9 +87,9 @@ ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_retu return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus Vibrator::setAmplitude(int32_t amplitude) { +ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) { LOG(INFO) << "Vibrator set amplitude: " << amplitude; - if (amplitude <= 0 || amplitude > 255) { + if (amplitude <= 0.0f || amplitude > 1.0f) { return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT)); } return ndk::ScopedAStatus::ok(); @@ -97,6 +100,55 @@ ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) { + *maxDelayMs = kComposeDelayMaxMs; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) { + *maxSize = kComposeSizeMax; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite, + const std::shared_ptr<IVibratorCallback>& callback) { + if (composite.size() > kComposeSizeMax) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + for (auto& e : composite) { + if (e.delayMs > kComposeDelayMaxMs) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (e.scale <= 0.0f || e.scale > 1.0f) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (e.primitive < CompositePrimitive::NOOP || + e.primitive > CompositePrimitive::QUICK_FALL) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + } + + std::thread([=] { + LOG(INFO) << "Starting compose on another thread"; + + for (auto& e : composite) { + if (e.delayMs) { + usleep(e.delayMs * 1000); + } + LOG(INFO) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale " + << e.scale; + } + + if (callback != nullptr) { + LOG(INFO) << "Notifying perform complete"; + callback->onComplete(); + } + }).detach(); + + return ndk::ScopedAStatus::ok(); +} + } // namespace vibrator } // namespace hardware } // namespace android diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h index 14e7292b43..817ec805fa 100644 --- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h +++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h @@ -32,8 +32,12 @@ class Vibrator : public BnVibrator { const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) override; ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _aidl_return) override; - ndk::ScopedAStatus setAmplitude(int32_t amplitude) override; + ndk::ScopedAStatus setAmplitude(float amplitude) override; ndk::ScopedAStatus setExternalControl(bool enabled) override; + ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs); + ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize); + ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite, + const std::shared_ptr<IVibratorCallback>& callback) override; }; } // namespace vibrator diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp index b6aa9e2fd0..5c6120b6f5 100644 --- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp +++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp @@ -28,6 +28,8 @@ using android::sp; using android::String16; using android::binder::Status; using android::hardware::vibrator::BnVibratorCallback; +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; using android::hardware::vibrator::Effect; using android::hardware::vibrator::EffectStrength; using android::hardware::vibrator::IVibrator; @@ -55,6 +57,20 @@ const std::vector<EffectStrength> kInvalidEffectStrengths = { static_cast<EffectStrength>(static_cast<int8_t>(kEffectStrengths.back()) + 1), }; +// TODO(b/143992652): autogenerate +const std::vector<CompositePrimitive> kCompositePrimitives = { + CompositePrimitive::NOOP, CompositePrimitive::CLICK, + CompositePrimitive::THUD, CompositePrimitive::SPIN, + CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE, + CompositePrimitive::QUICK_FALL, +}; +// TODO(b/143992652): autogenerate + +const std::vector<CompositePrimitive> kInvalidPrimitives = { + static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.front()) - 1), + static_cast<CompositePrimitive>(static_cast<int32_t>(kCompositePrimitives.back()) + 1), +}; + class CompletionCallback : public BnVibratorCallback { public: CompletionCallback(const std::function<void()>& callback) : mCallback(callback) {} @@ -201,11 +217,11 @@ TEST_P(VibratorAidl, InvalidEffectsUnsupported) { TEST_P(VibratorAidl, ChangeVibrationAmplitude) { if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) { - EXPECT_TRUE(vibrator->setAmplitude(1).isOk()); + EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(0.1f).exceptionCode()); EXPECT_TRUE(vibrator->on(2000, nullptr /*callback*/).isOk()); - EXPECT_TRUE(vibrator->setAmplitude(128).isOk()); + EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(0.5f).exceptionCode()); sleep(1); - EXPECT_TRUE(vibrator->setAmplitude(255).isOk()); + EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(1.0f).exceptionCode()); sleep(1); } } @@ -214,7 +230,7 @@ TEST_P(VibratorAidl, AmplitudeOutsideRangeFails) { if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) { EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(-1).exceptionCode()); EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(0).exceptionCode()); - EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(256).exceptionCode()); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(1.1).exceptionCode()); } } @@ -240,7 +256,7 @@ TEST_P(VibratorAidl, ExternalAmplitudeControl) { if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) { EXPECT_TRUE(vibrator->setExternalControl(true).isOk()); - Status amplitudeStatus = vibrator->setAmplitude(128); + Status amplitudeStatus = vibrator->setAmplitude(0.5); if (supportsExternalAmplitudeControl) { EXPECT_TRUE(amplitudeStatus.isOk()); } else { @@ -259,6 +275,102 @@ TEST_P(VibratorAidl, ExternalControlUnsupportedMatchingCapabilities) { } } +TEST_P(VibratorAidl, ComposeValidPrimitives) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + int32_t maxDelay, maxSize; + + EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionDelayMax(&maxDelay).exceptionCode()); + EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionSizeMax(&maxSize).exceptionCode()); + + std::vector<CompositeEffect> composite; + + for (auto primitive : kCompositePrimitives) { + CompositeEffect effect; + + effect.delayMs = std::rand() % (maxDelay + 1); + effect.primitive = primitive; + effect.scale = static_cast<float>(std::rand()) / RAND_MAX ?: 1.0f; + composite.emplace_back(effect); + + if (composite.size() == maxSize) { + EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode()); + composite.clear(); + vibrator->off(); + } + } + + if (composite.size() != 0) { + EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode()); + vibrator->off(); + } + } +} + +TEST_P(VibratorAidl, ComposeUnsupportedPrimitives) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + for (auto primitive : kInvalidPrimitives) { + std::vector<CompositeEffect> composite(1); + + for (auto& effect : composite) { + effect.delayMs = 0; + effect.primitive = primitive; + effect.scale = 1.0f; + } + EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, + vibrator->compose(composite, nullptr).exceptionCode()); + vibrator->off(); + } + } +} + +TEST_P(VibratorAidl, CompseDelayBoundary) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + int32_t maxDelay; + + EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionDelayMax(&maxDelay).exceptionCode()); + + std::vector<CompositeEffect> composite(1); + CompositeEffect effect; + + effect.delayMs = 1; + effect.primitive = CompositePrimitive::CLICK; + effect.scale = 1.0f; + + std::fill(composite.begin(), composite.end(), effect); + EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode()); + + effect.delayMs = maxDelay + 1; + + std::fill(composite.begin(), composite.end(), effect); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, + vibrator->compose(composite, nullptr).exceptionCode()); + vibrator->off(); + } +} + +TEST_P(VibratorAidl, CompseSizeBoundary) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + int32_t maxSize; + + EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionSizeMax(&maxSize).exceptionCode()); + + std::vector<CompositeEffect> composite(maxSize); + CompositeEffect effect; + + effect.delayMs = 1; + effect.primitive = CompositePrimitive::CLICK; + effect.scale = 1.0f; + + std::fill(composite.begin(), composite.end(), effect); + EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode()); + + composite.emplace_back(effect); + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, + vibrator->compose(composite, nullptr).exceptionCode()); + vibrator->off(); + } +} + INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl, testing::ValuesIn(android::getAidlHalInstanceNames(IVibrator::descriptor)), android::PrintInstanceNameToString); diff --git a/wifi/1.0/vts/functional/Android.bp b/wifi/1.0/vts/functional/Android.bp index 95bb59cc91..bf77503c93 100644 --- a/wifi/1.0/vts/functional/Android.bp +++ b/wifi/1.0/vts/functional/Android.bp @@ -39,7 +39,6 @@ cc_test { defaults: ["VtsHalTargetTestDefaults"], srcs: [ "VtsHalWifiV1_0TargetTest.cpp", - "wifi_ap_iface_hidl_test.cpp", "wifi_chip_hidl_test.cpp", "wifi_p2p_iface_hidl_test.cpp", "wifi_rtt_controller_hidl_test.cpp", @@ -53,14 +52,17 @@ cc_test { "android.hardware.wifi@1.3", "libwifi-system-iface" ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts-core"], } +// These tests are split out so that they can be conditioned on presence of the +// "android.hardware.wifi.aware" feature. cc_test { name: "VtsHalWifiNanV1_0TargetTest", defaults: ["VtsHalTargetTestDefaults"], srcs: [ "VtsHalWifiV1_0TargetTest.cpp", + "wifi_chip_hidl_nan_test.cpp", "wifi_nan_iface_hidl_test.cpp", ], static_libs: [ @@ -68,5 +70,23 @@ cc_test { "android.hardware.wifi@1.0", "libwifi-system-iface" ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts-core"], +} + +// These tests are split out so that they can be conditioned on presence of +// the hostapd HAL, which indicates SoftAP support. +cc_test { + name: "VtsHalWifiApV1_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "VtsHalWifiV1_0TargetTest.cpp", + "wifi_ap_iface_hidl_test.cpp", + "wifi_chip_hidl_ap_test.cpp", + ], + static_libs: [ + "VtsHalWifiV1_0TargetTestUtil", + "android.hardware.wifi@1.0", + "libwifi-system-iface" + ], + test_suites: ["general-tests", "vts-core"], } diff --git a/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp b/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp index e7b85938cf..128dae5a87 100644 --- a/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp +++ b/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp @@ -14,37 +14,8 @@ * limitations under the License. */ -#include <android-base/logging.h> +#include <VtsHalHidlTargetTestEnvBase.h> -#include "wifi_hidl_test_utils.h" - -class WifiVtsHidlEnvironment_1_0 : public WifiHidlEnvironment { - public: - // get the test environment singleton - static WifiVtsHidlEnvironment_1_0* Instance() { - static WifiVtsHidlEnvironment_1_0* instance = - new WifiVtsHidlEnvironment_1_0; - return instance; - } - - virtual void registerTestServices() override { - registerTestService<android::hardware::wifi::V1_0::IWifi>(); - } - - private: - WifiVtsHidlEnvironment_1_0() {} -}; - -WifiHidlEnvironment* gEnv = WifiVtsHidlEnvironment_1_0::Instance(); - -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(gEnv); - ::testing::InitGoogleTest(&argc, argv); - gEnv->init(&argc, argv); - int status = gEnv->initFromOptions(argc, argv); - if (status == 0) { - status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - } - return status; -} +// TODO(b/143892896): Remove this file after wifi_hidl_test_utils.cpp is +// updated. +::testing::VtsHalHidlTargetTestEnvBase* gEnv = nullptr;
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp index e5762f28f0..8be8a0cb76 100644 --- a/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp +++ b/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp @@ -16,39 +16,37 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.0/IWifi.h> #include <android/hardware/wifi/1.0/IWifiApIface.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" +using ::android::sp; using ::android::hardware::wifi::V1_0::IfaceType; +using ::android::hardware::wifi::V1_0::IWifi; using ::android::hardware::wifi::V1_0::IWifiApIface; using ::android::hardware::wifi::V1_0::WifiBand; using ::android::hardware::wifi::V1_0::WifiStatusCode; -using ::android::sp; - -extern WifiHidlEnvironment* gEnv; /** * Fixture to use for all AP Iface HIDL interface tests. */ -class WifiApIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiApIfaceHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - if (!gEnv->isSoftApOn) return; - wifi_ap_iface_ = getWifiApIface(); + wifi_ap_iface_ = getWifiApIface(GetInstanceName()); ASSERT_NE(nullptr, wifi_ap_iface_.get()); } - virtual void TearDown() override { - if (!gEnv->isSoftApOn) return; - stopWifi(); - } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: sp<IWifiApIface> wifi_ap_iface_; + std::string GetInstanceName() { return GetParam(); } }; /* @@ -56,18 +54,15 @@ class WifiApIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifiApIface proxy object is * successfully created. */ -TEST(WifiApIfaceHidlTestNoFixture, Create) { - if (!gEnv->isSoftApOn) return; - EXPECT_NE(nullptr, getWifiApIface().get()); - stopWifi(); +TEST_P(WifiApIfaceHidlTest, Create) { + // The creation of a proxy object is tested as part of SetUp method. } /* * GetType: * Ensures that the correct interface type is returned for AP interface. */ -TEST_F(WifiApIfaceHidlTest, GetType) { - if (!gEnv->isSoftApOn) return; +TEST_P(WifiApIfaceHidlTest, GetType) { const auto& status_and_type = HIDL_INVOKE(wifi_ap_iface_, getType); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_type.first.code); EXPECT_EQ(IfaceType::AP, status_and_type.second); @@ -78,8 +73,7 @@ TEST_F(WifiApIfaceHidlTest, GetType) { * Ensures that a call to set the country code will return with a success * status code. */ -TEST_F(WifiApIfaceHidlTest, SetCountryCode) { - if (!gEnv->isSoftApOn) return; +TEST_P(WifiApIfaceHidlTest, SetCountryCode) { const android::hardware::hidl_array<int8_t, 2> kCountryCode{ std::array<int8_t, 2>{{0x55, 0x53}}}; EXPECT_EQ(WifiStatusCode::SUCCESS, @@ -90,10 +84,15 @@ TEST_F(WifiApIfaceHidlTest, SetCountryCode) { * GetValidFrequenciesForBand: * Ensures that we can retrieve valid frequencies for 2.4 GHz band. */ -TEST_F(WifiApIfaceHidlTest, GetValidFrequenciesForBand) { - if (!gEnv->isSoftApOn) return; +TEST_P(WifiApIfaceHidlTest, GetValidFrequenciesForBand) { const auto& status_and_freqs = HIDL_INVOKE( wifi_ap_iface_, getValidFrequenciesForBand, WifiBand::BAND_24GHZ); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_freqs.first.code); EXPECT_GT(status_and_freqs.second.size(), 0u); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiApIfaceHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_ap_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_ap_test.cpp new file mode 100644 index 0000000000..33817d5f95 --- /dev/null +++ b/wifi/1.0/vts/functional/wifi_chip_hidl_ap_test.cpp @@ -0,0 +1,180 @@ +/* + * 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/wifi/1.0/IWifi.h> +#include <android/hardware/wifi/1.0/IWifiChip.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include "wifi_hidl_call_util.h" +#include "wifi_hidl_test_utils.h" + +using ::android::sp; +using ::android::hardware::wifi::V1_0::ChipModeId; +using ::android::hardware::wifi::V1_0::IfaceType; +using ::android::hardware::wifi::V1_0::IWifi; +using ::android::hardware::wifi::V1_0::IWifiApIface; +using ::android::hardware::wifi::V1_0::IWifiChip; +using ::android::hardware::wifi::V1_0::IWifiIface; +using ::android::hardware::wifi::V1_0::WifiStatus; +using ::android::hardware::wifi::V1_0::WifiStatusCode; + +/** + * Fixture for IWifiChip tests that are conditioned on SoftAP support. + */ +class WifiChipHidlApTest : public ::testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + wifi_chip_ = getWifiChip(GetInstanceName()); + ASSERT_NE(nullptr, wifi_chip_.get()); + } + + virtual void TearDown() override { stopWifi(GetInstanceName()); } + + protected: + // Helper function to configure the Chip in one of the supported modes. + // Most of the non-mode-configuration-related methods require chip + // to be first configured. + ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) { + ChipModeId mode_id; + EXPECT_EQ(expectSuccess, + configureChipToSupportIfaceType(wifi_chip_, type, &mode_id)); + return mode_id; + } + + std::string getIfaceName(const sp<IWifiIface>& iface) { + const auto& status_and_name = HIDL_INVOKE(iface, getName); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_name.first.code); + return status_and_name.second; + } + + WifiStatusCode createApIface(sp<IWifiApIface>* ap_iface) { + const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createApIface); + *ap_iface = status_and_iface.second; + return status_and_iface.first.code; + } + + WifiStatusCode removeApIface(const std::string& name) { + return HIDL_INVOKE(wifi_chip_, removeApIface, name).code; + } + + sp<IWifiChip> wifi_chip_; + + private: + std::string GetInstanceName() { return GetParam(); } +}; + +/* + * CreateApIface + * Configures the chip in AP mode and ensures that at least 1 iface creation + * succeeds. + */ +TEST_P(WifiChipHidlApTest, CreateApIface) { + configureChipForIfaceType(IfaceType::AP, true); + + sp<IWifiApIface> iface; + EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface)); + EXPECT_NE(nullptr, iface.get()); +} + +/* + * GetApIfaceNames + * Configures the chip in AP mode and ensures that the iface list is empty + * before creating the iface. Then, create the iface and ensure that + * iface name is returned via the list. + */ +TEST_P(WifiChipHidlApTest, GetApIfaceNames) { + configureChipForIfaceType(IfaceType::AP, true); + + const auto& status_and_iface_names1 = + HIDL_INVOKE(wifi_chip_, getApIfaceNames); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code); + EXPECT_EQ(0u, status_and_iface_names1.second.size()); + + sp<IWifiApIface> iface; + EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface)); + EXPECT_NE(nullptr, iface.get()); + + std::string iface_name = getIfaceName(iface); + const auto& status_and_iface_names2 = + HIDL_INVOKE(wifi_chip_, getApIfaceNames); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code); + EXPECT_EQ(1u, status_and_iface_names2.second.size()); + EXPECT_EQ(iface_name, status_and_iface_names2.second[0]); + + EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name)); + const auto& status_and_iface_names3 = + HIDL_INVOKE(wifi_chip_, getApIfaceNames); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code); + EXPECT_EQ(0u, status_and_iface_names3.second.size()); +} + +/* + * GetApIface + * Configures the chip in AP mode and create an iface. Then, retrieve + * the iface object using the correct name and ensure any other name + * doesn't retrieve an iface object. + */ +TEST_P(WifiChipHidlApTest, GetApIface) { + configureChipForIfaceType(IfaceType::AP, true); + + sp<IWifiApIface> ap_iface; + EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface)); + EXPECT_NE(nullptr, ap_iface.get()); + + std::string iface_name = getIfaceName(ap_iface); + const auto& status_and_iface1 = + HIDL_INVOKE(wifi_chip_, getApIface, iface_name); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code); + EXPECT_NE(nullptr, status_and_iface1.second.get()); + + std::string invalid_name = iface_name + "0"; + const auto& status_and_iface2 = + HIDL_INVOKE(wifi_chip_, getApIface, invalid_name); + EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code); + EXPECT_EQ(nullptr, status_and_iface2.second.get()); +} + +/* + * RemoveApIface + * Configures the chip in AP mode and create an iface. Then, remove + * the iface object using the correct name and ensure any other name + * doesn't remove the iface. + */ +TEST_P(WifiChipHidlApTest, RemoveApIface) { + configureChipForIfaceType(IfaceType::AP, true); + + sp<IWifiApIface> ap_iface; + EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface)); + EXPECT_NE(nullptr, ap_iface.get()); + + std::string iface_name = getIfaceName(ap_iface); + std::string invalid_name = iface_name + "0"; + EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(invalid_name)); + EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name)); + + // No such iface exists now. So, this should return failure. + EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(iface_name)); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiChipHidlApTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp new file mode 100644 index 0000000000..95f223d5de --- /dev/null +++ b/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.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/wifi/1.0/IWifi.h> +#include <android/hardware/wifi/1.0/IWifiChip.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include "wifi_hidl_call_util.h" +#include "wifi_hidl_test_utils.h" + +using ::android::sp; +using ::android::hardware::wifi::V1_0::ChipModeId; +using ::android::hardware::wifi::V1_0::IfaceType; +using ::android::hardware::wifi::V1_0::IWifi; +using ::android::hardware::wifi::V1_0::IWifiChip; +using ::android::hardware::wifi::V1_0::IWifiIface; +using ::android::hardware::wifi::V1_0::IWifiNanIface; +using ::android::hardware::wifi::V1_0::WifiStatus; +using ::android::hardware::wifi::V1_0::WifiStatusCode; + +/** + * Fixture for IWifiChip tests that are conditioned on NAN support. + */ +class WifiChipHidlNanTest : public ::testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + wifi_chip_ = getWifiChip(GetInstanceName()); + ASSERT_NE(nullptr, wifi_chip_.get()); + } + + virtual void TearDown() override { stopWifi(GetInstanceName()); } + + protected: + // Helper function to configure the Chip in one of the supported modes. + // Most of the non-mode-configuration-related methods require chip + // to be first configured. + ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) { + ChipModeId mode_id; + EXPECT_EQ(expectSuccess, + configureChipToSupportIfaceType(wifi_chip_, type, &mode_id)); + return mode_id; + } + + std::string getIfaceName(const sp<IWifiIface>& iface) { + const auto& status_and_name = HIDL_INVOKE(iface, getName); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_name.first.code); + return status_and_name.second; + } + + WifiStatusCode createNanIface(sp<IWifiNanIface>* nan_iface) { + const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createNanIface); + *nan_iface = status_and_iface.second; + return status_and_iface.first.code; + } + + WifiStatusCode removeNanIface(const std::string& name) { + return HIDL_INVOKE(wifi_chip_, removeNanIface, name).code; + } + + sp<IWifiChip> wifi_chip_; + + private: + std::string GetInstanceName() { return GetParam(); } +}; + +/* + * CreateNanIface + * Configures the chip in NAN mode and ensures that at least 1 iface creation + * succeeds. + */ +TEST_P(WifiChipHidlNanTest, CreateNanIface) { + configureChipForIfaceType(IfaceType::NAN, true); + + sp<IWifiNanIface> iface; + ASSERT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface)); + EXPECT_NE(nullptr, iface.get()); +} + +/* + * GetNanIfaceNames + * Configures the chip in NAN mode and ensures that the iface list is empty + * before creating the iface. Then, create the iface and ensure that + * iface name is returned via the list. + */ +TEST_P(WifiChipHidlNanTest, GetNanIfaceNames) { + configureChipForIfaceType(IfaceType::NAN, true); + + const auto& status_and_iface_names1 = + HIDL_INVOKE(wifi_chip_, getNanIfaceNames); + ASSERT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code); + EXPECT_EQ(0u, status_and_iface_names1.second.size()); + + sp<IWifiNanIface> iface; + EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface)); + EXPECT_NE(nullptr, iface.get()); + + std::string iface_name = getIfaceName(iface); + const auto& status_and_iface_names2 = + HIDL_INVOKE(wifi_chip_, getNanIfaceNames); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code); + EXPECT_EQ(1u, status_and_iface_names2.second.size()); + EXPECT_EQ(iface_name, status_and_iface_names2.second[0]); + + EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name)); + const auto& status_and_iface_names3 = + HIDL_INVOKE(wifi_chip_, getNanIfaceNames); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code); + EXPECT_EQ(0u, status_and_iface_names3.second.size()); +} + +/* + * GetNanIface + * Configures the chip in NAN mode and create an iface. Then, retrieve + * the iface object using the correct name and ensure any other name + * doesn't retrieve an iface object. + */ +TEST_P(WifiChipHidlNanTest, GetNanIface) { + configureChipForIfaceType(IfaceType::NAN, true); + + sp<IWifiNanIface> nan_iface; + EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface)); + EXPECT_NE(nullptr, nan_iface.get()); + + std::string iface_name = getIfaceName(nan_iface); + const auto& status_and_iface1 = + HIDL_INVOKE(wifi_chip_, getNanIface, iface_name); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code); + EXPECT_NE(nullptr, status_and_iface1.second.get()); + + std::string invalid_name = iface_name + "0"; + const auto& status_and_iface2 = + HIDL_INVOKE(wifi_chip_, getNanIface, invalid_name); + EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code); + EXPECT_EQ(nullptr, status_and_iface2.second.get()); +} + +/* + * RemoveNanIface + * Configures the chip in NAN mode and create an iface. Then, remove + * the iface object using the correct name and ensure any other name + * doesn't remove the iface. + */ +TEST_P(WifiChipHidlNanTest, RemoveNanIface) { + configureChipForIfaceType(IfaceType::NAN, true); + + sp<IWifiNanIface> nan_iface; + EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface)); + EXPECT_NE(nullptr, nan_iface.get()); + + std::string iface_name = getIfaceName(nan_iface); + std::string invalid_name = iface_name + "0"; + EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(invalid_name)); + + EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name)); + + // No such iface exists now. So, this should return failure. + EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(iface_name)); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiChipHidlNanTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp index 1b7e821906..ec96fcf50c 100644 --- a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp +++ b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp @@ -16,10 +16,12 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.0/IWifi.h> #include <android/hardware/wifi/1.0/IWifiChip.h> #include <android/hardware/wifi/1.3/IWifiChip.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" @@ -27,21 +29,20 @@ using ::android::sp; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; -using ::android::hardware::wifi::V1_0::IfaceType; using ::android::hardware::wifi::V1_0::ChipId; using ::android::hardware::wifi::V1_0::ChipModeId; -using ::android::hardware::wifi::V1_0::WifiDebugRingBufferStatus; -using ::android::hardware::wifi::V1_0::WifiDebugRingBufferVerboseLevel; -using ::android::hardware::wifi::V1_0::WifiDebugHostWakeReasonStats; -using ::android::hardware::wifi::V1_0::WifiStatus; -using ::android::hardware::wifi::V1_0::WifiStatusCode; +using ::android::hardware::wifi::V1_0::IfaceType; +using ::android::hardware::wifi::V1_0::IWifi; using ::android::hardware::wifi::V1_0::IWifiChip; -using ::android::hardware::wifi::V1_0::IWifiApIface; using ::android::hardware::wifi::V1_0::IWifiIface; -using ::android::hardware::wifi::V1_0::IWifiNanIface; using ::android::hardware::wifi::V1_0::IWifiP2pIface; using ::android::hardware::wifi::V1_0::IWifiRttController; using ::android::hardware::wifi::V1_0::IWifiStaIface; +using ::android::hardware::wifi::V1_0::WifiDebugHostWakeReasonStats; +using ::android::hardware::wifi::V1_0::WifiDebugRingBufferStatus; +using ::android::hardware::wifi::V1_0::WifiDebugRingBufferVerboseLevel; +using ::android::hardware::wifi::V1_0::WifiStatus; +using ::android::hardware::wifi::V1_0::WifiStatusCode; extern WifiHidlEnvironment* gEnv; @@ -64,16 +65,19 @@ bool hasAnyRingBufferCapabilities(uint32_t caps) { } // namespace /** - * Fixture to use for all Wifi chip HIDL interface tests. + * Fixture for IWifiChip tests. + * + * Tests that require SoftAP or NAN support should go into WifiChipHidlApTest or + * WifiChipHidlNanTest respectively. */ -class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiChipHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - wifi_chip_ = getWifiChip(); + wifi_chip_ = getWifiChip(GetInstanceName()); ASSERT_NE(nullptr, wifi_chip_.get()); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: // Helper function to configure the Chip in one of the supported modes. @@ -114,26 +118,6 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { return status_and_name.second; } - WifiStatusCode createApIface(sp<IWifiApIface>* ap_iface) { - const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createApIface); - *ap_iface = status_and_iface.second; - return status_and_iface.first.code; - } - - WifiStatusCode removeApIface(const std::string& name) { - return HIDL_INVOKE(wifi_chip_, removeApIface, name).code; - } - - WifiStatusCode createNanIface(sp<IWifiNanIface>* nan_iface) { - const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createNanIface); - *nan_iface = status_and_iface.second; - return status_and_iface.first.code; - } - - WifiStatusCode removeNanIface(const std::string& name) { - return HIDL_INVOKE(wifi_chip_, removeNanIface, name).code; - } - WifiStatusCode createP2pIface(sp<IWifiP2pIface>* p2p_iface) { const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createP2pIface); *p2p_iface = status_and_iface.second; @@ -155,6 +139,9 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { } sp<IWifiChip> wifi_chip_; + + protected: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -162,15 +149,14 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifiChip proxy object is * successfully created. */ -TEST(WifiChipHidlTestNoFixture, Create) { - EXPECT_NE(nullptr, getWifiChip().get()); - stopWifi(); +TEST_P(WifiChipHidlTest, Create) { + // The creation of a proxy object is tested as part of SetUp method. } /* * GetId: */ -TEST_F(WifiChipHidlTest, GetId) { +TEST_P(WifiChipHidlTest, GetId) { EXPECT_EQ(WifiStatusCode::SUCCESS, HIDL_INVOKE(wifi_chip_, getId).first.code); } @@ -178,7 +164,7 @@ TEST_F(WifiChipHidlTest, GetId) { /* * GetAvailableMode: */ -TEST_F(WifiChipHidlTest, GetAvailableModes) { +TEST_P(WifiChipHidlTest, GetAvailableModes) { const auto& status_and_modes = HIDL_INVOKE(wifi_chip_, getAvailableModes); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_modes.first.code); EXPECT_LT(0u, status_and_modes.second.size()); @@ -187,17 +173,17 @@ TEST_F(WifiChipHidlTest, GetAvailableModes) { /* * ConfigureChip: */ -TEST_F(WifiChipHidlTest, ConfigureChip) { +TEST_P(WifiChipHidlTest, ConfigureChip) { const auto& status_and_modes = HIDL_INVOKE(wifi_chip_, getAvailableModes); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_modes.first.code); EXPECT_LT(0u, status_and_modes.second.size()); for (const auto& mode : status_and_modes.second) { // configureChip() requires to be called with a fresh IWifiChip object. - wifi_chip_ = getWifiChip(); + wifi_chip_ = getWifiChip(GetInstanceName()); ASSERT_NE(nullptr, wifi_chip_.get()); EXPECT_EQ(WifiStatusCode::SUCCESS, HIDL_INVOKE(wifi_chip_, configureChip, mode.id).code); - stopWifi(); + stopWifi(GetInstanceName()); // Sleep for 5 milliseconds between each wifi state toggle. usleep(5000); } @@ -206,7 +192,7 @@ TEST_F(WifiChipHidlTest, ConfigureChip) { /* * GetCapabilities: */ -TEST_F(WifiChipHidlTest, GetCapabilities) { +TEST_P(WifiChipHidlTest, GetCapabilities) { configureChipForIfaceType(IfaceType::STA, true); const auto& status_and_caps = HIDL_INVOKE(wifi_chip_, getCapabilities); if (status_and_caps.first.code != WifiStatusCode::SUCCESS) { @@ -219,7 +205,7 @@ TEST_F(WifiChipHidlTest, GetCapabilities) { /* * GetMode: */ -TEST_F(WifiChipHidlTest, GetMode) { +TEST_P(WifiChipHidlTest, GetMode) { ChipModeId chip_mode_id = configureChipForIfaceType(IfaceType::STA, true); const auto& status_and_mode = HIDL_INVOKE(wifi_chip_, getMode); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_mode.first.code); @@ -229,7 +215,7 @@ TEST_F(WifiChipHidlTest, GetMode) { /* * RequestChipDebugInfo: */ -TEST_F(WifiChipHidlTest, RequestChipDebugInfo) { +TEST_P(WifiChipHidlTest, RequestChipDebugInfo) { configureChipForIfaceType(IfaceType::STA, true); const auto& status_and_chip_info = HIDL_INVOKE(wifi_chip_, requestChipDebugInfo); @@ -241,7 +227,7 @@ TEST_F(WifiChipHidlTest, RequestChipDebugInfo) { /* * RequestFirmwareDebugDump */ -TEST_F(WifiChipHidlTest, RequestFirmwareDebugDump) { +TEST_P(WifiChipHidlTest, RequestFirmwareDebugDump) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status_and_firmware_dump = HIDL_INVOKE(wifi_chip_, requestFirmwareDebugDump); @@ -256,7 +242,7 @@ TEST_F(WifiChipHidlTest, RequestFirmwareDebugDump) { /* * RequestDriverDebugDump */ -TEST_F(WifiChipHidlTest, RequestDriverDebugDump) { +TEST_P(WifiChipHidlTest, RequestDriverDebugDump) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status_and_driver_dump = HIDL_INVOKE(wifi_chip_, requestDriverDebugDump); @@ -273,7 +259,7 @@ TEST_F(WifiChipHidlTest, RequestDriverDebugDump) { /* * GetDebugRingBuffersStatus */ -TEST_F(WifiChipHidlTest, GetDebugRingBuffersStatus) { +TEST_P(WifiChipHidlTest, GetDebugRingBuffersStatus) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status_and_ring_buffer_status = HIDL_INVOKE(wifi_chip_, getDebugRingBuffersStatus); @@ -292,7 +278,7 @@ TEST_F(WifiChipHidlTest, GetDebugRingBuffersStatus) { /* * StartLoggingToDebugRingBuffer */ -TEST_F(WifiChipHidlTest, StartLoggingToDebugRingBuffer) { +TEST_P(WifiChipHidlTest, StartLoggingToDebugRingBuffer) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); std::string ring_name; const auto& status_and_ring_buffer_status = @@ -320,7 +306,7 @@ TEST_F(WifiChipHidlTest, StartLoggingToDebugRingBuffer) { /* * ForceDumpToDebugRingBuffer */ -TEST_F(WifiChipHidlTest, ForceDumpToDebugRingBuffer) { +TEST_P(WifiChipHidlTest, ForceDumpToDebugRingBuffer) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); std::string ring_name; const auto& status_and_ring_buffer_status = @@ -346,7 +332,7 @@ TEST_F(WifiChipHidlTest, ForceDumpToDebugRingBuffer) { /* * GetDebugHostWakeReasonStats */ -TEST_F(WifiChipHidlTest, GetDebugHostWakeReasonStats) { +TEST_P(WifiChipHidlTest, GetDebugHostWakeReasonStats) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status_and_debug_wake_reason = HIDL_INVOKE(wifi_chip_, getDebugHostWakeReasonStats); @@ -360,206 +346,11 @@ TEST_F(WifiChipHidlTest, GetDebugHostWakeReasonStats) { } /* - * CreateApIface - * Configures the chip in AP mode and ensures that at least 1 iface creation - * succeeds. - */ -TEST_F(WifiChipHidlTest, CreateApIface) { - if (!gEnv->isSoftApOn) return; - configureChipForIfaceType(IfaceType::AP, true); - - sp<IWifiApIface> iface; - EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface)); - EXPECT_NE(nullptr, iface.get()); -} - -/* - * GetApIfaceNames - * Configures the chip in AP mode and ensures that the iface list is empty - * before creating the iface. Then, create the iface and ensure that - * iface name is returned via the list. - */ -TEST_F(WifiChipHidlTest, GetApIfaceNames) { - if (!gEnv->isSoftApOn) return; - configureChipForIfaceType(IfaceType::AP, true); - - const auto& status_and_iface_names1 = - HIDL_INVOKE(wifi_chip_, getApIfaceNames); - EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code); - EXPECT_EQ(0u, status_and_iface_names1.second.size()); - - sp<IWifiApIface> iface; - EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface)); - EXPECT_NE(nullptr, iface.get()); - - std::string iface_name = getIfaceName(iface); - const auto& status_and_iface_names2 = - HIDL_INVOKE(wifi_chip_, getApIfaceNames); - EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code); - EXPECT_EQ(1u, status_and_iface_names2.second.size()); - EXPECT_EQ(iface_name, status_and_iface_names2.second[0]); - - EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name)); - const auto& status_and_iface_names3 = - HIDL_INVOKE(wifi_chip_, getApIfaceNames); - EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code); - EXPECT_EQ(0u, status_and_iface_names3.second.size()); -} - -/* - * GetApIface - * Configures the chip in AP mode and create an iface. Then, retrieve - * the iface object using the correct name and ensure any other name - * doesn't retrieve an iface object. - */ -TEST_F(WifiChipHidlTest, GetApIface) { - if (!gEnv->isSoftApOn) return; - configureChipForIfaceType(IfaceType::AP, true); - - sp<IWifiApIface> ap_iface; - EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface)); - EXPECT_NE(nullptr, ap_iface.get()); - - std::string iface_name = getIfaceName(ap_iface); - const auto& status_and_iface1 = - HIDL_INVOKE(wifi_chip_, getApIface, iface_name); - EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code); - EXPECT_NE(nullptr, status_and_iface1.second.get()); - - std::string invalid_name = iface_name + "0"; - const auto& status_and_iface2 = - HIDL_INVOKE(wifi_chip_, getApIface, invalid_name); - EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code); - EXPECT_EQ(nullptr, status_and_iface2.second.get()); -} - -/* - * RemoveApIface - * Configures the chip in AP mode and create an iface. Then, remove - * the iface object using the correct name and ensure any other name - * doesn't remove the iface. - */ -TEST_F(WifiChipHidlTest, RemoveApIface) { - if (!gEnv->isSoftApOn) return; - configureChipForIfaceType(IfaceType::AP, true); - - sp<IWifiApIface> ap_iface; - EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface)); - EXPECT_NE(nullptr, ap_iface.get()); - - std::string iface_name = getIfaceName(ap_iface); - std::string invalid_name = iface_name + "0"; - EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(invalid_name)); - EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name)); - - // No such iface exists now. So, this should return failure. - EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(iface_name)); -} - -/* - * CreateNanIface - * Configures the chip in NAN mode and ensures that at least 1 iface creation - * succeeds. - */ -TEST_F(WifiChipHidlTest, CreateNanIface) { - if (!gEnv->isNanOn) return; - configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn); - - sp<IWifiNanIface> iface; - ASSERT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface)); - EXPECT_NE(nullptr, iface.get()); -} - -/* - * GetNanIfaceNames - * Configures the chip in NAN mode and ensures that the iface list is empty - * before creating the iface. Then, create the iface and ensure that - * iface name is returned via the list. - */ -TEST_F(WifiChipHidlTest, GetNanIfaceNames) { - if (!gEnv->isNanOn) return; - configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn); - - const auto& status_and_iface_names1 = - HIDL_INVOKE(wifi_chip_, getNanIfaceNames); - ASSERT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code); - EXPECT_EQ(0u, status_and_iface_names1.second.size()); - - sp<IWifiNanIface> iface; - EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface)); - EXPECT_NE(nullptr, iface.get()); - - std::string iface_name = getIfaceName(iface); - const auto& status_and_iface_names2 = - HIDL_INVOKE(wifi_chip_, getNanIfaceNames); - EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code); - EXPECT_EQ(1u, status_and_iface_names2.second.size()); - EXPECT_EQ(iface_name, status_and_iface_names2.second[0]); - - EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name)); - const auto& status_and_iface_names3 = - HIDL_INVOKE(wifi_chip_, getNanIfaceNames); - EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code); - EXPECT_EQ(0u, status_and_iface_names3.second.size()); -} - -/* - * GetNanIface - * Configures the chip in NAN mode and create an iface. Then, retrieve - * the iface object using the correct name and ensure any other name - * doesn't retrieve an iface object. - */ -TEST_F(WifiChipHidlTest, GetNanIface) { - if (!gEnv->isNanOn) return; - configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn); - - sp<IWifiNanIface> nan_iface; - EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface)); - EXPECT_NE(nullptr, nan_iface.get()); - - std::string iface_name = getIfaceName(nan_iface); - const auto& status_and_iface1 = - HIDL_INVOKE(wifi_chip_, getNanIface, iface_name); - EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code); - EXPECT_NE(nullptr, status_and_iface1.second.get()); - - std::string invalid_name = iface_name + "0"; - const auto& status_and_iface2 = - HIDL_INVOKE(wifi_chip_, getNanIface, invalid_name); - EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code); - EXPECT_EQ(nullptr, status_and_iface2.second.get()); -} - -/* - * RemoveNanIface - * Configures the chip in NAN mode and create an iface. Then, remove - * the iface object using the correct name and ensure any other name - * doesn't remove the iface. - */ -TEST_F(WifiChipHidlTest, RemoveNanIface) { - if (!gEnv->isNanOn) return; - configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn); - - sp<IWifiNanIface> nan_iface; - EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface)); - EXPECT_NE(nullptr, nan_iface.get()); - - std::string iface_name = getIfaceName(nan_iface); - std::string invalid_name = iface_name + "0"; - EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(invalid_name)); - - EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name)); - - // No such iface exists now. So, this should return failure. - EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(iface_name)); -} - -/* * CreateP2pIface * Configures the chip in P2P mode and ensures that at least 1 iface creation * succeeds. */ -TEST_F(WifiChipHidlTest, CreateP2pIface) { +TEST_P(WifiChipHidlTest, CreateP2pIface) { configureChipForIfaceType(IfaceType::P2P, true); sp<IWifiP2pIface> iface; @@ -573,7 +364,7 @@ TEST_F(WifiChipHidlTest, CreateP2pIface) { * before creating the iface. Then, create the iface and ensure that * iface name is returned via the list. */ -TEST_F(WifiChipHidlTest, GetP2pIfaceNames) { +TEST_P(WifiChipHidlTest, GetP2pIfaceNames) { configureChipForIfaceType(IfaceType::P2P, true); const auto& status_and_iface_names1 = @@ -605,7 +396,7 @@ TEST_F(WifiChipHidlTest, GetP2pIfaceNames) { * the iface object using the correct name and ensure any other name * doesn't retrieve an iface object. */ -TEST_F(WifiChipHidlTest, GetP2pIface) { +TEST_P(WifiChipHidlTest, GetP2pIface) { configureChipForIfaceType(IfaceType::P2P, true); sp<IWifiP2pIface> p2p_iface; @@ -631,7 +422,7 @@ TEST_F(WifiChipHidlTest, GetP2pIface) { * the iface object using the correct name and ensure any other name * doesn't remove the iface. */ -TEST_F(WifiChipHidlTest, RemoveP2pIface) { +TEST_P(WifiChipHidlTest, RemoveP2pIface) { configureChipForIfaceType(IfaceType::P2P, true); sp<IWifiP2pIface> p2p_iface; @@ -652,7 +443,7 @@ TEST_F(WifiChipHidlTest, RemoveP2pIface) { * Configures the chip in STA mode and ensures that at least 1 iface creation * succeeds. */ -TEST_F(WifiChipHidlTest, CreateStaIface) { +TEST_P(WifiChipHidlTest, CreateStaIface) { configureChipForIfaceType(IfaceType::STA, true); sp<IWifiStaIface> iface; @@ -666,7 +457,7 @@ TEST_F(WifiChipHidlTest, CreateStaIface) { * before creating the iface. Then, create the iface and ensure that * iface name is returned via the list. */ -TEST_F(WifiChipHidlTest, GetStaIfaceNames) { +TEST_P(WifiChipHidlTest, GetStaIfaceNames) { configureChipForIfaceType(IfaceType::STA, true); const auto& status_and_iface_names1 = @@ -698,7 +489,7 @@ TEST_F(WifiChipHidlTest, GetStaIfaceNames) { * the iface object using the correct name and ensure any other name * doesn't retrieve an iface object. */ -TEST_F(WifiChipHidlTest, GetStaIface) { +TEST_P(WifiChipHidlTest, GetStaIface) { configureChipForIfaceType(IfaceType::STA, true); sp<IWifiStaIface> sta_iface; @@ -724,7 +515,7 @@ TEST_F(WifiChipHidlTest, GetStaIface) { * the iface object using the correct name and ensure any other name * doesn't remove the iface. */ -TEST_F(WifiChipHidlTest, RemoveStaIface) { +TEST_P(WifiChipHidlTest, RemoveStaIface) { configureChipForIfaceType(IfaceType::STA, true); sp<IWifiStaIface> sta_iface; @@ -743,7 +534,7 @@ TEST_F(WifiChipHidlTest, RemoveStaIface) { /* * CreateRttController */ -TEST_F(WifiChipHidlTest, CreateRttController) { +TEST_P(WifiChipHidlTest, CreateRttController) { configureChipForIfaceType(IfaceType::STA, true); sp<IWifiStaIface> iface; @@ -755,3 +546,9 @@ TEST_F(WifiChipHidlTest, CreateRttController) { EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_rtt_controller.first.code); EXPECT_NE(nullptr, status_and_rtt_controller.second.get()); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiChipHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_hidl_test.cpp index b8e501c0a5..512701a40c 100644 --- a/wifi/1.0/vts/functional/wifi_hidl_test.cpp +++ b/wifi/1.0/vts/functional/wifi_hidl_test.cpp @@ -18,7 +18,9 @@ #include <android/hardware/wifi/1.0/IWifi.h> -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_test_utils.h" @@ -28,13 +30,14 @@ using ::android::sp; /** * Fixture to use for all root Wifi HIDL interface tests. */ -class WifiHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override {} - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -42,7 +45,12 @@ class WifiHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifi proxy object is * successfully created. */ -TEST(WifiHidlTestNoFixture, Create) { - EXPECT_NE(nullptr, getWifi().get()); - stopWifi(); +TEST_P(WifiHidlTest, Create) { + // The creation of a proxy object is tested as part of SetUp method. } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_hidl_test_utils.h b/wifi/1.0/vts/functional/wifi_hidl_test_utils.h index 529b142f22..bdee2ec0ce 100644 --- a/wifi/1.0/vts/functional/wifi_hidl_test_utils.h +++ b/wifi/1.0/vts/functional/wifi_hidl_test_utils.h @@ -61,48 +61,4 @@ class WifiHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { stopWifi(); sleep(5); } - - public: - // Whether NaN feature is supported on the device. - bool isNanOn = false; - // Whether SoftAp feature is supported on the device. - bool isSoftApOn = false; - - void usage(char* me, char* arg) { - fprintf(stderr, - "unrecognized option: %s\n\n" - "usage: %s <gtest options> <test options>\n\n" - "test options are:\n\n" - "-N, --nan_on: Whether NAN feature is supported\n" - "-S, --softap_on: Whether SOFTAP feature is supported\n", - arg, me); - } - - int initFromOptions(int argc, char** argv) { - static struct option options[] = {{"nan_on", no_argument, 0, 'N'}, - {"softap_on", no_argument, 0, 'S'}, - {0, 0, 0, 0}}; - - int c; - while ((c = getopt_long(argc, argv, "NS", options, NULL)) >= 0) { - switch (c) { - case 'N': - isNanOn = true; - break; - case 'S': - isSoftApOn = true; - break; - default: - usage(argv[0], argv[optind]); - return 2; - } - } - - if (optind < argc) { - usage(argv[0], argv[optind]); - return 2; - } - - return 0; - } }; diff --git a/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp index 64b4fb6d1a..422e3f6bba 100644 --- a/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp +++ b/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp @@ -16,10 +16,12 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.0/IWifi.h> #include <android/hardware/wifi/1.0/IWifiNanIface.h> #include <android/hardware/wifi/1.0/IWifiNanIfaceEventCallback.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include <chrono> #include <condition_variable> #include <mutex> @@ -29,27 +31,28 @@ using namespace ::android::hardware::wifi::V1_0; +using ::android::sp; using ::android::hardware::Return; using ::android::hardware::Void; -using ::android::sp; +using ::android::hardware::wifi::V1_0::IWifi; #define TIMEOUT_PERIOD 10 /** * Fixture to use for all NAN Iface HIDL interface tests. */ -class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { - public: +class WifiNanIfaceHidlTest : public ::testing::TestWithParam<std::string> { + public: virtual void SetUp() override { - iwifiNanIface = getWifiNanIface(); - ASSERT_NE(nullptr, iwifiNanIface.get()); - ASSERT_EQ(WifiStatusCode::SUCCESS, HIDL_INVOKE(iwifiNanIface, registerEventCallback, - new WifiNanIfaceEventCallback(*this)).code); + iwifiNanIface = getWifiNanIface(GetInstanceName()); + ASSERT_NE(nullptr, iwifiNanIface.get()); + ASSERT_EQ(WifiStatusCode::SUCCESS, + HIDL_INVOKE(iwifiNanIface, registerEventCallback, + new WifiNanIfaceEventCallback(*this)) + .code); } - virtual void TearDown() override { - stopWifi(); - } + virtual void TearDown() override { stopWifi(GetInstanceName()); } /* Used as a mechanism to inform the test about data/event callback */ inline void notify() { @@ -438,6 +441,8 @@ class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { NanFollowupReceivedInd nanFollowupReceivedInd; NanDataPathRequestInd nanDataPathRequestInd; NanDataPathConfirmInd nanDataPathConfirmInd; + + std::string GetInstanceName() { return GetParam(); } }; /* @@ -445,9 +450,8 @@ class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifiNanIface proxy object is * successfully created. */ -TEST(WifiNanIfaceHidlTestNoFixture, Create) { - ASSERT_NE(nullptr, getWifiNanIface().get()); - stopWifi(); +TEST_P(WifiNanIfaceHidlTest, Create) { + // The creation of a proxy object is tested as part of SetUp method. } /* @@ -455,41 +459,51 @@ TEST(WifiNanIfaceHidlTestNoFixture, Create) { * Ensure that API calls fail with ERROR_WIFI_IFACE_INVALID when using an interface once wifi * is disabled. */ -TEST(WifiNanIfaceHidlTestNoFixture, FailOnIfaceInvalid) { - android::sp<IWifiNanIface> iwifiNanIface = getWifiNanIface(); - ASSERT_NE(nullptr, iwifiNanIface.get()); - stopWifi(); - sleep(5); // make sure that all chips/interfaces are invalidated - ASSERT_EQ(WifiStatusCode::ERROR_WIFI_IFACE_INVALID, - HIDL_INVOKE(iwifiNanIface, getCapabilitiesRequest, 0).code); +TEST_P(WifiNanIfaceHidlTest, FailOnIfaceInvalid) { + stopWifi(GetInstanceName()); + android::sp<IWifiNanIface> iwifiNanIface = + getWifiNanIface(GetInstanceName()); + ASSERT_NE(nullptr, iwifiNanIface.get()); + stopWifi(GetInstanceName()); + sleep(5); // make sure that all chips/interfaces are invalidated + ASSERT_EQ(WifiStatusCode::ERROR_WIFI_IFACE_INVALID, + HIDL_INVOKE(iwifiNanIface, getCapabilitiesRequest, 0).code); } /* * getCapabilitiesRequest: validate that returns capabilities. */ -TEST_F(WifiNanIfaceHidlTest, getCapabilitiesRequest) { - uint16_t inputCmdId = 10; - callbackType = INVALID; - ASSERT_EQ(WifiStatusCode::SUCCESS, +TEST_P(WifiNanIfaceHidlTest, getCapabilitiesRequest) { + uint16_t inputCmdId = 10; + callbackType = INVALID; + ASSERT_EQ( + WifiStatusCode::SUCCESS, HIDL_INVOKE(iwifiNanIface, getCapabilitiesRequest, inputCmdId).code); - // wait for a callback - ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_CAPABILITIES_RESPONSE)); - ASSERT_EQ(NOTIFY_CAPABILITIES_RESPONSE, callbackType); - ASSERT_EQ(id, inputCmdId); - - // check for reasonable capability values - EXPECT_GT(capabilities.maxConcurrentClusters, (unsigned int) 0); - EXPECT_GT(capabilities.maxPublishes, (unsigned int) 0); - EXPECT_GT(capabilities.maxSubscribes, (unsigned int) 0); - EXPECT_EQ(capabilities.maxServiceNameLen, (unsigned int) 255); - EXPECT_EQ(capabilities.maxMatchFilterLen, (unsigned int) 255); - EXPECT_GT(capabilities.maxTotalMatchFilterLen, (unsigned int) 255); - EXPECT_EQ(capabilities.maxServiceSpecificInfoLen, (unsigned int) 255); - EXPECT_GE(capabilities.maxExtendedServiceSpecificInfoLen, (unsigned int) 255); - EXPECT_GT(capabilities.maxNdiInterfaces, (unsigned int) 0); - EXPECT_GT(capabilities.maxNdpSessions, (unsigned int) 0); - EXPECT_GT(capabilities.maxAppInfoLen, (unsigned int) 0); - EXPECT_GT(capabilities.maxQueuedTransmitFollowupMsgs, (unsigned int) 0); - EXPECT_GT(capabilities.maxSubscribeInterfaceAddresses, (unsigned int) 0); - EXPECT_NE(capabilities.supportedCipherSuites, (unsigned int) 0); + // wait for a callback + ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_CAPABILITIES_RESPONSE)); + ASSERT_EQ(NOTIFY_CAPABILITIES_RESPONSE, callbackType); + ASSERT_EQ(id, inputCmdId); + + // check for reasonable capability values + EXPECT_GT(capabilities.maxConcurrentClusters, (unsigned int)0); + EXPECT_GT(capabilities.maxPublishes, (unsigned int)0); + EXPECT_GT(capabilities.maxSubscribes, (unsigned int)0); + EXPECT_EQ(capabilities.maxServiceNameLen, (unsigned int)255); + EXPECT_EQ(capabilities.maxMatchFilterLen, (unsigned int)255); + EXPECT_GT(capabilities.maxTotalMatchFilterLen, (unsigned int)255); + EXPECT_EQ(capabilities.maxServiceSpecificInfoLen, (unsigned int)255); + EXPECT_GE(capabilities.maxExtendedServiceSpecificInfoLen, + (unsigned int)255); + EXPECT_GT(capabilities.maxNdiInterfaces, (unsigned int)0); + EXPECT_GT(capabilities.maxNdpSessions, (unsigned int)0); + EXPECT_GT(capabilities.maxAppInfoLen, (unsigned int)0); + EXPECT_GT(capabilities.maxQueuedTransmitFollowupMsgs, (unsigned int)0); + EXPECT_GT(capabilities.maxSubscribeInterfaceAddresses, (unsigned int)0); + EXPECT_NE(capabilities.supportedCipherSuites, (unsigned int)0); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiNanIfaceHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_p2p_iface_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_p2p_iface_hidl_test.cpp index 269eb6c62e..8f3327150c 100644 --- a/wifi/1.0/vts/functional/wifi_p2p_iface_hidl_test.cpp +++ b/wifi/1.0/vts/functional/wifi_p2p_iface_hidl_test.cpp @@ -16,25 +16,29 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.0/IWifi.h> #include <android/hardware/wifi/1.0/IWifiP2pIface.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_test_utils.h" -using ::android::hardware::wifi::V1_0::IWifiP2pIface; using ::android::sp; +using ::android::hardware::wifi::V1_0::IWifi; +using ::android::hardware::wifi::V1_0::IWifiP2pIface; /** * Fixture to use for all P2P Iface HIDL interface tests. */ -class WifiP2pIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiP2pIfaceHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override {} - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -42,7 +46,13 @@ class WifiP2pIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifiP2pIface proxy object is * successfully created. */ -TEST(WifiP2pIfaceHidlTestNoFixture, Create) { - EXPECT_NE(nullptr, getWifiP2pIface().get()); - stopWifi(); +TEST_P(WifiP2pIfaceHidlTest, Create) { + stopWifi(GetInstanceName()); + EXPECT_NE(nullptr, getWifiP2pIface(GetInstanceName()).get()); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiP2pIfaceHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp index e13086d2ec..e1ee34fef0 100644 --- a/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp +++ b/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp @@ -16,25 +16,29 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.0/IWifi.h> #include <android/hardware/wifi/1.0/IWifiRttController.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_test_utils.h" -using ::android::hardware::wifi::V1_0::IWifiRttController; using ::android::sp; +using ::android::hardware::wifi::V1_0::IWifi; +using ::android::hardware::wifi::V1_0::IWifiRttController; /** * Fixture to use for all RTT controller HIDL interface tests. */ -class WifiRttControllerHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiRttControllerHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override {} - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -42,7 +46,13 @@ class WifiRttControllerHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifiRttController proxy object is * successfully created. */ -TEST(WifiRttControllerHidlTestNoFixture, Create) { - EXPECT_NE(nullptr, getWifiRttController().get()); - stopWifi(); +TEST_P(WifiRttControllerHidlTest, Create) { + stopWifi(GetInstanceName()); + EXPECT_NE(nullptr, getWifiRttController(GetInstanceName()).get()); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiRttControllerHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.0/vts/functional/wifi_sta_iface_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_sta_iface_hidl_test.cpp index a41386338b..30b6fba38c 100644 --- a/wifi/1.0/vts/functional/wifi_sta_iface_hidl_test.cpp +++ b/wifi/1.0/vts/functional/wifi_sta_iface_hidl_test.cpp @@ -16,10 +16,12 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.0/IWifi.h> #include <android/hardware/wifi/1.0/IWifiStaIface.h> #include <android/hardware/wifi/1.3/IWifiStaIface.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" @@ -28,6 +30,7 @@ using ::android::sp; using ::android::hardware::wifi::V1_0::Bssid; using ::android::hardware::wifi::V1_0::CommandId; using ::android::hardware::wifi::V1_0::IfaceType; +using ::android::hardware::wifi::V1_0::IWifi; using ::android::hardware::wifi::V1_0::IWifiStaIface; using ::android::hardware::wifi::V1_0::Rssi; using ::android::hardware::wifi::V1_0::Ssid; @@ -41,14 +44,14 @@ using ::android::hardware::wifi::V1_0::WifiStatusCode; /** * Fixture to use for all STA Iface HIDL interface tests. */ -class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiStaIfaceHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - wifi_sta_iface_ = getWifiStaIface(); + wifi_sta_iface_ = getWifiStaIface(GetInstanceName()); ASSERT_NE(nullptr, wifi_sta_iface_.get()); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: bool isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask cap_mask) { @@ -59,6 +62,7 @@ class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { } sp<IWifiStaIface> wifi_sta_iface_; + std::string GetInstanceName() { return GetParam(); } }; /* @@ -66,15 +70,14 @@ class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifiStaIface proxy object is * successfully created. */ -TEST(WifiStaIfaceHidlTestNoFixture, Create) { - EXPECT_NE(nullptr, getWifiStaIface().get()); - stopWifi(); +TEST_P(WifiStaIfaceHidlTest, Create) { + // The creation of a proxy object is tested as part of SetUp method. } /* * GetCapabilities: */ -TEST_F(WifiStaIfaceHidlTest, GetCapabilities) { +TEST_P(WifiStaIfaceHidlTest, GetCapabilities) { const auto& status_and_caps = HIDL_INVOKE(wifi_sta_iface_, getCapabilities); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_caps.first.code); EXPECT_GT(status_and_caps.second, 0u); @@ -84,7 +87,7 @@ TEST_F(WifiStaIfaceHidlTest, GetCapabilities) { * GetType: * Ensures that the correct interface type is returned for station interface. */ -TEST_F(WifiStaIfaceHidlTest, GetType) { +TEST_P(WifiStaIfaceHidlTest, GetType) { const auto& status_and_type = HIDL_INVOKE(wifi_sta_iface_, getType); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_type.first.code); EXPECT_EQ(IfaceType::STA, status_and_type.second); @@ -94,7 +97,7 @@ TEST_F(WifiStaIfaceHidlTest, GetType) { * GetApfPacketFilterCapabilities: * Ensures that we can retrieve APF packet filter capabilites. */ -TEST_F(WifiStaIfaceHidlTest, GetApfPacketFilterCapabilities) { +TEST_P(WifiStaIfaceHidlTest, GetApfPacketFilterCapabilities) { if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::APF)) { // No-op if APF packet filer is not supported. return; @@ -109,7 +112,7 @@ TEST_F(WifiStaIfaceHidlTest, GetApfPacketFilterCapabilities) { * GetBackgroundScanCapabilities: * Ensures that we can retrieve background scan capabilities. */ -TEST_F(WifiStaIfaceHidlTest, GetBackgroundScanCapabilities) { +TEST_P(WifiStaIfaceHidlTest, GetBackgroundScanCapabilities) { if (!isCapabilitySupported( IWifiStaIface::StaIfaceCapabilityMask::BACKGROUND_SCAN)) { // No-op if background scan is not supported. @@ -125,7 +128,7 @@ TEST_F(WifiStaIfaceHidlTest, GetBackgroundScanCapabilities) { * GetValidFrequenciesForBand: * Ensures that we can retrieve valid frequencies for 2.4 GHz band. */ -TEST_F(WifiStaIfaceHidlTest, GetValidFrequenciesForBand) { +TEST_P(WifiStaIfaceHidlTest, GetValidFrequenciesForBand) { const auto& status_and_freqs = HIDL_INVOKE( wifi_sta_iface_, getValidFrequenciesForBand, WifiBand::BAND_24GHZ); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_freqs.first.code); @@ -137,7 +140,7 @@ TEST_F(WifiStaIfaceHidlTest, GetValidFrequenciesForBand) { * Ensures that calls to enable, disable, and retrieve link layer stats * will return a success status code. */ -TEST_F(WifiStaIfaceHidlTest, LinkLayerStatsCollection) { +TEST_P(WifiStaIfaceHidlTest, LinkLayerStatsCollection) { if (!isCapabilitySupported( IWifiStaIface::StaIfaceCapabilityMask::LINK_LAYER_STATS)) { // No-op if link layer stats is not supported. @@ -172,7 +175,7 @@ TEST_F(WifiStaIfaceHidlTest, LinkLayerStatsCollection) { * Ensures that calls to disable RSSI monitoring will return an error status * code if RSSI monitoring is not enabled. */ -TEST_F(WifiStaIfaceHidlTest, RSSIMonitoring) { +TEST_P(WifiStaIfaceHidlTest, RSSIMonitoring) { if (!isCapabilitySupported( IWifiStaIface::StaIfaceCapabilityMask::RSSI_MONITOR)) { // No-op if RSSI monitor is not supported. @@ -197,7 +200,7 @@ TEST_F(WifiStaIfaceHidlTest, RSSIMonitoring) { * Ensures that calls to configure and enable roaming will return a success * status code. */ -TEST_F(WifiStaIfaceHidlTest, RoamingControl) { +TEST_P(WifiStaIfaceHidlTest, RoamingControl) { if (!isCapabilitySupported( IWifiStaIface::StaIfaceCapabilityMask::CONTROL_ROAMING)) { // No-op if roaming control is not supported. @@ -242,9 +245,9 @@ TEST_F(WifiStaIfaceHidlTest, RoamingControl) { * Ensures that calls to enable neighbor discovery offload will return a success * status code. */ -TEST_F(WifiStaIfaceHidlTest, EnableNDOffload) { - if (!isCapabilitySupported( - IWifiStaIface::StaIfaceCapabilityMask::ND_OFFLOAD)) { +TEST_P(WifiStaIfaceHidlTest, EnableNDOffload) { + if (!isCapabilitySupported( + IWifiStaIface::StaIfaceCapabilityMask::ND_OFFLOAD)) { // No-op if nd offload is not supported. return; } @@ -257,7 +260,7 @@ TEST_F(WifiStaIfaceHidlTest, EnableNDOffload) { * Ensures that calls to set scanning MAC OUI will return a success status * code. */ -TEST_F(WifiStaIfaceHidlTest, SetScanningMacOui) { +TEST_P(WifiStaIfaceHidlTest, SetScanningMacOui) { if (!isCapabilitySupported( IWifiStaIface::StaIfaceCapabilityMask::SCAN_RAND)) { // No-op if SetScanningMacOui is not supported. @@ -274,9 +277,9 @@ TEST_F(WifiStaIfaceHidlTest, SetScanningMacOui) { * Ensures that calls to start packet fate monitoring and retrieve TX/RX * packets will return a success status code. */ -TEST_F(WifiStaIfaceHidlTest, PacketFateMonitoring) { - if (!isCapabilitySupported( - IWifiStaIface::StaIfaceCapabilityMask::DEBUG_PACKET_FATE)) { +TEST_P(WifiStaIfaceHidlTest, PacketFateMonitoring) { + if (!isCapabilitySupported( + IWifiStaIface::StaIfaceCapabilityMask::DEBUG_PACKET_FATE)) { // No-op if packet fate monitor is not supported. return; } @@ -291,3 +294,9 @@ TEST_F(WifiStaIfaceHidlTest, PacketFateMonitoring) { EXPECT_EQ(WifiStatusCode::SUCCESS, HIDL_INVOKE(wifi_sta_iface_, getDebugRxPacketFates).first.code); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiStaIfaceHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.1/vts/functional/Android.bp b/wifi/1.1/vts/functional/Android.bp index 6d7635d455..775031e683 100644 --- a/wifi/1.1/vts/functional/Android.bp +++ b/wifi/1.1/vts/functional/Android.bp @@ -28,5 +28,5 @@ cc_test { "android.hardware.wifi@1.3", "libwifi-system-iface" ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts-core"], } diff --git a/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp b/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp index a0f97f81e8..4b62b15699 100644 --- a/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp +++ b/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp @@ -14,37 +14,8 @@ * limitations under the License. */ -#include <android-base/logging.h> -#include <android/hardware/wifi/1.1/IWifi.h> +#include <VtsHalHidlTargetTestEnvBase.h> -#include "wifi_hidl_test_utils.h" - -class WifiHidlEnvironment_1_1 : public WifiHidlEnvironment { - public: - // get the test environment singleton - static WifiHidlEnvironment_1_1* Instance() { - static WifiHidlEnvironment_1_1* instance = new WifiHidlEnvironment_1_1; - return instance; - } - - virtual void registerTestServices() override { - registerTestService<android::hardware::wifi::V1_1::IWifi>(); - } - - private: - WifiHidlEnvironment_1_1() {} -}; - -WifiHidlEnvironment* gEnv = WifiHidlEnvironment_1_1::Instance(); - -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(gEnv); - ::testing::InitGoogleTest(&argc, argv); - gEnv->init(&argc, argv); - int status = gEnv->initFromOptions(argc, argv); - if (status == 0) { - int status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - } - return status; -} +// TODO(b/143892896): Remove this file after wifi_hidl_test_utils.cpp is +// updated. +::testing::VtsHalHidlTargetTestEnvBase* gEnv = nullptr; diff --git a/wifi/1.1/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.1/vts/functional/wifi_chip_hidl_test.cpp index 63235472af..08de240252 100644 --- a/wifi/1.1/vts/functional/wifi_chip_hidl_test.cpp +++ b/wifi/1.1/vts/functional/wifi_chip_hidl_test.cpp @@ -19,8 +19,9 @@ #include <android/hardware/wifi/1.1/IWifi.h> #include <android/hardware/wifi/1.1/IWifiChip.h> #include <android/hardware/wifi/1.3/IWifiChip.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" @@ -45,14 +46,14 @@ constexpr IWifiChip::TxPowerScenario kFakePowerScenario = /** * Fixture to use for all Wifi chip HIDL interface tests. */ -class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiChipHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - wifi_chip_ = IWifiChip::castFrom(getWifiChip()); + wifi_chip_ = IWifiChip::castFrom(getWifiChip(GetInstanceName())); ASSERT_NE(nullptr, wifi_chip_.get()); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: uint32_t configureChipForStaIfaceAndGetCapabilities() { @@ -77,12 +78,15 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { } sp<IWifiChip> wifi_chip_; + + private: + std::string GetInstanceName() { return GetParam(); } }; /* * SelectTxPowerScenario */ -TEST_F(WifiChipHidlTest, SelectTxPowerScenario) { +TEST_P(WifiChipHidlTest, SelectTxPowerScenario) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status = HIDL_INVOKE(wifi_chip_, selectTxPowerScenario, kFakePowerScenario); @@ -96,7 +100,7 @@ TEST_F(WifiChipHidlTest, SelectTxPowerScenario) { /* * ResetTxPowerScenario */ -TEST_F(WifiChipHidlTest, ResetTxPowerScenario) { +TEST_P(WifiChipHidlTest, ResetTxPowerScenario) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status = HIDL_INVOKE(wifi_chip_, resetTxPowerScenario); @@ -106,3 +110,9 @@ TEST_F(WifiChipHidlTest, ResetTxPowerScenario) { EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED, status.code); } } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiChipHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.2/vts/functional/Android.bp b/wifi/1.2/vts/functional/Android.bp index 97853d0300..f43e49ef12 100644 --- a/wifi/1.2/vts/functional/Android.bp +++ b/wifi/1.2/vts/functional/Android.bp @@ -30,7 +30,8 @@ cc_test { "android.hardware.wifi@1.3", "libwifi-system-iface" ], - test_suites: ["general-tests"], + disable_framework: true, + test_suites: ["general-tests", "vts-core"], } cc_test { @@ -47,5 +48,6 @@ cc_test { "android.hardware.wifi@1.2", "libwifi-system-iface" ], - test_suites: ["general-tests"], + disable_framework: true, + test_suites: ["general-tests", "vts-core"], } diff --git a/wifi/1.2/vts/functional/VtsHalWifiV1_2TargetTest.cpp b/wifi/1.2/vts/functional/VtsHalWifiV1_2TargetTest.cpp index c765cdcc3b..52c7a4ad0d 100644 --- a/wifi/1.2/vts/functional/VtsHalWifiV1_2TargetTest.cpp +++ b/wifi/1.2/vts/functional/VtsHalWifiV1_2TargetTest.cpp @@ -14,35 +14,8 @@ * limitations under the License. */ -#include <android-base/logging.h> -#include <android/hardware/wifi/1.2/IWifi.h> +#include <VtsHalHidlTargetTestEnvBase.h> -#include "wifi_hidl_test_utils.h" - -using ::android::hardware::wifi::V1_2::IWifi; - -// Test environment for Wifi HIDL HAL. -class WifiHidlEnvironment_1_2 : public WifiHidlEnvironment { - public: - // get the test environment singleton - static WifiHidlEnvironment_1_2* Instance() { - static WifiHidlEnvironment_1_2* instance = new WifiHidlEnvironment_1_2; - return instance; - } - - virtual void registerTestServices() override { registerTestService<IWifi>(); } - - private: - WifiHidlEnvironment_1_2() {} -}; - -WifiHidlEnvironment_1_2* gEnv = WifiHidlEnvironment_1_2::Instance(); - -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(gEnv); - ::testing::InitGoogleTest(&argc, argv); - gEnv->init(&argc, argv); - int status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - return status; -} +// TODO(b/143892896): Remove this file after wifi_hidl_test_utils.cpp is +// updated. +::testing::VtsHalHidlTargetTestEnvBase* gEnv = nullptr;
\ No newline at end of file diff --git a/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp index 9d567feafa..47faec8b88 100644 --- a/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp +++ b/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp @@ -16,12 +16,14 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.2/IWifi.h> #include <android/hardware/wifi/1.2/IWifiChip.h> #include <android/hardware/wifi/1.2/IWifiChipEventCallback.h> #include <android/hardware/wifi/1.3/IWifiChip.h> - +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include <VtsHalHidlTargetCallbackBase.h> -#include <VtsHalHidlTargetTestBase.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" @@ -50,14 +52,14 @@ constexpr IWifiChip::TxPowerScenario kPowerScenarioVoiceCall = /** * Fixture to use for all Wifi chip HIDL interface tests. */ -class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiChipHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - wifi_chip_ = IWifiChip::castFrom(getWifiChip()); + wifi_chip_ = IWifiChip::castFrom(getWifiChip(GetInstanceName())); ASSERT_NE(nullptr, wifi_chip_.get()); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } // A simple test implementation of WifiChipEventCallback. class WifiChipEventCallback @@ -123,6 +125,9 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { } sp<IWifiChip> wifi_chip_; + + private: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -130,7 +135,7 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { * This test case tests the selectTxPowerScenario_1_2() API with SAR scenarios * newly defined in 1.2 */ -TEST_F(WifiChipHidlTest, SelectTxPowerScenario_1_2_body) { +TEST_P(WifiChipHidlTest, SelectTxPowerScenario_1_2_body) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status = HIDL_INVOKE(wifi_chip_, selectTxPowerScenario_1_2, kPowerScenarioBody); @@ -147,7 +152,7 @@ TEST_F(WifiChipHidlTest, SelectTxPowerScenario_1_2_body) { * This test case tests the selectTxPowerScenario_1_2() API with previously * defined SAR scenarios */ -TEST_F(WifiChipHidlTest, SelectTxPowerScenario_1_2_voiceCall) { +TEST_P(WifiChipHidlTest, SelectTxPowerScenario_1_2_voiceCall) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status = HIDL_INVOKE(wifi_chip_, selectTxPowerScenario_1_2, kPowerScenarioVoiceCall); @@ -167,9 +172,15 @@ TEST_F(WifiChipHidlTest, SelectTxPowerScenario_1_2_voiceCall) { * since event is triggered internally in the HAL implementation, and can not be * triggered from the test case */ -TEST_F(WifiChipHidlTest, registerEventCallback_1_2) { +TEST_P(WifiChipHidlTest, registerEventCallback_1_2) { sp<WifiChipEventCallback> wifiChipEventCallback = new WifiChipEventCallback(); const auto& status = HIDL_INVOKE(wifi_chip_, registerEventCallback_1_2, wifiChipEventCallback); EXPECT_EQ(WifiStatusCode::SUCCESS, status.code); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiChipHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames( + ::android::hardware::wifi::V1_2::IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp index 4dbc82bd99..f3f76e1564 100644 --- a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp +++ b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp @@ -16,10 +16,12 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.2/IWifi.h> #include <android/hardware/wifi/1.2/IWifiNanIface.h> #include <android/hardware/wifi/1.2/IWifiNanIfaceEventCallback.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include <chrono> #include <condition_variable> #include <mutex> @@ -36,19 +38,19 @@ using ::android::sp; #define TIMEOUT_PERIOD 10 -android::sp<android::hardware::wifi::V1_2::IWifiNanIface> -getWifiNanIface_1_2() { +android::sp<android::hardware::wifi::V1_2::IWifiNanIface> getWifiNanIface_1_2( + const std::string& instance_name) { return android::hardware::wifi::V1_2::IWifiNanIface::castFrom( - getWifiNanIface()); + getWifiNanIface(instance_name)); } /** * Fixture to use for all NAN Iface HIDL interface tests. */ -class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiNanIfaceHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - iwifiNanIface = getWifiNanIface_1_2(); + iwifiNanIface = getWifiNanIface_1_2(GetInstanceName()); ASSERT_NE(nullptr, iwifiNanIface.get()); ASSERT_EQ(WifiStatusCode::SUCCESS, HIDL_INVOKE(iwifiNanIface, registerEventCallback_1_2, @@ -56,7 +58,7 @@ class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { .code); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } /* Used as a mechanism to inform the test about data/event callback */ inline void notify() { @@ -458,6 +460,8 @@ class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { ::android::hardware::wifi::V1_2::NanDataPathConfirmInd nanDataPathConfirmInd_1_2; NanDataPathScheduleUpdateInd nanDataPathScheduleUpdateInd; + + std::string GetInstanceName() { return GetParam(); } }; /* @@ -465,15 +469,14 @@ class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that an instance of the IWifiNanIface proxy object is * successfully created. */ -TEST(WifiNanIfaceHidlTestNoFixture, Create) { - ASSERT_NE(nullptr, getWifiNanIface_1_2().get()); - stopWifi(); +TEST_P(WifiNanIfaceHidlTest, Create) { + // The creation of a proxy object is tested as part of SetUp method. } /* * enableRequest_1_2InvalidArgs: validate that fails with invalid arguments */ -TEST_F(WifiNanIfaceHidlTest, enableRequest_1_2InvalidArgs) { +TEST_P(WifiNanIfaceHidlTest, enableRequest_1_2InvalidArgs) { uint16_t inputCmdId = 10; callbackType = INVALID; NanEnableRequest nanEnableRequest = {}; @@ -493,7 +496,7 @@ TEST_F(WifiNanIfaceHidlTest, enableRequest_1_2InvalidArgs) { * enableRequest_1_2ShimInvalidArgs: validate that fails with invalid arguments * to the shim */ -TEST_F(WifiNanIfaceHidlTest, enableRequest_1_2ShimInvalidArgs) { +TEST_P(WifiNanIfaceHidlTest, enableRequest_1_2ShimInvalidArgs) { uint16_t inputCmdId = 10; NanEnableRequest nanEnableRequest = {}; nanEnableRequest.configParams.numberOfPublishServiceIdsInBeacon = @@ -508,7 +511,7 @@ TEST_F(WifiNanIfaceHidlTest, enableRequest_1_2ShimInvalidArgs) { /* * configRequest_1_2InvalidArgs: validate that fails with invalid arguments */ -TEST_F(WifiNanIfaceHidlTest, configRequest_1_2InvalidArgs) { +TEST_P(WifiNanIfaceHidlTest, configRequest_1_2InvalidArgs) { uint16_t inputCmdId = 10; callbackType = INVALID; NanConfigRequest nanConfigRequest = {}; @@ -528,7 +531,7 @@ TEST_F(WifiNanIfaceHidlTest, configRequest_1_2InvalidArgs) { * configRequest_1_2ShimInvalidArgs: validate that fails with invalid arguments * to the shim */ -TEST_F(WifiNanIfaceHidlTest, configRequest_1_2ShimInvalidArgs) { +TEST_P(WifiNanIfaceHidlTest, configRequest_1_2ShimInvalidArgs) { uint16_t inputCmdId = 10; NanConfigRequest nanConfigRequest = {}; nanConfigRequest.numberOfPublishServiceIdsInBeacon = 128; // must be <= 127 @@ -538,3 +541,9 @@ TEST_F(WifiNanIfaceHidlTest, configRequest_1_2ShimInvalidArgs) { nanConfigRequest, nanConfigRequestSupp) .code); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiNanIfaceHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames( + ::android::hardware::wifi::V1_2::IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.2/vts/functional/wifi_sta_iface_hidl_test.cpp b/wifi/1.2/vts/functional/wifi_sta_iface_hidl_test.cpp index 92f5d1453e..1b907b272f 100644 --- a/wifi/1.2/vts/functional/wifi_sta_iface_hidl_test.cpp +++ b/wifi/1.2/vts/functional/wifi_sta_iface_hidl_test.cpp @@ -19,9 +19,11 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.2/IWifi.h> #include <android/hardware/wifi/1.2/IWifiStaIface.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" @@ -34,14 +36,15 @@ using ::android::hardware::wifi::V1_2::IWifiStaIface; /** * Fixture to use for all STA Iface HIDL interface tests. */ -class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiStaIfaceHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - wifi_sta_iface_ = IWifiStaIface::castFrom(getWifiStaIface()); + wifi_sta_iface_ = + IWifiStaIface::castFrom(getWifiStaIface(GetInstanceName())); ASSERT_NE(nullptr, wifi_sta_iface_.get()); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: bool isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask cap_mask) { @@ -52,6 +55,9 @@ class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { } sp<IWifiStaIface> wifi_sta_iface_; + + private: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -59,7 +65,7 @@ class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that calls to set MAC address will return a success status * code. */ -TEST_F(WifiStaIfaceHidlTest, SetMacAddress) { +TEST_P(WifiStaIfaceHidlTest, SetMacAddress) { const android::hardware::hidl_array<uint8_t, 6> kMac{ std::array<uint8_t, 6>{{0x12, 0x22, 0x33, 0x52, 0x10, 0x41}}}; EXPECT_EQ(WifiStatusCode::SUCCESS, @@ -76,7 +82,7 @@ TEST_F(WifiStaIfaceHidlTest, SetMacAddress) { * TODO: We can't execute APF opcodes from this test because there's no way * to loop test packets through the wifi firmware (b/73804303#comment29). */ -TEST_F(WifiStaIfaceHidlTest, DISABLED_ReadApfPacketFilterData) { +TEST_P(WifiStaIfaceHidlTest, DISABLED_ReadApfPacketFilterData) { if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::APF)) { // Disable test if APF packet filer is not supported. LOG(WARNING) << "TEST SKIPPED: APF packet filtering not supported"; @@ -107,3 +113,9 @@ TEST_F(WifiStaIfaceHidlTest, DISABLED_ReadApfPacketFilterData) { EXPECT_EQ(status_and_data.second, data); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiStaIfaceHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames( + ::android::hardware::wifi::V1_2::IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.3/default/tests/wifi_ap_iface_unit_tests.cpp b/wifi/1.3/default/tests/wifi_ap_iface_unit_tests.cpp deleted file mode 100644 index 680f534c41..0000000000 --- a/wifi/1.3/default/tests/wifi_ap_iface_unit_tests.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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-base/macros.h> -#include <cutils/properties.h> -#include <gmock/gmock.h> - -#undef NAN // This is weird, NAN is defined in bionic/libc/include/math.h:38 -#include "wifi_ap_iface.h" - -#include "mock_interface_tool.h" -#include "mock_wifi_feature_flags.h" -#include "mock_wifi_iface_util.h" -#include "mock_wifi_legacy_hal.h" - -using testing::NiceMock; -using testing::Return; -using testing::Test; - -namespace { -constexpr char kIfaceName[] = "mockWlan0"; -} // namespace - -namespace android { -namespace hardware { -namespace wifi { -namespace V1_3 { -namespace implementation { - -class WifiApIfaceTest : public Test { - protected: - std::shared_ptr<NiceMock<wifi_system::MockInterfaceTool>> iface_tool_{ - new NiceMock<wifi_system::MockInterfaceTool>}; - std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{ - new NiceMock<legacy_hal::MockWifiLegacyHal>(iface_tool_)}; - std::shared_ptr<NiceMock<iface_util::MockWifiIfaceUtil>> iface_util_{ - new NiceMock<iface_util::MockWifiIfaceUtil>(iface_tool_)}; - std::shared_ptr<NiceMock<feature_flags::MockWifiFeatureFlags>> - feature_flags_{new NiceMock<feature_flags::MockWifiFeatureFlags>}; -}; - -TEST_F(WifiApIfaceTest, SetRandomMacAddressIfFeatureEnabled) { - EXPECT_CALL(*feature_flags_, isApMacRandomizationDisabled()) - .WillOnce(testing::Return(false)); - EXPECT_CALL(*iface_util_, getOrCreateRandomMacAddress()) - .WillOnce(testing::Return(std::array<uint8_t, 6>{0, 0, 0, 0, 0, 0})); - EXPECT_CALL(*iface_util_, setMacAddress(testing::_, testing::_)) - .WillOnce(testing::Return(true)); - sp<WifiApIface> ap_iface = - new WifiApIface(kIfaceName, legacy_hal_, iface_util_, feature_flags_); -} - -TEST_F(WifiApIfaceTest, DontSetRandomMacAddressIfFeatureDisabled) { - EXPECT_CALL(*feature_flags_, isApMacRandomizationDisabled()) - .WillOnce(testing::Return(true)); - EXPECT_CALL(*iface_util_, getOrCreateRandomMacAddress()).Times(0); - EXPECT_CALL(*iface_util_, setMacAddress(testing::_, testing::_)).Times(0); - sp<WifiApIface> ap_iface = - new WifiApIface(kIfaceName, legacy_hal_, iface_util_, feature_flags_); -} -} // namespace implementation -} // namespace V1_3 -} // namespace wifi -} // namespace hardware -} // namespace android diff --git a/wifi/1.3/vts/functional/Android.bp b/wifi/1.3/vts/functional/Android.bp index 9ffda8be47..fe9c791ec8 100644 --- a/wifi/1.3/vts/functional/Android.bp +++ b/wifi/1.3/vts/functional/Android.bp @@ -30,4 +30,6 @@ cc_test { "android.hardware.wifi@1.3", "libwifi-system-iface" ], + disable_framework: true, + test_suites: ["general-tests", "vts-core"], } diff --git a/wifi/1.3/vts/functional/VtsHalWifiV1_3TargetTest.cpp b/wifi/1.3/vts/functional/VtsHalWifiV1_3TargetTest.cpp index faf426e999..52c7a4ad0d 100644 --- a/wifi/1.3/vts/functional/VtsHalWifiV1_3TargetTest.cpp +++ b/wifi/1.3/vts/functional/VtsHalWifiV1_3TargetTest.cpp @@ -14,37 +14,8 @@ * limitations under the License. */ -#include <android-base/logging.h> -#include <android/hardware/wifi/1.3/IWifi.h> +#include <VtsHalHidlTargetTestEnvBase.h> -#include "wifi_hidl_test_utils.h" - -using ::android::hardware::wifi::V1_3::IWifi; - -// Test environment for Wifi HIDL HAL. -class WifiHidlEnvironment_1_3 : public WifiHidlEnvironment { - public: - // get the test environment singleton - static WifiHidlEnvironment_1_3* Instance() { - static WifiHidlEnvironment_1_3* instance = new WifiHidlEnvironment_1_3; - return instance; - } - - virtual void registerTestServices() override { - registerTestService<android::hardware::wifi::V1_3::IWifi>(); - } - - private: - WifiHidlEnvironment_1_3() {} -}; - -WifiHidlEnvironment_1_3* gEnv = WifiHidlEnvironment_1_3::Instance(); - -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(gEnv); - ::testing::InitGoogleTest(&argc, argv); - gEnv->init(&argc, argv); - int status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - return status; -} +// TODO(b/143892896): Remove this file after wifi_hidl_test_utils.cpp is +// updated. +::testing::VtsHalHidlTargetTestEnvBase* gEnv = nullptr;
\ No newline at end of file diff --git a/wifi/1.3/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.3/vts/functional/wifi_chip_hidl_test.cpp index d980fcb310..db939679ad 100644 --- a/wifi/1.3/vts/functional/wifi_chip_hidl_test.cpp +++ b/wifi/1.3/vts/functional/wifi_chip_hidl_test.cpp @@ -16,9 +16,11 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.3/IWifi.h> #include <android/hardware/wifi/1.3/IWifiChip.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" @@ -39,14 +41,14 @@ constexpr IWifiChip::LatencyMode kLatencyModeLow = IWifiChip::LatencyMode::LOW; /** * Fixture to use for all Wifi chip HIDL interface tests. */ -class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiChipHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - wifi_chip_ = IWifiChip::castFrom(getWifiChip()); + wifi_chip_ = IWifiChip::castFrom(getWifiChip(GetInstanceName())); ASSERT_NE(nullptr, wifi_chip_.get()); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: // Helper function to configure the Chip in one of the supported modes. @@ -70,6 +72,9 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { } sp<IWifiChip> wifi_chip_; + + private: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -77,7 +82,7 @@ class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase { * This test case tests the setLatencyMode() API with * Latency mode NORMAL */ -TEST_F(WifiChipHidlTest, SetLatencyMode_normal) { +TEST_P(WifiChipHidlTest, SetLatencyMode_normal) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status = HIDL_INVOKE(wifi_chip_, setLatencyMode, kLatencyModeNormal); @@ -92,7 +97,7 @@ TEST_F(WifiChipHidlTest, SetLatencyMode_normal) { * SetLatencyMode_low * This test case tests the setLatencyMode() API with Latency mode LOW */ -TEST_F(WifiChipHidlTest, SetLatencyMode_low) { +TEST_P(WifiChipHidlTest, SetLatencyMode_low) { uint32_t caps = configureChipForStaIfaceAndGetCapabilities(); const auto& status = HIDL_INVOKE(wifi_chip_, setLatencyMode, kLatencyModeLow); @@ -106,7 +111,7 @@ TEST_F(WifiChipHidlTest, SetLatencyMode_low) { /* * GetCapabilities_1_3 */ -TEST_F(WifiChipHidlTest, GetCapabilities_1_3) { +TEST_P(WifiChipHidlTest, GetCapabilities_1_3) { configureChipForIfaceType(IfaceType::STA, true); const auto& status_and_caps = HIDL_INVOKE(wifi_chip_, getCapabilities_1_3); if (status_and_caps.first.code != WifiStatusCode::SUCCESS) { @@ -116,3 +121,9 @@ TEST_F(WifiChipHidlTest, GetCapabilities_1_3) { } EXPECT_NE(0u, status_and_caps.second); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiChipHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames( + ::android::hardware::wifi::V1_3::IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp b/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp index 71e90acb93..c5acc3c1b2 100644 --- a/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp +++ b/wifi/1.3/vts/functional/wifi_sta_iface_hidl_test.cpp @@ -19,28 +19,33 @@ #include <android-base/logging.h> +#include <android/hardware/wifi/1.3/IWifi.h> #include <android/hardware/wifi/1.3/IWifiStaIface.h> - -#include <VtsHalHidlTargetTestBase.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> #include "wifi_hidl_call_util.h" #include "wifi_hidl_test_utils.h" using ::android::sp; +using ::android::hardware::hidl_array; +using ::android::hardware::wifi::V1_0::WifiStatus; using ::android::hardware::wifi::V1_0::WifiStatusCode; using ::android::hardware::wifi::V1_3::IWifiStaIface; /** * Fixture to use for all STA Iface HIDL interface tests. */ -class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class WifiStaIfaceHidlTest : public ::testing::TestWithParam<std::string> { public: virtual void SetUp() override { - wifi_sta_iface_ = IWifiStaIface::castFrom(getWifiStaIface()); + wifi_sta_iface_ = + IWifiStaIface::castFrom(getWifiStaIface(GetInstanceName())); ASSERT_NE(nullptr, wifi_sta_iface_.get()); } - virtual void TearDown() override { stopWifi(); } + virtual void TearDown() override { stopWifi(GetInstanceName()); } protected: bool isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask cap_mask) { @@ -51,6 +56,9 @@ class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { } sp<IWifiStaIface> wifi_sta_iface_; + + private: + std::string GetInstanceName() { return GetParam(); } }; /* @@ -58,15 +66,12 @@ class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { * Ensures that calls to get factory MAC address will retrieve a non-zero MAC * and return a success status code. */ -TEST_F(WifiStaIfaceHidlTest, GetFactoryMacAddress) { - const auto& status_and_mac = +TEST_P(WifiStaIfaceHidlTest, GetFactoryMacAddress) { + std::pair<WifiStatus, hidl_array<uint8_t, 6> > status_and_mac = HIDL_INVOKE(wifi_sta_iface_, getFactoryMacAddress); EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_mac.first.code); - const int num_elements = sizeof(status_and_mac.second) / sizeof(uint8_t); - EXPECT_EQ(6, num_elements); - for (int i = 0; i < num_elements; i++) { - EXPECT_NE(0, status_and_mac.second[i]); - } + hidl_array<uint8_t, 6> all_zero{}; + EXPECT_NE(all_zero, status_and_mac.second); } /* @@ -74,7 +79,7 @@ TEST_F(WifiStaIfaceHidlTest, GetFactoryMacAddress) { * Ensures that calls to get link layer stats V1_3 will retrieve a non-empty * StaLinkLayerStats after link layer stats collection is enabled. */ -TEST_F(WifiStaIfaceHidlTest, GetLinkLayerStats_1_3) { +TEST_P(WifiStaIfaceHidlTest, GetLinkLayerStats_1_3) { if (!isCapabilitySupported( IWifiStaIface::StaIfaceCapabilityMask::LINK_LAYER_STATS)) { // No-op if link layer stats is not supported. @@ -95,3 +100,9 @@ TEST_F(WifiStaIfaceHidlTest, GetLinkLayerStats_1_3) { WifiStatusCode::SUCCESS, HIDL_INVOKE(wifi_sta_iface_, disableLinkLayerStatsCollection).code); } + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiStaIfaceHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames( + ::android::hardware::wifi::V1_3::IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.4/Android.bp b/wifi/1.4/Android.bp new file mode 100644 index 0000000000..5750e426ad --- /dev/null +++ b/wifi/1.4/Android.bp @@ -0,0 +1,26 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.wifi@1.4", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IWifi.hal", + "IWifiApIface.hal", + "IWifiChip.hal", + "IWifiRttController.hal", + "IWifiRttControllerEventCallback.hal", + "IWifiStaIface.hal", + ], + interfaces: [ + "android.hardware.wifi@1.0", + "android.hardware.wifi@1.1", + "android.hardware.wifi@1.2", + "android.hardware.wifi@1.3", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/wifi/1.4/IWifi.hal b/wifi/1.4/IWifi.hal new file mode 100644 index 0000000000..765e09d16a --- /dev/null +++ b/wifi/1.4/IWifi.hal @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package android.hardware.wifi@1.4; + +import @1.3::IWifi; + +/** + * This is the root of the HAL module and is the interface returned when + * loading an implementation of the Wi-Fi HAL. There must be at most one + * module loaded in the system. + * IWifi.getChip() must return @1.2::IWifiChip + */ +interface IWifi extends @1.3::IWifi {}; diff --git a/wifi/1.4/IWifiApIface.hal b/wifi/1.4/IWifiApIface.hal new file mode 100644 index 0000000000..80c576dbfc --- /dev/null +++ b/wifi/1.4/IWifiApIface.hal @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package android.hardware.wifi@1.4; + +import @1.0::IWifiApIface; +import @1.0::MacAddress; +import @1.0::WifiStatus; + +/** + * Represents a network interface in AP mode. + * + * This can be obtained through @1.0::IWifiChip.getApIface() and casting + * IWifiApIface up to 1.4. + */ +interface IWifiApIface extends @1.0::IWifiApIface { + /** + * Changes the MAC address of the interface to the given MAC address. + * + * @param mac MAC address to change to. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|, + * |WifiStatusCode.ERROR_UNKNOWN| + */ + setMacAddress(MacAddress mac) generates (WifiStatus status); + + /** + * Gets the factory MAC address of the interface. + * + * @return status WifiStatus of the operation + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|, + * |WifiStatusCode.ERROR_UNKNOWN| + * @return mac factory MAC address of the interface + */ + getFactoryMacAddress() generates (WifiStatus status, MacAddress mac); +}; diff --git a/wifi/1.4/IWifiChip.hal b/wifi/1.4/IWifiChip.hal new file mode 100644 index 0000000000..d269427a6d --- /dev/null +++ b/wifi/1.4/IWifiChip.hal @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package android.hardware.wifi@1.4; + +import @1.0::WifiStatus; +import @1.0::IWifiIface; +import @1.3::IWifiChip; +import IWifiRttController; + +/** + * Interface that represents a chip that must be configured as a single unit. + */ +interface IWifiChip extends @1.3::IWifiChip { + /** + * Create a RTTController instance. + * + * RTT controller can be either: + * a) Bound to a specific iface by passing in the corresponding |IWifiIface| + * object in |iface| param, OR + * b) Let the implementation decide the iface to use for RTT operations by + * passing null in |iface| param. + * + * @param boundIface HIDL interface object representing the iface if + * the responder must be bound to a specific iface, null otherwise. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_CHIP_INVALID| + */ + createRttController_1_4(IWifiIface boundIface) + generates (WifiStatus status, IWifiRttController rtt); +}; diff --git a/wifi/1.4/IWifiRttController.hal b/wifi/1.4/IWifiRttController.hal new file mode 100644 index 0000000000..5c71975f2f --- /dev/null +++ b/wifi/1.4/IWifiRttController.hal @@ -0,0 +1,102 @@ +/* + * 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. + */ + +package android.hardware.wifi@1.4; + +import @1.0::IWifiRttController; +import @1.0::CommandId; +import @1.0::WifiChannelInfo; +import @1.0::WifiStatus; +import IWifiRttControllerEventCallback; + +/** + * Interface used to perform RTT(Round trip time) operations. + */ +interface IWifiRttController extends @1.0::IWifiRttController { + /** + * Requests notifications of significant events on this rtt controller. + * Multiple calls to this must register multiple callbacks each of which must + * receive all events. + * + * @param callback An instance of the |IWifiRttControllerEventCallback| HIDL + * interface object. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID| + */ + registerEventCallback_1_4(IWifiRttControllerEventCallback callback) + generates (WifiStatus status); + + /** + * API to request RTT measurement. + * + * @param cmdId command Id to use for this invocation. + * @param rttConfigs Vector of |RttConfig| parameters. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_INVALID_ARGS|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_UNKNOWN| + */ + rangeRequest_1_4(CommandId cmdId, vec<RttConfig> rttConfigs) generates (WifiStatus status); + + /** + * RTT capabilities of the device. + * + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_UNKNOWN| + * @return capabilities Instance of |RttCapabilities|. + */ + getCapabilities_1_4() generates (WifiStatus status, RttCapabilities capabilities); + + /** + * Get RTT responder information e.g. WiFi channel to enable responder on. + * + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_UNKNOWN| + * @return info Instance of |RttResponderInfo|. + */ + getResponderInfo_1_4() generates (WifiStatus status, RttResponder info); + + /** + * Enable RTT responder mode. + * + * @param cmdId command Id to use for this invocation. + * @parm channelHint Hint of the channel information where RTT responder must + * be enabled on. + * @param maxDurationInSeconds Timeout of responder mode. + * @param info Instance of |RttResponderInfo|. + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_RTT_CONTROLLER_INVALID|, + * |WifiStatusCode.ERROR_INVALID_ARGS|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_UNKNOWN| + */ + enableResponder_1_4(CommandId cmdId, WifiChannelInfo channelHint, + uint32_t maxDurationInSeconds, RttResponder info) generates (WifiStatus status); +}; diff --git a/wifi/1.4/IWifiRttControllerEventCallback.hal b/wifi/1.4/IWifiRttControllerEventCallback.hal new file mode 100644 index 0000000000..75de3d43d3 --- /dev/null +++ b/wifi/1.4/IWifiRttControllerEventCallback.hal @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package android.hardware.wifi@1.4; + +import @1.0::IWifiRttControllerEventCallback; +import @1.0::CommandId; + +/** + * RTT Response and Event Callbacks. + */ +interface IWifiRttControllerEventCallback extends @1.0::IWifiRttControllerEventCallback { + /* + * Invoked when an RTT result is available. + * + * @param cmdId command Id corresponding to the original request. + * @param results Vector of |RttResult| instances. + */ + oneway onResults_1_4(CommandId cmdId, vec<RttResult> results); +}; diff --git a/wifi/1.4/IWifiStaIface.hal b/wifi/1.4/IWifiStaIface.hal new file mode 100644 index 0000000000..fb658cd37c --- /dev/null +++ b/wifi/1.4/IWifiStaIface.hal @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package android.hardware.wifi@1.4; + +import @1.0::WifiStatus; +import @1.0::MacAddress; +import @1.0::IWifiStaIface; +import @1.3::IWifiStaIface; + +/** + * Interface used to represent a single STA iface. + * + * IWifiChip.createStaIface() may return a @1.4::IWifiStaIface when supported. + */ +interface IWifiStaIface extends @1.3::IWifiStaIface { + + enum StaIfaceCapabilityMask : @1.0::IWifiStaIface.StaIfaceCapabilityMask { + STA_6G = 1 << 15 + }; + + /** + * Get the capabilities supported by this STA iface. + * + * @return status WifiStatus of the operation. + * Possible status codes: + * |WifiStatusCode.SUCCESS|, + * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|, + * |WifiStatusCode.ERROR_NOT_AVAILABLE|, + * |WifiStatusCode.ERROR_NOT_SUPPORTED|, + * |WifiStatusCode.ERROR_UNKNOWN| + * @return capabilities Bitset of |StaIfaceCapabilityMask| values. + */ + getCapabilities_1_4() + generates (WifiStatus status, + bitfield<StaIfaceCapabilityMask> capabilities); +}; diff --git a/wifi/1.3/default/Android.mk b/wifi/1.4/default/Android.mk index 29f1c4256f..ab76ff66e2 100644 --- a/wifi/1.3/default/Android.mk +++ b/wifi/1.4/default/Android.mk @@ -67,7 +67,8 @@ LOCAL_SHARED_LIBRARIES := \ android.hardware.wifi@1.0 \ android.hardware.wifi@1.1 \ android.hardware.wifi@1.2 \ - android.hardware.wifi@1.3 + android.hardware.wifi@1.3 \ + android.hardware.wifi@1.4 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) include $(BUILD_STATIC_LIBRARY) @@ -76,6 +77,7 @@ include $(BUILD_STATIC_LIBRARY) ### include $(CLEAR_VARS) LOCAL_MODULE := android.hardware.wifi@1.0-service +LOCAL_VINTF_FRAGMENTS := android.hardware.wifi@1.0-service.xml LOCAL_MODULE_RELATIVE_PATH := hw LOCAL_PROPRIETARY_MODULE := true LOCAL_CPPFLAGS := -Wall -Werror -Wextra @@ -93,7 +95,8 @@ LOCAL_SHARED_LIBRARIES := \ android.hardware.wifi@1.0 \ android.hardware.wifi@1.1 \ android.hardware.wifi@1.2 \ - android.hardware.wifi@1.3 + android.hardware.wifi@1.3 \ + android.hardware.wifi@1.4 LOCAL_STATIC_LIBRARIES := \ android.hardware.wifi@1.0-service-lib LOCAL_INIT_RC := android.hardware.wifi@1.0-service.rc @@ -104,6 +107,7 @@ include $(BUILD_EXECUTABLE) ### include $(CLEAR_VARS) LOCAL_MODULE := android.hardware.wifi@1.0-service-lazy +LOCAL_VINTF_FRAGMENTS := android.hardware.wifi@1.0-service.xml LOCAL_OVERRIDES_MODULES := android.hardware.wifi@1.0-service LOCAL_CFLAGS := -DLAZY_SERVICE LOCAL_MODULE_RELATIVE_PATH := hw @@ -123,7 +127,8 @@ LOCAL_SHARED_LIBRARIES := \ android.hardware.wifi@1.0 \ android.hardware.wifi@1.1 \ android.hardware.wifi@1.2 \ - android.hardware.wifi@1.3 + android.hardware.wifi@1.3 \ + android.hardware.wifi@1.4 LOCAL_STATIC_LIBRARIES := \ android.hardware.wifi@1.0-service-lib LOCAL_INIT_RC := android.hardware.wifi@1.0-service-lazy.rc @@ -145,7 +150,6 @@ LOCAL_SRC_FILES := \ tests/mock_wifi_legacy_hal.cpp \ tests/mock_wifi_mode_controller.cpp \ tests/ringbuffer_unit_tests.cpp \ - tests/wifi_ap_iface_unit_tests.cpp \ tests/wifi_nan_iface_unit_tests.cpp \ tests/wifi_chip_unit_tests.cpp \ tests/wifi_iface_util_unit_tests.cpp @@ -165,5 +169,6 @@ LOCAL_SHARED_LIBRARIES := \ android.hardware.wifi@1.0 \ android.hardware.wifi@1.1 \ android.hardware.wifi@1.2 \ - android.hardware.wifi@1.3 + android.hardware.wifi@1.3 \ + android.hardware.wifi@1.4 include $(BUILD_NATIVE_TEST) diff --git a/wifi/1.3/default/OWNERS b/wifi/1.4/default/OWNERS index 8bfb14882c..8bfb14882c 100644 --- a/wifi/1.3/default/OWNERS +++ b/wifi/1.4/default/OWNERS diff --git a/wifi/1.3/default/THREADING.README b/wifi/1.4/default/THREADING.README index 8366ca0201..8366ca0201 100644 --- a/wifi/1.3/default/THREADING.README +++ b/wifi/1.4/default/THREADING.README diff --git a/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc b/wifi/1.4/default/android.hardware.wifi@1.0-service-lazy.rc index cf917b5458..cf917b5458 100644 --- a/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc +++ b/wifi/1.4/default/android.hardware.wifi@1.0-service-lazy.rc diff --git a/wifi/1.3/default/android.hardware.wifi@1.0-service.rc b/wifi/1.4/default/android.hardware.wifi@1.0-service.rc index 2317bac546..2317bac546 100644 --- a/wifi/1.3/default/android.hardware.wifi@1.0-service.rc +++ b/wifi/1.4/default/android.hardware.wifi@1.0-service.rc diff --git a/wifi/1.4/default/android.hardware.wifi@1.0-service.xml b/wifi/1.4/default/android.hardware.wifi@1.0-service.xml new file mode 100644 index 0000000000..b5d25cd140 --- /dev/null +++ b/wifi/1.4/default/android.hardware.wifi@1.0-service.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="device"> + <hal format="hidl"> + <name>android.hardware.wifi</name> + <transport>hwbinder</transport> + <version>1.4</version> + <interface> + <name>IWifi</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/wifi/1.3/default/hidl_callback_util.h b/wifi/1.4/default/hidl_callback_util.h index a44af79fe3..fc601b804a 100644 --- a/wifi/1.3/default/hidl_callback_util.h +++ b/wifi/1.4/default/hidl_callback_util.h @@ -52,7 +52,7 @@ class HidlDeathHandler : public android::hardware::hidl_death_recipient { namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace hidl_callback_util { template <typename CallbackType> @@ -117,7 +117,7 @@ class HidlCallbackHandler { } // namespace hidl_callback_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/hidl_return_util.h b/wifi/1.4/default/hidl_return_util.h index 9707444fe6..99c70928b7 100644 --- a/wifi/1.3/default/hidl_return_util.h +++ b/wifi/1.4/default/hidl_return_util.h @@ -23,7 +23,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace hidl_return_util { using namespace android::hardware::wifi::V1_0; @@ -113,7 +113,7 @@ Return<void> validateAndCall( } // namespace hidl_return_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/hidl_struct_util.cpp b/wifi/1.4/default/hidl_struct_util.cpp index 2e4db70480..13a09f3438 100644 --- a/wifi/1.3/default/hidl_struct_util.cpp +++ b/wifi/1.4/default/hidl_struct_util.cpp @@ -22,7 +22,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace hidl_struct_util { @@ -91,7 +91,7 @@ V1_3::IWifiChip::ChipCapabilityMask convertLegacyFeatureToHidlChipCapability( } IWifiStaIface::StaIfaceCapabilityMask -convertLegacyFeatureToHidlStaIfaceCapability(uint32_t feature) { +convertLegacyFeatureToHidlStaIfaceCapability(uint64_t feature) { using HidlStaIfaceCaps = IWifiStaIface::StaIfaceCapabilityMask; switch (feature) { case WIFI_FEATURE_GSCAN: @@ -120,6 +120,8 @@ convertLegacyFeatureToHidlStaIfaceCapability(uint32_t feature) { return HidlStaIfaceCaps::ND_OFFLOAD; case WIFI_FEATURE_MKEEP_ALIVE: return HidlStaIfaceCaps::KEEP_ALIVE; + case WIFI_FEATURE_INFRA_6G: + return HidlStaIfaceCaps::STA_6G; }; CHECK(false) << "Unknown legacy feature: " << feature; return {}; @@ -302,11 +304,11 @@ legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy_1_2( } legacy_hal::wifi_latency_mode convertHidlLatencyModeToLegacy( - IWifiChip::LatencyMode hidl_latency_mode) { + V1_3::IWifiChip::LatencyMode hidl_latency_mode) { switch (hidl_latency_mode) { - case IWifiChip::LatencyMode::NORMAL: + case V1_3::IWifiChip::LatencyMode::NORMAL: return legacy_hal::WIFI_LATENCY_MODE_NORMAL; - case IWifiChip::LatencyMode::LOW: + case V1_3::IWifiChip::LatencyMode::LOW: return legacy_hal::WIFI_LATENCY_MODE_LOW; } CHECK(false); @@ -365,7 +367,7 @@ bool convertLegacyWifiMacInfosToHidl( } bool convertLegacyFeaturesToHidlStaCapabilities( - uint32_t legacy_feature_set, uint32_t legacy_logger_feature_set, + uint64_t legacy_feature_set, uint32_t legacy_logger_feature_set, uint32_t* hidl_caps) { if (!hidl_caps) { return false; @@ -384,7 +386,8 @@ bool convertLegacyFeaturesToHidlStaCapabilities( WIFI_FEATURE_IE_WHITELIST, WIFI_FEATURE_SCAN_RAND, WIFI_FEATURE_INFRA_5G, WIFI_FEATURE_HOTSPOT, WIFI_FEATURE_PNO, WIFI_FEATURE_TDLS, WIFI_FEATURE_TDLS_OFFCHANNEL, - WIFI_FEATURE_CONFIG_NDO, WIFI_FEATURE_MKEEP_ALIVE}) { + WIFI_FEATURE_CONFIG_NDO, WIFI_FEATURE_MKEEP_ALIVE, + WIFI_FEATURE_INFRA_6G}) { if (feature & legacy_feature_set) { *hidl_caps |= convertLegacyFeatureToHidlStaIfaceCapability(feature); } @@ -2279,6 +2282,8 @@ legacy_hal::wifi_rtt_preamble convertHidlRttPreambleToLegacy(RttPreamble type) { return legacy_hal::WIFI_RTT_PREAMBLE_HT; case RttPreamble::VHT: return legacy_hal::WIFI_RTT_PREAMBLE_VHT; + case RttPreamble::HE: + return legacy_hal::WIFI_RTT_PREAMBLE_HE; }; CHECK(false); } @@ -2291,6 +2296,8 @@ RttPreamble convertLegacyRttPreambleToHidl(legacy_hal::wifi_rtt_preamble type) { return RttPreamble::HT; case legacy_hal::WIFI_RTT_PREAMBLE_VHT: return RttPreamble::VHT; + case legacy_hal::WIFI_RTT_PREAMBLE_HE: + return RttPreamble::HE; }; CHECK(false) << "Unknown legacy type: " << type; } @@ -2354,6 +2361,8 @@ WifiRatePreamble convertLegacyWifiRatePreambleToHidl(uint8_t preamble) { return WifiRatePreamble::HT; case 3: return WifiRatePreamble::VHT; + case 4: + return WifiRatePreamble::HE; default: return WifiRatePreamble::RESERVED; }; @@ -2579,9 +2588,10 @@ bool convertLegacyRttCapabilitiesToHidl( hidl_capabilities->responderSupported = legacy_capabilities.responder_supported; hidl_capabilities->preambleSupport = 0; - for (const auto flag : {legacy_hal::WIFI_RTT_PREAMBLE_LEGACY, - legacy_hal::WIFI_RTT_PREAMBLE_HT, - legacy_hal::WIFI_RTT_PREAMBLE_VHT}) { + for (const auto flag : + {legacy_hal::WIFI_RTT_PREAMBLE_LEGACY, + legacy_hal::WIFI_RTT_PREAMBLE_HT, legacy_hal::WIFI_RTT_PREAMBLE_VHT, + legacy_hal::WIFI_RTT_PREAMBLE_HE}) { if (legacy_capabilities.preamble_support & flag) { hidl_capabilities->preambleSupport |= static_cast<std::underlying_type<RttPreamble>::type>( @@ -2683,7 +2693,7 @@ bool convertLegacyVectorOfRttResultToHidl( } } // namespace hidl_struct_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/hidl_struct_util.h b/wifi/1.4/default/hidl_struct_util.h index 3eefd9592b..cfaa4adccc 100644 --- a/wifi/1.3/default/hidl_struct_util.h +++ b/wifi/1.4/default/hidl_struct_util.h @@ -25,6 +25,8 @@ #include <android/hardware/wifi/1.2/types.h> #include <android/hardware/wifi/1.3/IWifiChip.h> #include <android/hardware/wifi/1.3/types.h> +#include <android/hardware/wifi/1.4/IWifiStaIface.h> +#include <android/hardware/wifi/1.4/types.h> #include "wifi_legacy_hal.h" @@ -37,7 +39,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace hidl_struct_util { using namespace android::hardware::wifi::V1_0; @@ -68,7 +70,7 @@ bool convertLegacyWifiMacInfosToHidl( // STA iface conversion methods. bool convertLegacyFeaturesToHidlStaCapabilities( - uint32_t legacy_feature_set, uint32_t legacy_logger_feature_set, + uint64_t legacy_feature_set, uint32_t legacy_logger_feature_set, uint32_t* hidl_caps); bool convertLegacyApfCapabilitiesToHidl( const legacy_hal::PacketFilterCapabilities& legacy_caps, @@ -188,7 +190,7 @@ bool convertLegacyVectorOfRttResultToHidl( std::vector<RttResult>* hidl_results); } // namespace hidl_struct_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/hidl_sync_util.cpp b/wifi/1.4/default/hidl_sync_util.cpp index 160727f609..593a3bc5be 100644 --- a/wifi/1.3/default/hidl_sync_util.cpp +++ b/wifi/1.4/default/hidl_sync_util.cpp @@ -23,7 +23,7 @@ std::recursive_mutex g_mutex; namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace hidl_sync_util { @@ -33,7 +33,7 @@ std::unique_lock<std::recursive_mutex> acquireGlobalLock() { } // namespace hidl_sync_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/hidl_sync_util.h b/wifi/1.4/default/hidl_sync_util.h index ebfb05144b..02444219e9 100644 --- a/wifi/1.3/default/hidl_sync_util.h +++ b/wifi/1.4/default/hidl_sync_util.h @@ -24,13 +24,13 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace hidl_sync_util { std::unique_lock<std::recursive_mutex> acquireGlobalLock(); } // namespace hidl_sync_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/ringbuffer.cpp b/wifi/1.4/default/ringbuffer.cpp index 1294c52982..0fe8ef45aa 100644 --- a/wifi/1.3/default/ringbuffer.cpp +++ b/wifi/1.4/default/ringbuffer.cpp @@ -21,7 +21,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { Ringbuffer::Ringbuffer(size_t maxSize) : size_(0), maxSize_(maxSize) {} @@ -48,7 +48,7 @@ const std::list<std::vector<uint8_t>>& Ringbuffer::getData() const { } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/ringbuffer.h b/wifi/1.4/default/ringbuffer.h index d9f8df685d..ddce648949 100644 --- a/wifi/1.3/default/ringbuffer.h +++ b/wifi/1.4/default/ringbuffer.h @@ -23,7 +23,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { /** @@ -45,7 +45,7 @@ class Ringbuffer { }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/service.cpp b/wifi/1.4/default/service.cpp index 0b41d28da9..3f7f6094fd 100644 --- a/wifi/1.3/default/service.cpp +++ b/wifi/1.4/default/service.cpp @@ -28,11 +28,11 @@ using android::hardware::configureRpcThreadpool; using android::hardware::joinRpcThreadpool; using android::hardware::LazyServiceRegistrar; -using android::hardware::wifi::V1_3::implementation::feature_flags:: +using android::hardware::wifi::V1_4::implementation::feature_flags:: WifiFeatureFlags; -using android::hardware::wifi::V1_3::implementation::iface_util::WifiIfaceUtil; -using android::hardware::wifi::V1_3::implementation::legacy_hal::WifiLegacyHal; -using android::hardware::wifi::V1_3::implementation::mode_controller:: +using android::hardware::wifi::V1_4::implementation::iface_util::WifiIfaceUtil; +using android::hardware::wifi::V1_4::implementation::legacy_hal::WifiLegacyHal; +using android::hardware::wifi::V1_4::implementation::mode_controller:: WifiModeController; #ifdef LAZY_SERVICE @@ -51,8 +51,8 @@ int main(int /*argc*/, char** argv) { const auto iface_tool = std::make_shared<android::wifi_system::InterfaceTool>(); // Setup hwbinder service - android::sp<android::hardware::wifi::V1_3::IWifi> service = - new android::hardware::wifi::V1_3::implementation::Wifi( + android::sp<android::hardware::wifi::V1_4::IWifi> service = + new android::hardware::wifi::V1_4::implementation::Wifi( iface_tool, std::make_shared<WifiLegacyHal>(iface_tool), std::make_shared<WifiModeController>(), std::make_shared<WifiIfaceUtil>(iface_tool), diff --git a/wifi/1.3/default/tests/hidl_struct_util_unit_tests.cpp b/wifi/1.4/default/tests/hidl_struct_util_unit_tests.cpp index 4e9ebded12..14a15048fe 100644 --- a/wifi/1.3/default/tests/hidl_struct_util_unit_tests.cpp +++ b/wifi/1.4/default/tests/hidl_struct_util_unit_tests.cpp @@ -34,7 +34,7 @@ constexpr char kIfaceName2[] = "wlan1"; namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using namespace android::hardware::wifi::V1_0; using ::android::hardware::wifi::V1_0::WifiChannelWidthInMhz; @@ -277,9 +277,9 @@ TEST_F(HidlStructUtilTest, CanConvertLegacyFeaturesToHidl) { uint32_t hidle_caps; uint32_t legacy_feature_set = - WIFI_FEATURE_D2D_RTT | WIFI_FEATURE_SET_LATENCY_MODE; + WIFI_FEATURE_D2D_RTT | WIFI_FEATURE_SET_LATENCY_MODE; uint32_t legacy_logger_feature_set = - legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED; + legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED; ASSERT_TRUE(hidl_struct_util::convertLegacyFeaturesToHidlChipCapabilities( legacy_feature_set, legacy_logger_feature_set, &hidle_caps)); @@ -292,7 +292,7 @@ TEST_F(HidlStructUtilTest, CanConvertLegacyFeaturesToHidl) { hidle_caps); } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/main.cpp b/wifi/1.4/default/tests/main.cpp index 9aac837242..9aac837242 100644 --- a/wifi/1.3/default/tests/main.cpp +++ b/wifi/1.4/default/tests/main.cpp diff --git a/wifi/1.3/default/tests/mock_interface_tool.cpp b/wifi/1.4/default/tests/mock_interface_tool.cpp index b99a16446c..b99a16446c 100644 --- a/wifi/1.3/default/tests/mock_interface_tool.cpp +++ b/wifi/1.4/default/tests/mock_interface_tool.cpp diff --git a/wifi/1.3/default/tests/mock_interface_tool.h b/wifi/1.4/default/tests/mock_interface_tool.h index 0f17551f96..0f17551f96 100644 --- a/wifi/1.3/default/tests/mock_interface_tool.h +++ b/wifi/1.4/default/tests/mock_interface_tool.h diff --git a/wifi/1.3/default/tests/mock_wifi_feature_flags.cpp b/wifi/1.4/default/tests/mock_wifi_feature_flags.cpp index a393fdc539..b1fa432ca3 100644 --- a/wifi/1.3/default/tests/mock_wifi_feature_flags.cpp +++ b/wifi/1.4/default/tests/mock_wifi_feature_flags.cpp @@ -21,7 +21,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace feature_flags { @@ -29,7 +29,7 @@ MockWifiFeatureFlags::MockWifiFeatureFlags() {} } // namespace feature_flags } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/mock_wifi_feature_flags.h b/wifi/1.4/default/tests/mock_wifi_feature_flags.h index ee12b54c78..72d23045b0 100644 --- a/wifi/1.3/default/tests/mock_wifi_feature_flags.h +++ b/wifi/1.4/default/tests/mock_wifi_feature_flags.h @@ -25,7 +25,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace feature_flags { @@ -39,7 +39,7 @@ class MockWifiFeatureFlags : public WifiFeatureFlags { } // namespace feature_flags } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/mock_wifi_iface_util.cpp b/wifi/1.4/default/tests/mock_wifi_iface_util.cpp index 3d877c0cbf..0968569c56 100644 --- a/wifi/1.3/default/tests/mock_wifi_iface_util.cpp +++ b/wifi/1.4/default/tests/mock_wifi_iface_util.cpp @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace iface_util { @@ -33,7 +33,7 @@ MockWifiIfaceUtil::MockWifiIfaceUtil( : WifiIfaceUtil(iface_tool) {} } // namespace iface_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/mock_wifi_iface_util.h b/wifi/1.4/default/tests/mock_wifi_iface_util.h index 8ec93eb471..6cc81e4083 100644 --- a/wifi/1.3/default/tests/mock_wifi_iface_util.h +++ b/wifi/1.4/default/tests/mock_wifi_iface_util.h @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace iface_util { @@ -43,7 +43,7 @@ class MockWifiIfaceUtil : public WifiIfaceUtil { }; } // namespace iface_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/mock_wifi_legacy_hal.cpp b/wifi/1.4/default/tests/mock_wifi_legacy_hal.cpp index 0a202c42d2..8d65c59e25 100644 --- a/wifi/1.3/default/tests/mock_wifi_legacy_hal.cpp +++ b/wifi/1.4/default/tests/mock_wifi_legacy_hal.cpp @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace legacy_hal { @@ -33,7 +33,7 @@ MockWifiLegacyHal::MockWifiLegacyHal( : WifiLegacyHal(iface_tool) {} } // namespace legacy_hal } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/mock_wifi_legacy_hal.h b/wifi/1.4/default/tests/mock_wifi_legacy_hal.h index 81cb1ded86..6942c1e4e3 100644 --- a/wifi/1.3/default/tests/mock_wifi_legacy_hal.h +++ b/wifi/1.4/default/tests/mock_wifi_legacy_hal.h @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace legacy_hal { @@ -41,9 +41,9 @@ class MockWifiLegacyHal : public WifiLegacyHal { wifi_error(const std::string&, const on_radio_mode_change_callback&)); MOCK_METHOD1(getFirmwareVersion, std::pair<wifi_error, std::string>( - const std::string& iface_name)); + const std::string& iface_name)); MOCK_METHOD1(getDriverVersion, std::pair<wifi_error, std::string>( - const std::string& iface_name)); + const std::string& iface_name)); MOCK_METHOD2(selectTxPowerScenario, wifi_error(const std::string& iface_name, @@ -60,7 +60,7 @@ class MockWifiLegacyHal : public WifiLegacyHal { }; } // namespace legacy_hal } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/mock_wifi_mode_controller.cpp b/wifi/1.4/default/tests/mock_wifi_mode_controller.cpp index 2b0ea366f3..ee09029820 100644 --- a/wifi/1.3/default/tests/mock_wifi_mode_controller.cpp +++ b/wifi/1.4/default/tests/mock_wifi_mode_controller.cpp @@ -24,14 +24,14 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace mode_controller { MockWifiModeController::MockWifiModeController() : WifiModeController() {} } // namespace mode_controller } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/mock_wifi_mode_controller.h b/wifi/1.4/default/tests/mock_wifi_mode_controller.h index c204059e67..1e1ce696f0 100644 --- a/wifi/1.3/default/tests/mock_wifi_mode_controller.h +++ b/wifi/1.4/default/tests/mock_wifi_mode_controller.h @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace mode_controller { @@ -38,7 +38,7 @@ class MockWifiModeController : public WifiModeController { }; } // namespace mode_controller } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/ringbuffer_unit_tests.cpp b/wifi/1.4/default/tests/ringbuffer_unit_tests.cpp index 0cf1e4f256..a65347f7ac 100644 --- a/wifi/1.3/default/tests/ringbuffer_unit_tests.cpp +++ b/wifi/1.4/default/tests/ringbuffer_unit_tests.cpp @@ -24,7 +24,7 @@ using testing::Test; namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { class RingbufferTest : public Test { @@ -91,7 +91,7 @@ TEST_F(RingbufferTest, OversizedAppendDoesNotDropExistingData) { EXPECT_EQ(input, buffer_.getData().front()); } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/runtests.sh b/wifi/1.4/default/tests/runtests.sh index 6bce3ef8c4..6bce3ef8c4 100755 --- a/wifi/1.3/default/tests/runtests.sh +++ b/wifi/1.4/default/tests/runtests.sh diff --git a/wifi/1.3/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.4/default/tests/wifi_chip_unit_tests.cpp index d8ce2785b7..90e81e12c1 100644 --- a/wifi/1.3/default/tests/wifi_chip_unit_tests.cpp +++ b/wifi/1.4/default/tests/wifi_chip_unit_tests.cpp @@ -41,7 +41,7 @@ constexpr ChipId kFakeChipId = 5; namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { class WifiChipTest : public Test { @@ -173,8 +173,9 @@ class WifiChipTest : public Test { std::string createIface(const IfaceType& type) { std::string iface_name; if (type == IfaceType::AP) { - chip_->createApIface([&iface_name](const WifiStatus& status, - const sp<IWifiApIface>& iface) { + chip_->createApIface([&iface_name]( + const WifiStatus& status, + const sp<V1_0::IWifiApIface>& iface) { if (WifiStatusCode::SUCCESS == status.code) { ASSERT_NE(iface.get(), nullptr); iface->getName([&iface_name](const WifiStatus& status, @@ -251,7 +252,7 @@ class WifiChipTest : public Test { bool createRttController() { bool success = false; - chip_->createRttController( + chip_->createRttController_1_4( NULL, [&success](const WifiStatus& status, const sp<IWifiRttController>& rtt) { if (WifiStatusCode::SUCCESS == status.code) { @@ -749,7 +750,7 @@ TEST_F(WifiChipV2_AwareIfaceCombinationTest, // Create RTT controller sp<IWifiRttController> rtt_controller; - chip_->createRttController( + chip_->createRttController_1_4( NULL, [&rtt_controller](const WifiStatus& status, const sp<IWifiRttController>& rtt) { if (WifiStatusCode::SUCCESS == status.code) { @@ -865,7 +866,7 @@ TEST_F(WifiChip_MultiIfaceTest, CreateApStartsWithIdx1) { ASSERT_EQ(createIface(IfaceType::STA), "wlan3"); } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/wifi_iface_util_unit_tests.cpp b/wifi/1.4/default/tests/wifi_iface_util_unit_tests.cpp index 28d23ffa71..03394bc209 100644 --- a/wifi/1.3/default/tests/wifi_iface_util_unit_tests.cpp +++ b/wifi/1.4/default/tests/wifi_iface_util_unit_tests.cpp @@ -41,7 +41,7 @@ bool isValidUnicastLocallyAssignedMacAddress( namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace iface_util { class WifiIfaceUtilTest : public Test { @@ -90,7 +90,7 @@ TEST_F(WifiIfaceUtilTest, IfaceEventHandlers_SetMacAddress) { } } // namespace iface_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/tests/wifi_nan_iface_unit_tests.cpp b/wifi/1.4/default/tests/wifi_nan_iface_unit_tests.cpp index eb6c61065d..8aefa92412 100644 --- a/wifi/1.3/default/tests/wifi_nan_iface_unit_tests.cpp +++ b/wifi/1.4/default/tests/wifi_nan_iface_unit_tests.cpp @@ -38,7 +38,7 @@ constexpr char kIfaceName[] = "mockWlan0"; namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { bool CaptureIfaceEventHandlers( @@ -142,7 +142,7 @@ TEST_F(WifiNanIfaceTest, IfacEventHandlers_OnStateToggleOffOn) { captured_iface_event_handlers.on_state_toggle_off_on(kIfaceName); } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi.cpp b/wifi/1.4/default/wifi.cpp index 2f21819dbc..4f48d7e7a6 100644 --- a/wifi/1.3/default/wifi.cpp +++ b/wifi/1.4/default/wifi.cpp @@ -28,7 +28,7 @@ static constexpr android::hardware::wifi::V1_0::ChipId kChipId = 0; namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using hidl_return_util::validateAndCall; using hidl_return_util::validateAndCallWithLock; @@ -210,7 +210,7 @@ WifiStatus Wifi::stopLegacyHalAndDeinitializeModeController( return createWifiStatus(WifiStatusCode::SUCCESS); } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi.h b/wifi/1.4/default/wifi.h index 1c2a15409e..087d6f71e8 100644 --- a/wifi/1.3/default/wifi.h +++ b/wifi/1.4/default/wifi.h @@ -20,7 +20,7 @@ #include <functional> #include <android-base/macros.h> -#include <android/hardware/wifi/1.3/IWifi.h> +#include <android/hardware/wifi/1.4/IWifi.h> #include <utils/Looper.h> #include "hidl_callback_util.h" @@ -32,13 +32,13 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { /** * Root HIDL interface object used to control the Wifi HAL. */ -class Wifi : public V1_3::IWifi { +class Wifi : public V1_4::IWifi { public: Wifi(const std::shared_ptr<wifi_system::InterfaceTool> iface_tool, const std::shared_ptr<legacy_hal::WifiLegacyHal> legacy_hal, @@ -92,7 +92,7 @@ class Wifi : public V1_3::IWifi { }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_ap_iface.cpp b/wifi/1.4/default/wifi_ap_iface.cpp index 9a8681afed..e677f197b8 100644 --- a/wifi/1.3/default/wifi_ap_iface.cpp +++ b/wifi/1.4/default/wifi_ap_iface.cpp @@ -24,33 +24,18 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using hidl_return_util::validateAndCall; WifiApIface::WifiApIface( const std::string& ifname, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal, - const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util, - const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags) + const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util) : ifname_(ifname), legacy_hal_(legacy_hal), iface_util_(iface_util), - feature_flags_(feature_flags), - is_valid_(true) { - if (feature_flags_.lock()->isApMacRandomizationDisabled()) { - LOG(INFO) << "AP MAC randomization disabled"; - return; - } - LOG(INFO) << "AP MAC randomization enabled"; - // Set random MAC address - std::array<uint8_t, 6> randomized_mac = - iface_util_.lock()->getOrCreateRandomMacAddress(); - bool status = iface_util_.lock()->setMacAddress(ifname_, randomized_mac); - if (!status) { - LOG(ERROR) << "Failed to set random mac address"; - } -} + is_valid_(true) {} void WifiApIface::invalidate() { legacy_hal_.reset(); @@ -85,6 +70,20 @@ Return<void> WifiApIface::getValidFrequenciesForBand( hidl_status_cb, band); } +Return<void> WifiApIface::setMacAddress(const hidl_array<uint8_t, 6>& mac, + setMacAddress_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID, + &WifiApIface::setMacAddressInternal, hidl_status_cb, + mac); +} + +Return<void> WifiApIface::getFactoryMacAddress( + getFactoryMacAddress_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID, + &WifiApIface::getFactoryMacAddressInternal, + hidl_status_cb); +} + std::pair<WifiStatus, std::string> WifiApIface::getNameInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), ifname_}; } @@ -111,8 +110,28 @@ WifiApIface::getValidFrequenciesForBandInternal(WifiBand band) { ifname_, hidl_struct_util::convertHidlWifiBandToLegacy(band)); return {createWifiStatusFromLegacyError(legacy_status), valid_frequencies}; } + +WifiStatus WifiApIface::setMacAddressInternal( + const std::array<uint8_t, 6>& mac) { + bool status = iface_util_.lock()->setMacAddress(ifname_, mac); + if (!status) { + return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN); + } + return createWifiStatus(WifiStatusCode::SUCCESS); +} + +std::pair<WifiStatus, std::array<uint8_t, 6>> +WifiApIface::getFactoryMacAddressInternal() { + std::array<uint8_t, 6> mac = + iface_util_.lock()->getFactoryMacAddress(ifname_); + if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && + mac[4] == 0 && mac[5] == 0) { + return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), mac}; + } + return {createWifiStatus(WifiStatusCode::SUCCESS), mac}; +} } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_ap_iface.h b/wifi/1.4/default/wifi_ap_iface.h index 98c5c9c376..4f3438c298 100644 --- a/wifi/1.3/default/wifi_ap_iface.h +++ b/wifi/1.4/default/wifi_ap_iface.h @@ -18,29 +18,26 @@ #define WIFI_AP_IFACE_H_ #include <android-base/macros.h> -#include <android/hardware/wifi/1.0/IWifiApIface.h> +#include <android/hardware/wifi/1.4/IWifiApIface.h> -#include "wifi_feature_flags.h" #include "wifi_iface_util.h" #include "wifi_legacy_hal.h" namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using namespace android::hardware::wifi::V1_0; /** * HIDL interface object used to control a AP Iface instance. */ -class WifiApIface : public V1_0::IWifiApIface { +class WifiApIface : public V1_4::IWifiApIface { public: - WifiApIface( - const std::string& ifname, - const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal, - const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util, - const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags); + WifiApIface(const std::string& ifname, + const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal, + const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util); // Refer to |WifiChip::invalidate()|. void invalidate(); bool isValid(); @@ -53,6 +50,10 @@ class WifiApIface : public V1_0::IWifiApIface { setCountryCode_cb hidl_status_cb) override; Return<void> getValidFrequenciesForBand( WifiBand band, getValidFrequenciesForBand_cb hidl_status_cb) override; + Return<void> setMacAddress(const hidl_array<uint8_t, 6>& mac, + setMacAddress_cb hidl_status_cb) override; + Return<void> getFactoryMacAddress( + getFactoryMacAddress_cb hidl_status_cb) override; private: // Corresponding worker functions for the HIDL methods. @@ -61,18 +62,20 @@ class WifiApIface : public V1_0::IWifiApIface { WifiStatus setCountryCodeInternal(const std::array<int8_t, 2>& code); std::pair<WifiStatus, std::vector<WifiChannelInMhz>> getValidFrequenciesForBandInternal(WifiBand band); + WifiStatus setMacAddressInternal(const std::array<uint8_t, 6>& mac); + std::pair<WifiStatus, std::array<uint8_t, 6>> + getFactoryMacAddressInternal(); std::string ifname_; std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_; std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_; - std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags_; bool is_valid_; DISALLOW_COPY_AND_ASSIGN(WifiApIface); }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_chip.cpp b/wifi/1.4/default/wifi_chip.cpp index e9991dceec..7685ac61d9 100644 --- a/wifi/1.3/default/wifi_chip.cpp +++ b/wifi/1.4/default/wifi_chip.cpp @@ -307,7 +307,7 @@ std::vector<char> makeCharVec(const std::string& str) { namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using hidl_return_util::validateAndCall; using hidl_return_util::validateAndCallWithLock; @@ -321,7 +321,6 @@ WifiChip::WifiChip( legacy_hal_(legacy_hal), mode_controller_(mode_controller), iface_util_(iface_util), - feature_flags_(feature_flags), is_valid_(true), current_mode_id_(feature_flags::chip_mode_ids::kInvalid), modes_(feature_flags.lock()->getChipModes()), @@ -621,6 +620,14 @@ Return<void> WifiChip::debug(const hidl_handle& handle, return Void(); } +Return<void> WifiChip::createRttController_1_4( + const sp<IWifiIface>& bound_iface, + createRttController_1_4_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID, + &WifiChip::createRttControllerInternal_1_4, + hidl_status_cb, bound_iface); +} + void WifiChip::invalidateAndRemoveAllIfaces() { invalidateAndClearAll(ap_ifaces_); invalidateAndClearAll(nan_ifaces_); @@ -670,30 +677,6 @@ std::pair<WifiStatus, uint32_t> WifiChip::getCapabilitiesInternal() { return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), 0}; } -std::pair<WifiStatus, uint32_t> WifiChip::getCapabilitiesInternal_1_3() { - legacy_hal::wifi_error legacy_status; - uint32_t legacy_feature_set; - uint32_t legacy_logger_feature_set; - const auto ifname = getFirstActiveWlanIfaceName(); - std::tie(legacy_status, legacy_feature_set) = - legacy_hal_.lock()->getSupportedFeatureSet(ifname); - if (legacy_status != legacy_hal::WIFI_SUCCESS) { - return {createWifiStatusFromLegacyError(legacy_status), 0}; - } - std::tie(legacy_status, legacy_logger_feature_set) = - legacy_hal_.lock()->getLoggerSupportedFeatureSet(ifname); - if (legacy_status != legacy_hal::WIFI_SUCCESS) { - // some devices don't support querying logger feature set - legacy_logger_feature_set = 0; - } - uint32_t hidl_caps; - if (!hidl_struct_util::convertLegacyFeaturesToHidlChipCapabilities( - legacy_feature_set, legacy_logger_feature_set, &hidl_caps)) { - return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), 0}; - } - return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_caps}; -} - std::pair<WifiStatus, std::vector<IWifiChip::ChipMode>> WifiChip::getAvailableModesInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), modes_}; @@ -806,8 +789,7 @@ std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::createApIfaceInternal() { return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}}; } std::string ifname = allocateApIfaceName(); - sp<WifiApIface> iface = - new WifiApIface(ifname, legacy_hal_, iface_util_, feature_flags_); + sp<WifiApIface> iface = new WifiApIface(ifname, legacy_hal_, iface_util_); ap_ifaces_.push_back(iface); for (const auto& callback : event_cb_handler_.getCallbacks()) { if (!callback->onIfaceAdded(IfaceType::AP, ifname).isOk()) { @@ -998,18 +980,10 @@ WifiStatus WifiChip::removeStaIfaceInternal(const std::string& ifname) { return createWifiStatus(WifiStatusCode::SUCCESS); } -std::pair<WifiStatus, sp<IWifiRttController>> -WifiChip::createRttControllerInternal(const sp<IWifiIface>& bound_iface) { - if (sta_ifaces_.size() == 0 && - !canCurrentModeSupportIfaceOfType(IfaceType::STA)) { - LOG(ERROR) << "createRttControllerInternal: Chip cannot support STAs " - "(and RTT by extension)"; - return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}}; - } - sp<WifiRttController> rtt = new WifiRttController( - getFirstActiveWlanIfaceName(), bound_iface, legacy_hal_); - rtt_controllers_.emplace_back(rtt); - return {createWifiStatus(WifiStatusCode::SUCCESS), rtt}; +std::pair<WifiStatus, sp<V1_0::IWifiRttController>> +WifiChip::createRttControllerInternal(const sp<IWifiIface>& /*bound_iface*/) { + LOG(ERROR) << "createRttController is not supported on this HAL"; + return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}}; } std::pair<WifiStatus, std::vector<WifiDebugRingBufferStatus>> @@ -1160,6 +1134,45 @@ WifiStatus WifiChip::selectTxPowerScenarioInternal_1_2( return createWifiStatusFromLegacyError(legacy_status); } +std::pair<WifiStatus, uint32_t> WifiChip::getCapabilitiesInternal_1_3() { + legacy_hal::wifi_error legacy_status; + uint32_t legacy_feature_set; + uint32_t legacy_logger_feature_set; + const auto ifname = getFirstActiveWlanIfaceName(); + std::tie(legacy_status, legacy_feature_set) = + legacy_hal_.lock()->getSupportedFeatureSet(ifname); + if (legacy_status != legacy_hal::WIFI_SUCCESS) { + return {createWifiStatusFromLegacyError(legacy_status), 0}; + } + std::tie(legacy_status, legacy_logger_feature_set) = + legacy_hal_.lock()->getLoggerSupportedFeatureSet(ifname); + if (legacy_status != legacy_hal::WIFI_SUCCESS) { + // some devices don't support querying logger feature set + legacy_logger_feature_set = 0; + } + uint32_t hidl_caps; + if (!hidl_struct_util::convertLegacyFeaturesToHidlChipCapabilities( + legacy_feature_set, legacy_logger_feature_set, &hidl_caps)) { + return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), 0}; + } + return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_caps}; +} + +std::pair<WifiStatus, sp<IWifiRttController>> +WifiChip::createRttControllerInternal_1_4(const sp<IWifiIface>& bound_iface) { + if (sta_ifaces_.size() == 0 && + !canCurrentModeSupportIfaceOfType(IfaceType::STA)) { + LOG(ERROR) + << "createRttControllerInternal_1_4: Chip cannot support STAs " + "(and RTT by extension)"; + return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}}; + } + sp<WifiRttController> rtt = new WifiRttController( + getFirstActiveWlanIfaceName(), bound_iface, legacy_hal_); + rtt_controllers_.emplace_back(rtt); + return {createWifiStatus(WifiStatusCode::SUCCESS), rtt}; +} + WifiStatus WifiChip::handleChipConfiguration( /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id) { @@ -1539,7 +1552,7 @@ bool WifiChip::writeRingbufferFilesInternal() { } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_chip.h b/wifi/1.4/default/wifi_chip.h index 153ca6a62d..3bf18475ae 100644 --- a/wifi/1.3/default/wifi_chip.h +++ b/wifi/1.4/default/wifi_chip.h @@ -21,7 +21,8 @@ #include <map> #include <android-base/macros.h> -#include <android/hardware/wifi/1.3/IWifiChip.h> +#include <android/hardware/wifi/1.4/IWifiChip.h> +#include <android/hardware/wifi/1.4/IWifiRttController.h> #include "hidl_callback_util.h" #include "ringbuffer.h" @@ -37,7 +38,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using namespace android::hardware::wifi::V1_0; @@ -46,7 +47,7 @@ using namespace android::hardware::wifi::V1_0; * Since there is only a single chip instance used today, there is no * identifying handle information stored here. */ -class WifiChip : public V1_3::IWifiChip { +class WifiChip : public V1_4::IWifiChip { public: WifiChip( ChipId chip_id, @@ -152,6 +153,9 @@ class WifiChip : public V1_3::IWifiChip { getCapabilities_cb hidl_status_cb) override; Return<void> debug(const hidl_handle& handle, const hidl_vec<hidl_string>& options) override; + Return<void> createRttController_1_4( + const sp<IWifiIface>& bound_iface, + createRttController_1_4_cb hidl_status_cb) override; private: void invalidateAndRemoveAllIfaces(); @@ -195,8 +199,8 @@ class WifiChip : public V1_3::IWifiChip { std::pair<WifiStatus, sp<IWifiStaIface>> getStaIfaceInternal( const std::string& ifname); WifiStatus removeStaIfaceInternal(const std::string& ifname); - std::pair<WifiStatus, sp<IWifiRttController>> createRttControllerInternal( - const sp<IWifiIface>& bound_iface); + std::pair<WifiStatus, sp<V1_0::IWifiRttController>> + createRttControllerInternal(const sp<IWifiIface>& bound_iface); std::pair<WifiStatus, std::vector<WifiDebugRingBufferStatus>> getDebugRingBuffersStatusInternal(); WifiStatus startLoggingToDebugRingBufferInternal( @@ -217,6 +221,8 @@ class WifiChip : public V1_3::IWifiChip { const sp<V1_2::IWifiChipEventCallback>& event_callback); WifiStatus selectTxPowerScenarioInternal_1_2(TxPowerScenario scenario); std::pair<WifiStatus, uint32_t> getCapabilitiesInternal_1_3(); + std::pair<WifiStatus, sp<IWifiRttController>> + createRttControllerInternal_1_4(const sp<IWifiIface>& bound_iface); WifiStatus handleChipConfiguration( std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id); WifiStatus registerDebugRingBufferCallback(); @@ -251,7 +257,6 @@ class WifiChip : public V1_3::IWifiChip { std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_; std::weak_ptr<mode_controller::WifiModeController> mode_controller_; std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_; - std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags_; std::vector<sp<WifiApIface>> ap_ifaces_; std::vector<sp<WifiNanIface>> nan_ifaces_; std::vector<sp<WifiP2pIface>> p2p_ifaces_; @@ -273,7 +278,7 @@ class WifiChip : public V1_3::IWifiChip { }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_feature_flags.cpp b/wifi/1.4/default/wifi_feature_flags.cpp index 7212cfac3c..195b460dfe 100644 --- a/wifi/1.3/default/wifi_feature_flags.cpp +++ b/wifi/1.4/default/wifi_feature_flags.cpp @@ -19,7 +19,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace feature_flags { @@ -145,10 +145,12 @@ static const std::vector<IWifiChip::ChipMode> kChipModes{ #undef NAN #ifdef WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION -static const bool wifiHidlFeatureDisableApMacRandomization = true; -#else -static const bool wifiHidlFeatureDisableApMacRandomization = false; -#endif // WIFI_HIDL_FEATURE_DISABLE_AP +#pragma message \ + "WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION is deprecated; override " \ + "'config_wifi_ap_randomization_supported' in " \ + "frameworks/base/core/res/res/values/config.xml in the device overlay " \ + "instead" +#endif // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION WifiFeatureFlags::WifiFeatureFlags() {} @@ -156,13 +158,9 @@ std::vector<IWifiChip::ChipMode> WifiFeatureFlags::getChipModes() { return kChipModes; } -bool WifiFeatureFlags::isApMacRandomizationDisabled() { - return wifiHidlFeatureDisableApMacRandomization; -} - } // namespace feature_flags } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_feature_flags.h b/wifi/1.4/default/wifi_feature_flags.h index 3ae6920907..292dedfe19 100644 --- a/wifi/1.3/default/wifi_feature_flags.h +++ b/wifi/1.4/default/wifi_feature_flags.h @@ -22,7 +22,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace feature_flags { @@ -43,12 +43,11 @@ class WifiFeatureFlags { virtual ~WifiFeatureFlags() = default; virtual std::vector<V1_0::IWifiChip::ChipMode> getChipModes(); - virtual bool isApMacRandomizationDisabled(); }; } // namespace feature_flags } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_iface_util.cpp b/wifi/1.4/default/wifi_iface_util.cpp index 34bc02d3f9..2883b4627b 100644 --- a/wifi/1.3/default/wifi_iface_util.cpp +++ b/wifi/1.4/default/wifi_iface_util.cpp @@ -35,7 +35,7 @@ constexpr uint8_t kMacAddressLocallyAssignedMask = 0x02; namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace iface_util { @@ -112,7 +112,7 @@ std::array<uint8_t, 6> WifiIfaceUtil::createRandomMacAddress() { } } // namespace iface_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_iface_util.h b/wifi/1.4/default/wifi_iface_util.h index 98073e0763..35edff68bd 100644 --- a/wifi/1.3/default/wifi_iface_util.h +++ b/wifi/1.4/default/wifi_iface_util.h @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace iface_util { @@ -67,7 +67,7 @@ class WifiIfaceUtil { } // namespace iface_util } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp index 485bd16634..ae3c447a01 100644 --- a/wifi/1.3/default/wifi_legacy_hal.cpp +++ b/wifi/1.4/default/wifi_legacy_hal.cpp @@ -50,7 +50,7 @@ std::vector<char> makeCharVec(const std::string& str) { namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace legacy_hal { // Legacy HAL functions accept "C" style function pointers, so use global @@ -479,7 +479,7 @@ WifiLegacyHal::requestFirmwareMemoryDump(const std::string& iface_name) { std::pair<wifi_error, uint32_t> WifiLegacyHal::getSupportedFeatureSet( const std::string& iface_name) { feature_set set; - static_assert(sizeof(set) == sizeof(uint32_t), + static_assert(sizeof(set) == sizeof(uint64_t), "Some feature_flags can not be represented in output"); wifi_error status = global_func_table_.wifi_get_supported_feature_set( getIfaceHandle(iface_name), &set); @@ -1449,7 +1449,7 @@ void WifiLegacyHal::invalidate() { } // namespace legacy_hal } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h index 9cfa172402..7f16c30628 100644 --- a/wifi/1.3/default/wifi_legacy_hal.h +++ b/wifi/1.4/default/wifi_legacy_hal.h @@ -35,7 +35,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { // This is in a separate namespace to prevent typename conflicts between // the legacy HAL types and the HIDL interface types. @@ -396,7 +396,7 @@ class WifiLegacyHal { } // namespace legacy_hal } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_legacy_hal_stubs.cpp b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp index dedd2d4819..27afa1ff06 100644 --- a/wifi/1.3/default/wifi_legacy_hal_stubs.cpp +++ b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp @@ -20,7 +20,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace legacy_hal { template <typename> @@ -142,7 +142,7 @@ bool initHalFuncTableWithStubs(wifi_hal_fn* hal_fn) { } } // namespace legacy_hal } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_legacy_hal_stubs.h b/wifi/1.4/default/wifi_legacy_hal_stubs.h index 64854e00f6..577a545b1d 100644 --- a/wifi/1.3/default/wifi_legacy_hal_stubs.h +++ b/wifi/1.4/default/wifi_legacy_hal_stubs.h @@ -20,7 +20,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace legacy_hal { #include <hardware_legacy/wifi_hal.h> @@ -28,7 +28,7 @@ namespace legacy_hal { bool initHalFuncTableWithStubs(wifi_hal_fn* hal_fn); } // namespace legacy_hal } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_mode_controller.cpp b/wifi/1.4/default/wifi_mode_controller.cpp index c392486f84..252121a5b4 100644 --- a/wifi/1.3/default/wifi_mode_controller.cpp +++ b/wifi/1.4/default/wifi_mode_controller.cpp @@ -48,7 +48,7 @@ int convertIfaceTypeToFirmwareMode(IfaceType type) { namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace mode_controller { @@ -85,7 +85,7 @@ bool WifiModeController::deinitialize() { } } // namespace mode_controller } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_mode_controller.h b/wifi/1.4/default/wifi_mode_controller.h index ace5a52839..45fa999b2a 100644 --- a/wifi/1.3/default/wifi_mode_controller.h +++ b/wifi/1.4/default/wifi_mode_controller.h @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { namespace mode_controller { using namespace android::hardware::wifi::V1_0; @@ -55,7 +55,7 @@ class WifiModeController { } // namespace mode_controller } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_nan_iface.cpp b/wifi/1.4/default/wifi_nan_iface.cpp index ff9f4224df..981acb282c 100644 --- a/wifi/1.3/default/wifi_nan_iface.cpp +++ b/wifi/1.4/default/wifi_nan_iface.cpp @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using hidl_return_util::validateAndCall; @@ -881,7 +881,7 @@ WifiStatus WifiNanIface::configRequest_1_2Internal( } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_nan_iface.h b/wifi/1.4/default/wifi_nan_iface.h index 737be93217..e3a5c34cd3 100644 --- a/wifi/1.3/default/wifi_nan_iface.h +++ b/wifi/1.4/default/wifi_nan_iface.h @@ -28,7 +28,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using namespace android::hardware::wifi::V1_0; @@ -160,7 +160,7 @@ class WifiNanIface : public V1_2::IWifiNanIface { }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_p2p_iface.cpp b/wifi/1.4/default/wifi_p2p_iface.cpp index b5d5886f68..9e7341f248 100644 --- a/wifi/1.3/default/wifi_p2p_iface.cpp +++ b/wifi/1.4/default/wifi_p2p_iface.cpp @@ -23,7 +23,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using hidl_return_util::validateAndCall; @@ -60,7 +60,7 @@ std::pair<WifiStatus, IfaceType> WifiP2pIface::getTypeInternal() { } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_p2p_iface.h b/wifi/1.4/default/wifi_p2p_iface.h index 8a7207a412..a6fc59d04b 100644 --- a/wifi/1.3/default/wifi_p2p_iface.h +++ b/wifi/1.4/default/wifi_p2p_iface.h @@ -25,7 +25,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using namespace android::hardware::wifi::V1_0; @@ -58,7 +58,7 @@ class WifiP2pIface : public V1_0::IWifiP2pIface { }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_rtt_controller.cpp b/wifi/1.4/default/wifi_rtt_controller.cpp index 3dcbee687b..594a11660f 100644 --- a/wifi/1.3/default/wifi_rtt_controller.cpp +++ b/wifi/1.4/default/wifi_rtt_controller.cpp @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using hidl_return_util::validateAndCall; @@ -58,7 +58,7 @@ Return<void> WifiRttController::getBoundIface(getBoundIface_cb hidl_status_cb) { } Return<void> WifiRttController::registerEventCallback( - const sp<IWifiRttControllerEventCallback>& callback, + const sp<V1_0::IWifiRttControllerEventCallback>& callback, registerEventCallback_cb hidl_status_cb) { return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, @@ -67,7 +67,7 @@ Return<void> WifiRttController::registerEventCallback( } Return<void> WifiRttController::rangeRequest( - uint32_t cmd_id, const hidl_vec<RttConfig>& rtt_configs, + uint32_t cmd_id, const hidl_vec<V1_0::RttConfig>& rtt_configs, rangeRequest_cb hidl_status_cb) { return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, @@ -115,7 +115,7 @@ Return<void> WifiRttController::getResponderInfo( Return<void> WifiRttController::enableResponder( uint32_t cmd_id, const WifiChannelInfo& channel_hint, - uint32_t max_duration_seconds, const RttResponder& info, + uint32_t max_duration_seconds, const V1_0::RttResponder& info, enableResponder_cb hidl_status_cb) { return validateAndCall( this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, @@ -130,19 +130,135 @@ Return<void> WifiRttController::disableResponder( &WifiRttController::disableResponderInternal, hidl_status_cb, cmd_id); } +Return<void> WifiRttController::registerEventCallback_1_4( + const sp<IWifiRttControllerEventCallback>& callback, + registerEventCallback_1_4_cb hidl_status_cb) { + return validateAndCall( + this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::registerEventCallbackInternal_1_4, hidl_status_cb, + callback); +} + +Return<void> WifiRttController::rangeRequest_1_4( + uint32_t cmd_id, const hidl_vec<RttConfig>& rtt_configs, + rangeRequest_1_4_cb hidl_status_cb) { + return validateAndCall(this, + WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::rangeRequestInternal_1_4, + hidl_status_cb, cmd_id, rtt_configs); +} + +Return<void> WifiRttController::getCapabilities_1_4( + getCapabilities_1_4_cb hidl_status_cb) { + return validateAndCall( + this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::getCapabilitiesInternal_1_4, hidl_status_cb); +} + +Return<void> WifiRttController::getResponderInfo_1_4( + getResponderInfo_1_4_cb hidl_status_cb) { + return validateAndCall( + this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::getResponderInfoInternal_1_4, hidl_status_cb); +} + +Return<void> WifiRttController::enableResponder_1_4( + uint32_t cmd_id, const WifiChannelInfo& channel_hint, + uint32_t max_duration_seconds, const RttResponder& info, + enableResponder_1_4_cb hidl_status_cb) { + return validateAndCall( + this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID, + &WifiRttController::enableResponderInternal_1_4, hidl_status_cb, cmd_id, + channel_hint, max_duration_seconds, info); +} + std::pair<WifiStatus, sp<IWifiIface>> WifiRttController::getBoundIfaceInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), bound_iface_}; } WifiStatus WifiRttController::registerEventCallbackInternal( + const sp<V1_0::IWifiRttControllerEventCallback>& /* callback */) { + // Deprecated support for this api + return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED); +} + +WifiStatus WifiRttController::rangeRequestInternal( + uint32_t /* cmd_id */, + const std::vector<V1_0::RttConfig>& /* rtt_configs */) { + // Deprecated support for this api + return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED); +} + +WifiStatus WifiRttController::rangeCancelInternal( + uint32_t cmd_id, const std::vector<hidl_array<uint8_t, 6>>& addrs) { + std::vector<std::array<uint8_t, 6>> legacy_addrs; + for (const auto& addr : addrs) { + legacy_addrs.push_back(addr); + } + legacy_hal::wifi_error legacy_status = + legacy_hal_.lock()->cancelRttRangeRequest(ifname_, cmd_id, + legacy_addrs); + return createWifiStatusFromLegacyError(legacy_status); +} + +std::pair<WifiStatus, V1_0::RttCapabilities> +WifiRttController::getCapabilitiesInternal() { + // Deprecated support for this api + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; +} + +WifiStatus WifiRttController::setLciInternal(uint32_t cmd_id, + const RttLciInformation& lci) { + legacy_hal::wifi_lci_information legacy_lci; + if (!hidl_struct_util::convertHidlRttLciInformationToLegacy(lci, + &legacy_lci)) { + return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS); + } + legacy_hal::wifi_error legacy_status = + legacy_hal_.lock()->setRttLci(ifname_, cmd_id, legacy_lci); + return createWifiStatusFromLegacyError(legacy_status); +} + +WifiStatus WifiRttController::setLcrInternal(uint32_t cmd_id, + const RttLcrInformation& lcr) { + legacy_hal::wifi_lcr_information legacy_lcr; + if (!hidl_struct_util::convertHidlRttLcrInformationToLegacy(lcr, + &legacy_lcr)) { + return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS); + } + legacy_hal::wifi_error legacy_status = + legacy_hal_.lock()->setRttLcr(ifname_, cmd_id, legacy_lcr); + return createWifiStatusFromLegacyError(legacy_status); +} + +std::pair<WifiStatus, V1_0::RttResponder> +WifiRttController::getResponderInfoInternal() { + // Deprecated support for this api + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}}; +} + +WifiStatus WifiRttController::enableResponderInternal( + uint32_t /* cmd_id */, const WifiChannelInfo& /* channel_hint */, + uint32_t /* max_duration_seconds */, const V1_0::RttResponder& /* info */) { + // Deprecated support for this api + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED)}; +} + +WifiStatus WifiRttController::disableResponderInternal(uint32_t cmd_id) { + legacy_hal::wifi_error legacy_status = + legacy_hal_.lock()->disableRttResponder(ifname_, cmd_id); + return createWifiStatusFromLegacyError(legacy_status); +} + +WifiStatus WifiRttController::registerEventCallbackInternal_1_4( const sp<IWifiRttControllerEventCallback>& callback) { // TODO(b/31632518): remove the callback when the client is destroyed event_callbacks_.emplace_back(callback); return createWifiStatus(WifiStatusCode::SUCCESS); } -WifiStatus WifiRttController::rangeRequestInternal( +WifiStatus WifiRttController::rangeRequestInternal_1_4( uint32_t cmd_id, const std::vector<RttConfig>& rtt_configs) { std::vector<legacy_hal::wifi_rtt_config> legacy_configs; if (!hidl_struct_util::convertHidlVectorOfRttConfigToLegacy( @@ -166,7 +282,7 @@ WifiStatus WifiRttController::rangeRequestInternal( return; } for (const auto& callback : shared_ptr_this->getEventCallbacks()) { - callback->onResults(id, hidl_results); + callback->onResults_1_4(id, hidl_results); } }; legacy_hal::wifi_error legacy_status = @@ -175,20 +291,8 @@ WifiStatus WifiRttController::rangeRequestInternal( return createWifiStatusFromLegacyError(legacy_status); } -WifiStatus WifiRttController::rangeCancelInternal( - uint32_t cmd_id, const std::vector<hidl_array<uint8_t, 6>>& addrs) { - std::vector<std::array<uint8_t, 6>> legacy_addrs; - for (const auto& addr : addrs) { - legacy_addrs.push_back(addr); - } - legacy_hal::wifi_error legacy_status = - legacy_hal_.lock()->cancelRttRangeRequest(ifname_, cmd_id, - legacy_addrs); - return createWifiStatusFromLegacyError(legacy_status); -} - std::pair<WifiStatus, RttCapabilities> -WifiRttController::getCapabilitiesInternal() { +WifiRttController::getCapabilitiesInternal_1_4() { legacy_hal::wifi_error legacy_status; legacy_hal::wifi_rtt_capabilities legacy_caps; std::tie(legacy_status, legacy_caps) = @@ -204,32 +308,8 @@ WifiRttController::getCapabilitiesInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_caps}; } -WifiStatus WifiRttController::setLciInternal(uint32_t cmd_id, - const RttLciInformation& lci) { - legacy_hal::wifi_lci_information legacy_lci; - if (!hidl_struct_util::convertHidlRttLciInformationToLegacy(lci, - &legacy_lci)) { - return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS); - } - legacy_hal::wifi_error legacy_status = - legacy_hal_.lock()->setRttLci(ifname_, cmd_id, legacy_lci); - return createWifiStatusFromLegacyError(legacy_status); -} - -WifiStatus WifiRttController::setLcrInternal(uint32_t cmd_id, - const RttLcrInformation& lcr) { - legacy_hal::wifi_lcr_information legacy_lcr; - if (!hidl_struct_util::convertHidlRttLcrInformationToLegacy(lcr, - &legacy_lcr)) { - return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS); - } - legacy_hal::wifi_error legacy_status = - legacy_hal_.lock()->setRttLcr(ifname_, cmd_id, legacy_lcr); - return createWifiStatusFromLegacyError(legacy_status); -} - std::pair<WifiStatus, RttResponder> -WifiRttController::getResponderInfoInternal() { +WifiRttController::getResponderInfoInternal_1_4() { legacy_hal::wifi_error legacy_status; legacy_hal::wifi_rtt_responder legacy_responder; std::tie(legacy_status, legacy_responder) = @@ -245,7 +325,7 @@ WifiRttController::getResponderInfoInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_responder}; } -WifiStatus WifiRttController::enableResponderInternal( +WifiStatus WifiRttController::enableResponderInternal_1_4( uint32_t cmd_id, const WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, const RttResponder& info) { legacy_hal::wifi_channel_info legacy_channel_info; @@ -264,14 +344,8 @@ WifiStatus WifiRttController::enableResponderInternal( legacy_responder); return createWifiStatusFromLegacyError(legacy_status); } - -WifiStatus WifiRttController::disableResponderInternal(uint32_t cmd_id) { - legacy_hal::wifi_error legacy_status = - legacy_hal_.lock()->disableRttResponder(ifname_, cmd_id); - return createWifiStatusFromLegacyError(legacy_status); -} } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_rtt_controller.h b/wifi/1.4/default/wifi_rtt_controller.h index eedd22aeef..1f125555d0 100644 --- a/wifi/1.3/default/wifi_rtt_controller.h +++ b/wifi/1.4/default/wifi_rtt_controller.h @@ -19,21 +19,21 @@ #include <android-base/macros.h> #include <android/hardware/wifi/1.0/IWifiIface.h> -#include <android/hardware/wifi/1.0/IWifiRttController.h> -#include <android/hardware/wifi/1.0/IWifiRttControllerEventCallback.h> +#include <android/hardware/wifi/1.4/IWifiRttController.h> +#include <android/hardware/wifi/1.4/IWifiRttControllerEventCallback.h> #include "wifi_legacy_hal.h" namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { /** * HIDL interface object used to control all RTT operations. */ -class WifiRttController : public V1_0::IWifiRttController { +class WifiRttController : public V1_4::IWifiRttController { public: WifiRttController( const std::string& iface_name, const sp<IWifiIface>& bound_iface, @@ -47,10 +47,10 @@ class WifiRttController : public V1_0::IWifiRttController { // HIDL methods exposed. Return<void> getBoundIface(getBoundIface_cb hidl_status_cb) override; Return<void> registerEventCallback( - const sp<IWifiRttControllerEventCallback>& callback, + const sp<V1_0::IWifiRttControllerEventCallback>& callback, registerEventCallback_cb hidl_status_cb) override; Return<void> rangeRequest(uint32_t cmd_id, - const hidl_vec<RttConfig>& rtt_configs, + const hidl_vec<V1_0::RttConfig>& rtt_configs, rangeRequest_cb hidl_status_cb) override; Return<void> rangeCancel(uint32_t cmd_id, const hidl_vec<hidl_array<uint8_t, 6>>& addrs, @@ -64,29 +64,53 @@ class WifiRttController : public V1_0::IWifiRttController { Return<void> enableResponder(uint32_t cmd_id, const WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, - const RttResponder& info, + const V1_0::RttResponder& info, enableResponder_cb hidl_status_cb) override; Return<void> disableResponder(uint32_t cmd_id, disableResponder_cb hidl_status_cb) override; + Return<void> registerEventCallback_1_4( + const sp<IWifiRttControllerEventCallback>& callback, + registerEventCallback_1_4_cb hidl_status_cb) override; + Return<void> rangeRequest_1_4(uint32_t cmd_id, + const hidl_vec<RttConfig>& rtt_configs, + rangeRequest_1_4_cb hidl_status_cb) override; + Return<void> getCapabilities_1_4( + getCapabilities_1_4_cb hidl_status_cb) override; + Return<void> getResponderInfo_1_4( + getResponderInfo_1_4_cb hidl_status_cb) override; + Return<void> enableResponder_1_4( + uint32_t cmd_id, const WifiChannelInfo& channel_hint, + uint32_t max_duration_seconds, const RttResponder& info, + enableResponder_1_4_cb hidl_status_cb) override; private: // Corresponding worker functions for the HIDL methods. std::pair<WifiStatus, sp<IWifiIface>> getBoundIfaceInternal(); WifiStatus registerEventCallbackInternal( - const sp<IWifiRttControllerEventCallback>& callback); - WifiStatus rangeRequestInternal(uint32_t cmd_id, - const std::vector<RttConfig>& rtt_configs); + const sp<V1_0::IWifiRttControllerEventCallback>& callback); + WifiStatus rangeRequestInternal( + uint32_t cmd_id, const std::vector<V1_0::RttConfig>& rtt_configs); WifiStatus rangeCancelInternal( uint32_t cmd_id, const std::vector<hidl_array<uint8_t, 6>>& addrs); - std::pair<WifiStatus, RttCapabilities> getCapabilitiesInternal(); + std::pair<WifiStatus, V1_0::RttCapabilities> getCapabilitiesInternal(); WifiStatus setLciInternal(uint32_t cmd_id, const RttLciInformation& lci); WifiStatus setLcrInternal(uint32_t cmd_id, const RttLcrInformation& lcr); - std::pair<WifiStatus, RttResponder> getResponderInfoInternal(); + std::pair<WifiStatus, V1_0::RttResponder> getResponderInfoInternal(); WifiStatus enableResponderInternal(uint32_t cmd_id, const WifiChannelInfo& channel_hint, uint32_t max_duration_seconds, - const RttResponder& info); + const V1_0::RttResponder& info); WifiStatus disableResponderInternal(uint32_t cmd_id); + WifiStatus registerEventCallbackInternal_1_4( + const sp<IWifiRttControllerEventCallback>& callback); + WifiStatus rangeRequestInternal_1_4( + uint32_t cmd_id, const std::vector<RttConfig>& rtt_configs); + std::pair<WifiStatus, RttCapabilities> getCapabilitiesInternal_1_4(); + std::pair<WifiStatus, RttResponder> getResponderInfoInternal_1_4(); + WifiStatus enableResponderInternal_1_4(uint32_t cmd_id, + const WifiChannelInfo& channel_hint, + uint32_t max_duration_seconds, + const RttResponder& info); std::string ifname_; sp<IWifiIface> bound_iface_; @@ -98,7 +122,7 @@ class WifiRttController : public V1_0::IWifiRttController { }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_sta_iface.cpp b/wifi/1.4/default/wifi_sta_iface.cpp index a6539e5d90..8e1ada1032 100644 --- a/wifi/1.3/default/wifi_sta_iface.cpp +++ b/wifi/1.4/default/wifi_sta_iface.cpp @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using hidl_return_util::validateAndCall; @@ -266,6 +266,13 @@ Return<void> WifiStaIface::getFactoryMacAddress( hidl_status_cb); } +Return<void> WifiStaIface::getCapabilities_1_4( + getCapabilities_cb hidl_status_cb) { + return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID, + &WifiStaIface::getCapabilitiesInternal_1_4, + hidl_status_cb); +} + std::pair<WifiStatus, std::string> WifiStaIface::getNameInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), ifname_}; } @@ -283,26 +290,7 @@ WifiStatus WifiStaIface::registerEventCallbackInternal( } std::pair<WifiStatus, uint32_t> WifiStaIface::getCapabilitiesInternal() { - legacy_hal::wifi_error legacy_status; - uint32_t legacy_feature_set; - std::tie(legacy_status, legacy_feature_set) = - legacy_hal_.lock()->getSupportedFeatureSet(ifname_); - if (legacy_status != legacy_hal::WIFI_SUCCESS) { - return {createWifiStatusFromLegacyError(legacy_status), 0}; - } - uint32_t legacy_logger_feature_set; - std::tie(legacy_status, legacy_logger_feature_set) = - legacy_hal_.lock()->getLoggerSupportedFeatureSet(ifname_); - if (legacy_status != legacy_hal::WIFI_SUCCESS) { - // some devices don't support querying logger feature set - legacy_logger_feature_set = 0; - } - uint32_t hidl_caps; - if (!hidl_struct_util::convertLegacyFeaturesToHidlStaCapabilities( - legacy_feature_set, legacy_logger_feature_set, &hidl_caps)) { - return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), 0}; - } - return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_caps}; + return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), 0}; } std::pair<WifiStatus, StaApfPacketFilterCapabilities> @@ -640,8 +628,31 @@ WifiStaIface::getFactoryMacAddressInternal() { return {createWifiStatus(WifiStatusCode::SUCCESS), mac}; } +std::pair<WifiStatus, uint32_t> WifiStaIface::getCapabilitiesInternal_1_4() { + legacy_hal::wifi_error legacy_status; + uint64_t legacy_feature_set; + std::tie(legacy_status, legacy_feature_set) = + legacy_hal_.lock()->getSupportedFeatureSet(ifname_); + if (legacy_status != legacy_hal::WIFI_SUCCESS) { + return {createWifiStatusFromLegacyError(legacy_status), 0}; + } + uint32_t legacy_logger_feature_set; + std::tie(legacy_status, legacy_logger_feature_set) = + legacy_hal_.lock()->getLoggerSupportedFeatureSet(ifname_); + if (legacy_status != legacy_hal::WIFI_SUCCESS) { + // some devices don't support querying logger feature set + legacy_logger_feature_set = 0; + } + uint32_t hidl_caps; + if (!hidl_struct_util::convertLegacyFeaturesToHidlStaCapabilities( + legacy_feature_set, legacy_logger_feature_set, &hidl_caps)) { + return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), 0}; + } + return {createWifiStatus(WifiStatusCode::SUCCESS), hidl_caps}; +} + } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_sta_iface.h b/wifi/1.4/default/wifi_sta_iface.h index 92249394bf..ccf234f98e 100644 --- a/wifi/1.3/default/wifi_sta_iface.h +++ b/wifi/1.4/default/wifi_sta_iface.h @@ -19,7 +19,7 @@ #include <android-base/macros.h> #include <android/hardware/wifi/1.0/IWifiStaIfaceEventCallback.h> -#include <android/hardware/wifi/1.3/IWifiStaIface.h> +#include <android/hardware/wifi/1.4/IWifiStaIface.h> #include "hidl_callback_util.h" #include "wifi_iface_util.h" @@ -28,14 +28,14 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using namespace android::hardware::wifi::V1_0; /** * HIDL interface object used to control a STA Iface instance. */ -class WifiStaIface : public V1_3::IWifiStaIface { +class WifiStaIface : public V1_4::IWifiStaIface { public: WifiStaIface(const std::string& ifname, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal, @@ -111,6 +111,8 @@ class WifiStaIface : public V1_3::IWifiStaIface { setMacAddress_cb hidl_status_cb) override; Return<void> getFactoryMacAddress( getFactoryMacAddress_cb hidl_status_cb) override; + Return<void> getCapabilities_1_4( + getCapabilities_1_4_cb hidl_status_cb) override; private: // Corresponding worker functions for the HIDL methods. @@ -159,6 +161,7 @@ class WifiStaIface : public V1_3::IWifiStaIface { WifiStatus setMacAddressInternal(const std::array<uint8_t, 6>& mac); std::pair<WifiStatus, std::array<uint8_t, 6>> getFactoryMacAddressInternal(); + std::pair<WifiStatus, uint32_t> getCapabilitiesInternal_1_4(); std::string ifname_; std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_; @@ -171,7 +174,7 @@ class WifiStaIface : public V1_3::IWifiStaIface { }; } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_status_util.cpp b/wifi/1.4/default/wifi_status_util.cpp index 0a5bb13d4f..8ceb9265f1 100644 --- a/wifi/1.3/default/wifi_status_util.cpp +++ b/wifi/1.4/default/wifi_status_util.cpp @@ -19,7 +19,7 @@ namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { std::string legacyErrorToString(legacy_hal::wifi_error error) { @@ -46,6 +46,8 @@ std::string legacyErrorToString(legacy_hal::wifi_error error) { return "BUSY"; case legacy_hal::WIFI_ERROR_UNKNOWN: return "UNKNOWN"; + default: + return "UNKNOWN ERROR"; } } @@ -92,6 +94,10 @@ WifiStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error, case legacy_hal::WIFI_ERROR_UNKNOWN: return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, "unknown"); + + default: + return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, + "unknown error"); } } @@ -100,7 +106,7 @@ WifiStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error) { } } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.3/default/wifi_status_util.h b/wifi/1.4/default/wifi_status_util.h index bc8baa9fe7..3ff58f0c0a 100644 --- a/wifi/1.3/default/wifi_status_util.h +++ b/wifi/1.4/default/wifi_status_util.h @@ -17,14 +17,14 @@ #ifndef WIFI_STATUS_UTIL_H_ #define WIFI_STATUS_UTIL_H_ -#include <android/hardware/wifi/1.0/IWifi.h> +#include <android/hardware/wifi/1.4/IWifi.h> #include "wifi_legacy_hal.h" namespace android { namespace hardware { namespace wifi { -namespace V1_3 { +namespace V1_4 { namespace implementation { using namespace android::hardware::wifi::V1_0; @@ -37,7 +37,7 @@ WifiStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error, WifiStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error); } // namespace implementation -} // namespace V1_3 +} // namespace V1_4 } // namespace wifi } // namespace hardware } // namespace android diff --git a/wifi/1.4/types.hal b/wifi/1.4/types.hal new file mode 100644 index 0000000000..232e26ff80 --- /dev/null +++ b/wifi/1.4/types.hal @@ -0,0 +1,380 @@ +/* + * 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. + */ + +package android.hardware.wifi@1.4; + +import @1.0::MacAddress; +import @1.0::Rssi; +import @1.0::RttBw; +import @1.0::RttConfig; +import @1.0::RttPeerType; +import @1.0::RttPreamble; +import @1.0::RttStatus; +import @1.0::RttType; +import @1.0::TimeSpanInPs; +import @1.0::TimeStampInUs; +import @1.0::WifiChannelInfo; +import @1.0::WifiChannelWidthInMhz; +import @1.0::WifiInformationElement; +import @1.0::WifiRateNss; +import @1.0::WifiRatePreamble; + +/** + * Wifi Rate Preamble + */ +enum WifiRatePreamble : @1.0::WifiRatePreamble { + /** + * Preamble type for 11ax + */ + HE = 5, +}; + +/** + * RTT Measurement Preamble. + */ +enum RttPreamble : @1.0::RttPreamble { + /** + * Preamble type for 11ax + */ + HE = 0x8, +}; + +/** + * RTT configuration. + */ +struct RttConfig { + /** + * Peer device mac address. + */ + MacAddress addr; + + /** + * 1-sided or 2-sided RTT. + */ + RttType type; + + /** + * Optional - peer device hint (STA, P2P, AP). + */ + RttPeerType peer; + + /** + * Required for STA-AP mode, optional for P2P, NBD etc. + */ + WifiChannelInfo channel; + + /** + * Time interval between bursts (units: 100 ms). + * Applies to 1-sided and 2-sided RTT multi-burst requests. + * Range: 0-31, 0: no preference by initiator (2-sided RTT). + */ + uint32_t burstPeriod; + + /** + * Total number of RTT bursts to be executed. It will be + * specified in the same way as the parameter "Number of + * Burst Exponent" found in the FTM frame format. It + * applies to both: 1-sided RTT and 2-sided RTT. Valid + * values are 0 to 15 as defined in 802.11mc std. + * 0 means single shot + * The implication of this parameter on the maximum + * number of RTT results is the following: + * for 1-sided RTT: max num of RTT results = (2^num_burst)*(num_frames_per_burst) + * for 2-sided RTT: max num of RTT results = (2^num_burst)*(num_frames_per_burst - 1) + */ + uint32_t numBurst; + + /** + * Num of frames per burst. + * Minimum value = 1, Maximum value = 31 + * For 2-sided this equals the number of FTM frames + * to be attempted in a single burst. This also + * equals the number of FTM frames that the + * initiator will request that the responder send + * in a single frame. + */ + uint32_t numFramesPerBurst; + + /** + * Number of retries for a failed RTT frame. + * Applies to 1-sided RTT only. Minimum value = 0, Maximum value = 3 + */ + uint32_t numRetriesPerRttFrame; + + /** + * Following fields are only valid for 2-side RTT. + * + * + * Maximum number of retries that the initiator can + * retry an FTMR frame. + * Minimum value = 0, Maximum value = 3 + */ + uint32_t numRetriesPerFtmr; + + /** + * Whether to request location civic info or not. + */ + bool mustRequestLci; + + /** + * Whether to request location civic records or not. + */ + bool mustRequestLcr; + + /** + * Applies to 1-sided and 2-sided RTT. Valid values will + * be 2-11 and 15 as specified by the 802.11mc std for + * the FTM parameter burst duration. In a multi-burst + * request, if responder overrides with larger value, + * the initiator will return failure. In a single-burst + * request if responder overrides with larger value, + * the initiator will sent TMR_STOP to terminate RTT + * at the end of the burst_duration it requested. + */ + uint32_t burstDuration; + + /** + * RTT preamble to be used in the RTT frames. + */ + RttPreamble preamble; + + /** + * RTT BW to be used in the RTT frames. + */ + RttBw bw; +}; + +/** + * RTT Capabilities. + */ +struct RttCapabilities { + /** + * if 1-sided rtt data collection is supported. + */ + bool rttOneSidedSupported; + + /** + * if ftm rtt data collection is supported. + */ + bool rttFtmSupported; + + /** + * if initiator supports LCI request. Applies to 2-sided RTT. + */ + bool lciSupported; + + /** + * if initiator supports LCR request. Applies to 2-sided RTT. + */ + bool lcrSupported; + + /** + * if 11mc responder mode is supported. + */ + bool responderSupported; + + /** + * Bit mask indicates what preamble is supported by initiator. + * Combination of |RttPreamble| values. + */ + bitfield<RttPreamble> preambleSupport; + + /** + * Bit mask indicates what BW is supported by initiator. + * Combination of |RttBw| values. + */ + bitfield<RttBw> bwSupport; + + /** + * Draft 11mc spec version supported by chip. + * For instance, version 4.0 must be 40 and version 4.3 must be 43 etc. + */ + uint8_t mcVersion; +}; + +/** + * RTT Responder information + */ +struct RttResponder { + WifiChannelInfo channel; + + RttPreamble preamble; +}; + +/** + * Wifi rate info. + */ +struct WifiRateInfo { + /** + * Preamble used for RTT measurements. + */ + WifiRatePreamble preamble; + + /** + * Number of spatial streams. + */ + WifiRateNss nss; + + /** + * Bandwidth of channel. + */ + WifiChannelWidthInMhz bw; + + /** + * OFDM/CCK rate code would be as per ieee std in the units of 0.5mbps. + * HT/VHT/HE it would be mcs index. + */ + uint8_t rateMcsIdx; + + /** + * Bitrate in units of 100 Kbps. + */ + uint32_t bitRateInKbps; +}; + +/** + * RTT results. + */ +struct RttResult { + /** + * Peer device mac address. + */ + MacAddress addr; + + /** + * Burst number in a multi-burst request. + */ + uint32_t burstNum; + + /** + * Total RTT measurement frames attempted. + */ + uint32_t measurementNumber; + + /** + * Total successful RTT measurement frames. + */ + uint32_t successNumber; + + /** + * Maximum number of "FTM frames per burst" supported by + * the responder STA. Applies to 2-sided RTT only. + * If reponder overrides with larger value: + * - for single-burst request initiator will truncate the + * larger value and send a TMR_STOP after receiving as + * many frames as originally requested. + * - for multi-burst request, initiator will return + * failure right away. + */ + uint8_t numberPerBurstPeer; + + /** + * Ranging status. + */ + RttStatus status; + + /** + * When status == RTT_STATUS_FAIL_BUSY_TRY_LATER, + * this will be the time provided by the responder as to + * when the request can be tried again. Applies to 2-sided + * RTT only. In sec, 1-31sec. + */ + uint8_t retryAfterDuration; + + /** + * RTT type. + */ + RttType type; + + /** + * Average rssi in 0.5 dB steps e.g. 143 implies -71.5 dB. + */ + Rssi rssi; + + /** + * Rssi spread in 0.5 dB steps e.g. 5 implies 2.5 dB spread (optional). + */ + Rssi rssiSpread; + + /** + * 1-sided RTT: TX rate of RTT frame. + * 2-sided RTT: TX rate of initiator's Ack in response to FTM frame. + */ + WifiRateInfo txRate; + + /** + * 1-sided RTT: TX rate of Ack from other side. + * 2-sided RTT: TX rate of FTM frame coming from responder. + */ + WifiRateInfo rxRate; + + /** + * Round trip time in picoseconds + */ + TimeSpanInPs rtt; + + /** + * Rtt standard deviation in picoseconds. + */ + TimeSpanInPs rttSd; + + /** + * Difference between max and min rtt times recorded in picoseconds. + */ + TimeSpanInPs rttSpread; + + /** + * Distance in mm (optional). + */ + int32_t distanceInMm; + + /** + * Standard deviation in mm (optional). + */ + int32_t distanceSdInMm; + + /** + * Difference between max and min distance recorded in mm (optional). + */ + int32_t distanceSpreadInMm; + + /** + * Time of the measurement (in microseconds since boot). + */ + TimeStampInUs timeStampInUs; + + /** + * in ms, actual time taken by the FW to finish one burst + * measurement. Applies to 1-sided and 2-sided RTT. + */ + uint32_t burstDurationInMs; + + /** + * Number of bursts allowed by the responder. Applies + * to 2-sided RTT only. + */ + uint32_t negotiatedBurstNum; + + /** + * for 11mc only. + */ + WifiInformationElement lci; + + /** + * for 11mc only. + */ + WifiInformationElement lcr; +}; diff --git a/wifi/1.4/vts/OWNERS b/wifi/1.4/vts/OWNERS new file mode 100644 index 0000000000..8bfb14882c --- /dev/null +++ b/wifi/1.4/vts/OWNERS @@ -0,0 +1,2 @@ +rpius@google.com +etancohen@google.com diff --git a/wifi/1.4/vts/functional/Android.bp b/wifi/1.4/vts/functional/Android.bp new file mode 100644 index 0000000000..c71b319acf --- /dev/null +++ b/wifi/1.4/vts/functional/Android.bp @@ -0,0 +1,35 @@ +// +// 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. +// + +// SoftAP-specific tests, similar to VtsHalWifiApV1_0TargetTest. +cc_test { + name: "VtsHalWifiApV1_4TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "VtsHalWifiV1_4TargetTest.cpp", + "wifi_ap_iface_hidl_test.cpp", + ], + static_libs: [ + "VtsHalWifiV1_0TargetTestUtil", + "android.hardware.wifi@1.0", + "android.hardware.wifi@1.1", + "android.hardware.wifi@1.2", + "android.hardware.wifi@1.3", + "android.hardware.wifi@1.4", + "libwifi-system-iface" + ], + test_suites: ["general-tests", "vts-core"], +} diff --git a/wifi/1.4/vts/functional/VtsHalWifiV1_4TargetTest.cpp b/wifi/1.4/vts/functional/VtsHalWifiV1_4TargetTest.cpp new file mode 100644 index 0000000000..7e0f3cdc47 --- /dev/null +++ b/wifi/1.4/vts/functional/VtsHalWifiV1_4TargetTest.cpp @@ -0,0 +1,21 @@ +/* + * 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 <VtsHalHidlTargetTestEnvBase.h> + +// TODO(b/143892896): Remove this file after wifi_hidl_test_utils.cpp is +// updated. +::testing::VtsHalHidlTargetTestEnvBase* gEnv = nullptr;
\ No newline at end of file diff --git a/wifi/1.4/vts/functional/wifi_ap_iface_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_ap_iface_hidl_test.cpp new file mode 100644 index 0000000000..017ecb6b6d --- /dev/null +++ b/wifi/1.4/vts/functional/wifi_ap_iface_hidl_test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Staache 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/hardware/wifi/1.4/IWifi.h> +#include <android/hardware/wifi/1.4/IWifiApIface.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include "wifi_hidl_call_util.h" +#include "wifi_hidl_test_utils.h" + +using ::android::sp; +using ::android::hardware::hidl_array; +using ::android::hardware::wifi::V1_0::WifiStatus; +using ::android::hardware::wifi::V1_0::WifiStatusCode; +using ::android::hardware::wifi::V1_4::IWifi; +using ::android::hardware::wifi::V1_4::IWifiApIface; + +extern WifiHidlEnvironment* gEnv; + +/** + * Fixture to use for all STA Iface HIDL interface tests. + */ +class WifiApIfaceHidlTest : public ::testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + wifi_ap_iface_ = + IWifiApIface::castFrom(getWifiApIface(GetInstanceName())); + ASSERT_NE(nullptr, wifi_ap_iface_.get()); + } + + virtual void TearDown() override { stopWifi(GetInstanceName()); } + + protected: + sp<IWifiApIface> wifi_ap_iface_; + + private: + std::string GetInstanceName() { return GetParam(); } +}; + +/* + * SetMacAddress: + * Ensures that calls to set MAC address will return a success status + * code. + */ +TEST_P(WifiApIfaceHidlTest, SetMacAddress) { + const hidl_array<uint8_t, 6> kMac{{0x12, 0x22, 0x33, 0x52, 0x10, 0x41}}; + EXPECT_EQ(WifiStatusCode::SUCCESS, + HIDL_INVOKE(wifi_ap_iface_, setMacAddress, kMac).code); +} + +/* + * GetFactoryMacAddress: + * Ensures that calls to get factory MAC address will retrieve a non-zero MAC + * and return a success status code. + */ +TEST_P(WifiApIfaceHidlTest, GetFactoryMacAddress) { + std::pair<WifiStatus, hidl_array<uint8_t, 6> > status_and_mac = + HIDL_INVOKE(wifi_ap_iface_, getFactoryMacAddress); + EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_mac.first.code); + hidl_array<uint8_t, 6> all_zero{}; + EXPECT_NE(all_zero, status_and_mac.second); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, WifiApIfaceHidlTest, + testing::ValuesIn( + android::hardware::getAllHalInstanceNames(IWifi::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/wifi/1.4/vts/functional/wifi_sta_iface_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_sta_iface_hidl_test.cpp new file mode 100644 index 0000000000..ec4b2c949d --- /dev/null +++ b/wifi/1.4/vts/functional/wifi_sta_iface_hidl_test.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Staache 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/wifi/1.4/IWifiStaIface.h> + +#include <VtsHalHidlTargetTestBase.h> + +#include "wifi_hidl_call_util.h" +#include "wifi_hidl_test_utils.h" + +using ::android::sp; +using ::android::hardware::hidl_array; +using ::android::hardware::wifi::V1_0::WifiStatus; +using ::android::hardware::wifi::V1_0::WifiStatusCode; +using ::android::hardware::wifi::V1_4::IWifiStaIface; + +/** + * Fixture to use for all STA Iface HIDL interface tests. + */ +class WifiStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + wifi_sta_iface_ = IWifiStaIface::castFrom(getWifiStaIface()); + ASSERT_NE(nullptr, wifi_sta_iface_.get()); + } + + virtual void TearDown() override { stopWifi(); } + + protected: + sp<IWifiStaIface> wifi_sta_iface_; +}; + +/* + * GetCapabilities_1_4 + */ +TEST_F(WifiStaIfaceHidlTest, GetCapabilities_1_4) { + configureChipForIfaceType(IfaceType::STA, true); + + const auto& status_and_caps = + HIDL_INVOKE(wifi_sta_iface_, getCapabilities_1_4); + if (status_and_caps.first.code != WifiStatusCode::SUCCESS) { + EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED, + status_and_caps.first.code); + return; + } + EXPECT_NE(0u, status_and_caps.second); +} diff --git a/wifi/supplicant/1.3/Android.bp b/wifi/supplicant/1.3/Android.bp new file mode 100644 index 0000000000..3f2053132b --- /dev/null +++ b/wifi/supplicant/1.3/Android.bp @@ -0,0 +1,23 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.wifi.supplicant@1.3", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "ISupplicant.hal", + "ISupplicantStaIface.hal", + "ISupplicantStaIfaceCallback.hal", + "ISupplicantStaNetwork.hal", + ], + interfaces: [ + "android.hardware.wifi.supplicant@1.0", + "android.hardware.wifi.supplicant@1.1", + "android.hardware.wifi.supplicant@1.2", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/wifi/supplicant/1.3/ISupplicant.hal b/wifi/supplicant/1.3/ISupplicant.hal new file mode 100644 index 0000000000..246ce1fec3 --- /dev/null +++ b/wifi/supplicant/1.3/ISupplicant.hal @@ -0,0 +1,29 @@ +/* + * 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. + */ + +package android.hardware.wifi.supplicant@1.3; + +import @1.2::ISupplicant; + +/** + * Interface exposed by the supplicant HIDL service registered + * with the hardware service manager. + * This is the root level object for any the supplicant interactions. + * To use 1.3 features you must cast specific interfaces returned from the + * 1.2 HAL. For example V1_2::ISupplicant::addIface() adds V1_2::ISupplicantIface, + * which can be cast to V1_3::ISupplicantStaIface. + */ +interface ISupplicant extends @1.2::ISupplicant {}; diff --git a/wifi/supplicant/1.3/ISupplicantStaIface.hal b/wifi/supplicant/1.3/ISupplicantStaIface.hal new file mode 100644 index 0000000000..bfd8946251 --- /dev/null +++ b/wifi/supplicant/1.3/ISupplicantStaIface.hal @@ -0,0 +1,79 @@ +/* + * 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. + */ + +package android.hardware.wifi.supplicant@1.3; + +import @1.0::SupplicantStatus; +import @1.2::ISupplicantStaIface; +import ISupplicantStaIfaceCallback; + +/** + * Interface exposed by the supplicant for each station mode network + * interface (e.g wlan0) it controls. + */ +interface ISupplicantStaIface extends @1.2::ISupplicantStaIface { + /** + * Register for callbacks from this interface. + * + * These callbacks are invoked for events that are specific to this interface. + * Registration of multiple callback objects is supported. These objects must + * be automatically deleted when the corresponding client process is dead or + * if this interface is removed. + * + * @param callback An instance of the |ISupplicantStaIfaceCallback| HIDL + * interface object. + * @return status Status of the operation. + * Possible status codes: + * |SupplicantStatusCode.SUCCESS|, + * |SupplicantStatusCode.FAILURE_UNKNOWN|, + * |SupplicantStatusCode.FAILURE_IFACE_INVALID| + */ + registerCallback_1_3(ISupplicantStaIfaceCallback callback) + generates (SupplicantStatus status); + + /** + * Get Connection capabilities + * + * @return status Status of the operation, and connection capabilities. + * Possible status codes: + * |SupplicantStatusCode.SUCCESS|, + * |SupplicantStatusCode.FAILURE_UNKNOWN|, + */ + getConnectionCapabilities() + generates (SupplicantStatus status, ConnectionCapabilities capabilities); + + /** + * Get wpa driver capabilities. + * + * @return status Status of the operation, and a bitmap of wpa driver features. + * Possible status codes: + * |SupplicantStatusCode.SUCCESS|, + * |SupplicantStatusCode.FAILURE_UNKNOWN|, + */ + getWpaDriverCapabilities() generates (SupplicantStatus status, + bitfield<WpaDriverCapabilitiesMask> driverCapabilitiesMask); + + /** + * Set MBO cellular data status. + * + * @param available true means cellular data available, false otherwise. + * @return status Status of the operation. + * Possible status codes: + * |SupplicantStatusCode.SUCCESS|, + * |SupplicantStatusCode.FAILURE_UNKNOWN| + */ + setMboCellularDataStatus(bool available) generates (SupplicantStatus status); +}; diff --git a/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal b/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal new file mode 100644 index 0000000000..107e0fc0f6 --- /dev/null +++ b/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package android.hardware.wifi.supplicant@1.3; + +import @1.2::ISupplicantStaIfaceCallback; + +/** + * Callback Interface exposed by the supplicant service + * for each station mode interface (ISupplicantStaIface). + * + * Clients need to host an instance of this HIDL interface object and + * pass a reference of the object to the supplicant via the + * corresponding |ISupplicantStaIface.registerCallback_1_3| method. + */ +interface ISupplicantStaIfaceCallback extends @1.2::ISupplicantStaIfaceCallback { + /** + * Indicates PMK cache added event. + * + * @param expirationTimeInSec expiration time in seconds + * @param serializedEntry is serialized PMK cache entry, the content is + * opaque for the framework and depends on the native implementation. + */ + oneway onPmkCacheAdded(int64_t expirationTimeInSec, vec<uint8_t> serializedEntry); +}; diff --git a/wifi/supplicant/1.3/ISupplicantStaNetwork.hal b/wifi/supplicant/1.3/ISupplicantStaNetwork.hal new file mode 100644 index 0000000000..ab08cff9c5 --- /dev/null +++ b/wifi/supplicant/1.3/ISupplicantStaNetwork.hal @@ -0,0 +1,64 @@ +/* + * 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. + */ + +package android.hardware.wifi.supplicant@1.3; + +import @1.0::SupplicantStatus; +import @1.2::ISupplicantStaNetwork; + +/** + * Interface exposed by the supplicant for each station mode network + * configuration it controls. + */ +interface ISupplicantStaNetwork extends @1.2::ISupplicantStaNetwork { + /** + * Set OCSP (Online Certificate Status Protocol) type for this network. + * + * @param ocspType value to set. + * @return status Status of the operation. + * Possible status codes: + * |SupplicantStatusCode.SUCCESS|, + * |SupplicantStatusCode.FAILURE_ARGS_INVALID|, + * |SupplicantStatusCode.FAILURE_NETWORK_INVALID| + */ + setOcsp(OcspType ocspType) generates (SupplicantStatus status); + + /** + * Get OCSP (Online Certificate Status Protocol) type for this network. + * + * @return status Status of the operation. + * Possible status codes: + * |SupplicantStatusCode.SUCCESS|, + * |SupplicantStatusCode.FAILURE_ARGS_INVALID|, + * |SupplicantStatusCode.FAILURE_NETWORK_INVALID| + * @return ocspType ocsp type. + */ + getOcsp() generates (SupplicantStatus status, OcspType ocspType); + + /** + * Add a PMK into supplicant PMK cache. + * + * @param serializedEntry is serialized PMK cache entry, the content is + * opaque for the framework and depends on the native implementation. + * @return status Status of the operation + * Possible status codes: + * |SupplicantStatusCode.SUCCESS|, + * |SupplicantStatusCode.FAILURE_ARGS_INVALID|, + * |SupplicantStatusCode.FAILURE_UNKNOWN|, + * |SupplicantStatusCode.FAILURE_NETWORK_INVALID| + */ + setPmkCache(vec<uint8_t> serializedEntry) generates (SupplicantStatus status); +}; diff --git a/wifi/supplicant/1.3/types.hal b/wifi/supplicant/1.3/types.hal new file mode 100644 index 0000000000..4e01ab1df8 --- /dev/null +++ b/wifi/supplicant/1.3/types.hal @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package android.hardware.wifi.supplicant@1.3; + +/** + * OcspType: The type of OCSP request. + */ +enum OcspType : uint32_t { + NONE, + REQUEST_CERT_STATUS, + REQUIRE_CERT_STATUS, + REQUIRE_ALL_CERTS_STATUS, +}; + +/** + * Wifi Technologies + */ +enum WifiTechnology : uint32_t { + UNKNOWN = 0, + /** + * For 802.11a/b/g + */ + LEGACY = 1, + /** + * For 802.11n + */ + HT = 2, + /** + * For 802.11ac + */ + VHT = 3, + /** + * For 802.11ax + */ + HE = 4, +}; + +/** + * Connection Capabilities. + */ +struct ConnectionCapabilities { + /** + * Wifi Technology + */ + WifiTechnology technology; +}; + +/** + * WPA Driver capability. + */ +enum WpaDriverCapabilitiesMask : uint32_t { + /** + * Multi Band Operation. + */ + MBO = 1 << 0, + /** + * Optimized Connectivity Experience. + */ + OCE = 1 << 1, +}; diff --git a/wifi/supplicant/1.3/vts/OWNERS b/wifi/supplicant/1.3/vts/OWNERS new file mode 100644 index 0000000000..8bfb14882c --- /dev/null +++ b/wifi/supplicant/1.3/vts/OWNERS @@ -0,0 +1,2 @@ +rpius@google.com +etancohen@google.com diff --git a/wifi/supplicant/1.3/vts/functional/Android.bp b/wifi/supplicant/1.3/vts/functional/Android.bp new file mode 100644 index 0000000000..abb86008b5 --- /dev/null +++ b/wifi/supplicant/1.3/vts/functional/Android.bp @@ -0,0 +1,65 @@ +// +// 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: "VtsHalWifiSupplicantV1_3TargetTestUtil", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["supplicant_hidl_test_utils_1_3.cpp"], + export_include_dirs: [ + "." + ], + static_libs: [ + "VtsHalWifiV1_0TargetTestUtil", + "VtsHalWifiSupplicantV1_0TargetTestUtil", + "VtsHalWifiSupplicantV1_1TargetTestUtil", + "VtsHalWifiSupplicantV1_2TargetTestUtil", + "android.hardware.wifi.supplicant@1.0", + "android.hardware.wifi.supplicant@1.1", + "android.hardware.wifi.supplicant@1.2", + "android.hardware.wifi.supplicant@1.3", + "android.hardware.wifi@1.0", + "libgmock", + "libwifi-system", + "libwifi-system-iface", + ], +} + +cc_test { + name: "VtsHalWifiSupplicantV1_3TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "VtsHalWifiSupplicantV1_3TargetTest.cpp", + "supplicant_sta_iface_hidl_test.cpp", + "supplicant_sta_network_hidl_test.cpp", + ], + static_libs: [ + "VtsHalWifiV1_0TargetTestUtil", + "VtsHalWifiSupplicantV1_0TargetTestUtil", + "VtsHalWifiSupplicantV1_1TargetTestUtil", + "VtsHalWifiSupplicantV1_2TargetTestUtil", + "VtsHalWifiSupplicantV1_3TargetTestUtil", + "android.hardware.wifi.supplicant@1.0", + "android.hardware.wifi.supplicant@1.1", + "android.hardware.wifi.supplicant@1.2", + "android.hardware.wifi.supplicant@1.3", + "android.hardware.wifi@1.0", + "android.hardware.wifi@1.1", + "libgmock", + "libwifi-system", + "libwifi-system-iface", + ], + test_suites: ["general-tests"], +} diff --git a/wifi/supplicant/1.3/vts/functional/VtsHalWifiSupplicantV1_3TargetTest.cpp b/wifi/supplicant/1.3/vts/functional/VtsHalWifiSupplicantV1_3TargetTest.cpp new file mode 100644 index 0000000000..4dbb64eeda --- /dev/null +++ b/wifi/supplicant/1.3/vts/functional/VtsHalWifiSupplicantV1_3TargetTest.cpp @@ -0,0 +1,62 @@ +/* + * 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/wifi/1.1/IWifi.h> +#include <android/hardware/wifi/supplicant/1.3/ISupplicant.h> + +#include "supplicant_hidl_test_utils.h" +#include "wifi_hidl_test_utils.h" + +class WifiSupplicantHidlEnvironment_1_3 : public WifiSupplicantHidlEnvironment { + public: + // get the test environment singleton + static WifiSupplicantHidlEnvironment_1_3* Instance() { + static WifiSupplicantHidlEnvironment_1_3* instance = + new WifiSupplicantHidlEnvironment_1_3; + return instance; + } + virtual void registerTestServices() override { + registerTestService<::android::hardware::wifi::V1_0::IWifi>(); + registerTestService<::android::hardware::wifi::V1_1::IWifi>(); + registerTestService< + ::android::hardware::wifi::supplicant::V1_0::ISupplicant>(); + registerTestService< + ::android::hardware::wifi::supplicant::V1_1::ISupplicant>(); + registerTestService< + ::android::hardware::wifi::supplicant::V1_2::ISupplicant>(); + registerTestService< + ::android::hardware::wifi::supplicant::V1_3::ISupplicant>(); + } + + private: + WifiSupplicantHidlEnvironment_1_3() {} +}; + +WifiSupplicantHidlEnvironment* gEnv = + WifiSupplicantHidlEnvironment_1_3::Instance(); + +int main(int argc, char** argv) { + ::testing::AddGlobalTestEnvironment(gEnv); + ::testing::InitGoogleTest(&argc, argv); + gEnv->init(&argc, argv); + int status = gEnv->initFromOptions(argc, argv); + if (status == 0) { + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + } + return status; +} diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_hidl_test_utils_1_3.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_hidl_test_utils_1_3.cpp new file mode 100644 index 0000000000..308808deff --- /dev/null +++ b/wifi/supplicant/1.3/vts/functional/supplicant_hidl_test_utils_1_3.cpp @@ -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. + */ + +#include <VtsHalHidlTargetTestBase.h> +#include <android-base/logging.h> + +#include "supplicant_hidl_test_utils.h" +#include "supplicant_hidl_test_utils_1_3.h" + +using ::android::sp; +using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaIface; +using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork; + +sp<ISupplicantStaIface> getSupplicantStaIface_1_3() { + return ISupplicantStaIface::castFrom(getSupplicantStaIface()); +} + +sp<ISupplicantStaNetwork> createSupplicantStaNetwork_1_3() { + return ISupplicantStaNetwork::castFrom(createSupplicantStaNetwork()); +} diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_hidl_test_utils_1_3.h b/wifi/supplicant/1.3/vts/functional/supplicant_hidl_test_utils_1_3.h new file mode 100644 index 0000000000..39dbb8fc96 --- /dev/null +++ b/wifi/supplicant/1.3/vts/functional/supplicant_hidl_test_utils_1_3.h @@ -0,0 +1,28 @@ +/* + * 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 SUPPLICANT_HIDL_TEST_UTILS_1_3_H +#define SUPPLICANT_HIDL_TEST_UTILS_1_3_H + +#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaIface.h> +#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h> + +android::sp<android::hardware::wifi::supplicant::V1_3::ISupplicantStaIface> +getSupplicantStaIface_1_3(); +android::sp<android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork> +createSupplicantStaNetwork_1_3(); + +#endif /* SUPPLICANT_HIDL_TEST_UTILS_1_3_H */ diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp new file mode 100644 index 0000000000..2cf58813ad --- /dev/null +++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp @@ -0,0 +1,229 @@ +/* + * 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/hardware/wifi/supplicant/1.2/types.h> +#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaIface.h> +#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.h> +#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h> +#include <android/hardware/wifi/supplicant/1.3/types.h> +#include <hidl/HidlSupport.h> +#include <hidl/Status.h> + +#include "supplicant_hidl_test_utils.h" +#include "supplicant_hidl_test_utils_1_3.h" + +using ::android::sp; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatus; +using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode; +using ::android::hardware::wifi::supplicant::V1_2::DppAkm; +using ::android::hardware::wifi::supplicant::V1_2::DppFailureCode; +using ::android::hardware::wifi::supplicant::V1_2::DppProgressCode; +using ::android::hardware::wifi::supplicant::V1_3::ConnectionCapabilities; +using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaIface; +using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaIfaceCallback; +using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork; +using ::android::hardware::wifi::supplicant::V1_3::WpaDriverCapabilitiesMask; + +class SupplicantStaIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + startSupplicantAndWaitForHidlService(); + EXPECT_TRUE(turnOnExcessiveLogging()); + sta_iface_ = getSupplicantStaIface_1_3(); + ASSERT_NE(sta_iface_.get(), nullptr); + } + + virtual void TearDown() override { stopSupplicant(); } + + int64_t pmkCacheExpirationTimeInSec; + std::vector<uint8_t> serializedPmkCacheEntry; + + protected: + // ISupplicantStaIface object used for all tests in this fixture. + sp<ISupplicantStaIface> sta_iface_; +}; + +class IfaceCallback : public ISupplicantStaIfaceCallback { + Return<void> onNetworkAdded(uint32_t /* id */) override { return Void(); } + Return<void> onNetworkRemoved(uint32_t /* id */) override { return Void(); } + Return<void> onStateChanged( + ISupplicantStaIfaceCallback::State /* newState */, + const hidl_array<uint8_t, 6>& /*bssid */, uint32_t /* id */, + const hidl_vec<uint8_t>& /* ssid */) override { + return Void(); + } + Return<void> onAnqpQueryDone( + const hidl_array<uint8_t, 6>& /* bssid */, + const ISupplicantStaIfaceCallback::AnqpData& /* data */, + const ISupplicantStaIfaceCallback::Hs20AnqpData& /* hs20Data */) + override { + return Void(); + } + virtual Return<void> onHs20IconQueryDone( + const hidl_array<uint8_t, 6>& /* bssid */, + const hidl_string& /* fileName */, + const hidl_vec<uint8_t>& /* data */) override { + return Void(); + } + virtual Return<void> onHs20SubscriptionRemediation( + const hidl_array<uint8_t, 6>& /* bssid */, + ISupplicantStaIfaceCallback::OsuMethod /* osuMethod */, + const hidl_string& /* url*/) override { + return Void(); + } + Return<void> onHs20DeauthImminentNotice( + const hidl_array<uint8_t, 6>& /* bssid */, uint32_t /* reasonCode */, + uint32_t /* reAuthDelayInSec */, + const hidl_string& /* url */) override { + return Void(); + } + Return<void> onDisconnected(const hidl_array<uint8_t, 6>& /* bssid */, + bool /* locallyGenerated */, + ISupplicantStaIfaceCallback::ReasonCode + /* reasonCode */) override { + return Void(); + } + Return<void> onAssociationRejected( + const hidl_array<uint8_t, 6>& /* bssid */, + ISupplicantStaIfaceCallback::StatusCode /* statusCode */, + bool /*timedOut */) override { + return Void(); + } + Return<void> onAuthenticationTimeout( + const hidl_array<uint8_t, 6>& /* bssid */) override { + return Void(); + } + Return<void> onBssidChanged( + ISupplicantStaIfaceCallback::BssidChangeReason /* reason */, + const hidl_array<uint8_t, 6>& /* bssid */) override { + return Void(); + } + Return<void> onEapFailure() override { return Void(); } + Return<void> onEapFailure_1_1( + ISupplicantStaIfaceCallback::EapErrorCode /* eapErrorCode */) override { + return Void(); + } + Return<void> onWpsEventSuccess() override { return Void(); } + Return<void> onWpsEventFail( + const hidl_array<uint8_t, 6>& /* bssid */, + ISupplicantStaIfaceCallback::WpsConfigError /* configError */, + ISupplicantStaIfaceCallback::WpsErrorIndication /* errorInd */) + override { + return Void(); + } + Return<void> onWpsEventPbcOverlap() override { return Void(); } + Return<void> onExtRadioWorkStart(uint32_t /* id */) override { + return Void(); + } + Return<void> onExtRadioWorkTimeout(uint32_t /* id*/) override { + return Void(); + } + Return<void> onDppSuccessConfigReceived( + const hidl_vec<uint8_t>& /* ssid */, const hidl_string& /* password */, + const hidl_array<uint8_t, 32>& /* psk */, + DppAkm /* securityAkm */) override { + return Void(); + } + Return<void> onDppSuccessConfigSent() override { return Void(); } + Return<void> onDppProgress(DppProgressCode /* code */) override { + return Void(); + } + Return<void> onDppFailure(DppFailureCode /* code */) override { + return Void(); + } + Return<void> onPmkCacheAdded( + int64_t /* expirationTimeInSec */, + const hidl_vec<uint8_t>& /* serializedEntry */) override { + return Void(); + } +}; + +class IfacePmkCacheCallback : public IfaceCallback { + SupplicantStaIfaceHidlTest& parent_; + Return<void> onPmkCacheAdded( + int64_t expirationTimeInSec, + const hidl_vec<uint8_t>& serializedEntry) override { + parent_.pmkCacheExpirationTimeInSec = expirationTimeInSec; + parent_.serializedPmkCacheEntry = serializedEntry; + return Void(); + } + + public: + IfacePmkCacheCallback(SupplicantStaIfaceHidlTest& parent) + : parent_(parent) {} +}; + +/* + * RegisterCallback_1_3 + */ +TEST_F(SupplicantStaIfaceHidlTest, RegisterCallback_1_3) { + sta_iface_->registerCallback_1_3( + new IfaceCallback(), [](const SupplicantStatus& status) { + EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code); + }); +} + +/* + * getConnectionCapabilities + */ +TEST_F(SupplicantStaIfaceHidlTest, GetConnectionCapabilities) { + sta_iface_->getConnectionCapabilities( + [&](const SupplicantStatus& status, + ConnectionCapabilities /* capabilities */) { + EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code); + }); +} + +/* + * GetWpaDriverCapabilities + */ +TEST_F(SupplicantStaIfaceHidlTest, GetWpaDriverCapabilities) { + sta_iface_->getWpaDriverCapabilities( + [&](const SupplicantStatus& status, uint32_t) { + EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code); + }); +} + +/* + * SetMboCellularDataStatus + */ +TEST_F(SupplicantStaIfaceHidlTest, SetMboCellularDataStatus) { + uint32_t driverCapMask = 0; + + // Get MBO support from the device. + sta_iface_->getWpaDriverCapabilities( + [&](const SupplicantStatus& status, uint32_t driverCapMaskInternal) { + EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code); + + driverCapMask = driverCapMaskInternal; + }); + + SupplicantStatusCode expectedStatusCode = + (driverCapMask & WpaDriverCapabilitiesMask::MBO) + ? SupplicantStatusCode::SUCCESS + : SupplicantStatusCode::FAILURE_UNKNOWN; + + sta_iface_->setMboCellularDataStatus( + true, [expectedStatusCode](const SupplicantStatus& status) { + EXPECT_EQ(expectedStatusCode, status.code); + }); +} diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp new file mode 100644 index 0000000000..07bc9d8103 --- /dev/null +++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp @@ -0,0 +1,86 @@ +/* + * 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 <VtsHalHidlTargetTestBase.h> +#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h> + +#include "supplicant_hidl_test_utils.h" +#include "supplicant_hidl_test_utils_1_3.h" + +using ::android::sp; +using ::android::hardware::hidl_vec; +using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatus; +using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode; +using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork; +using ::android::hardware::wifi::supplicant::V1_3::OcspType; +namespace { +constexpr OcspType kTestOcspType = OcspType::REQUEST_CERT_STATUS; +constexpr OcspType kTestInvalidOcspType = (OcspType)-1; +} // namespace + +class SupplicantStaNetworkHidlTest + : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + startSupplicantAndWaitForHidlService(); + EXPECT_TRUE(turnOnExcessiveLogging()); + sta_network_ = createSupplicantStaNetwork_1_3(); + ASSERT_NE(sta_network_.get(), nullptr); + } + + virtual void TearDown() override { stopSupplicant(); } + + protected: + // ISupplicantStaNetwork object used for all tests in this fixture. + sp<ISupplicantStaNetwork> sta_network_; +}; + +/* + * SetGetOcsp + */ +TEST_F(SupplicantStaNetworkHidlTest, SetGetOcsp) { + OcspType testOcspType = kTestOcspType; + + sta_network_->setOcsp(testOcspType, [](const SupplicantStatus &status) { + EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code); + }); + + sta_network_->setOcsp( + kTestInvalidOcspType, [](const SupplicantStatus &status) { + EXPECT_EQ(SupplicantStatusCode::FAILURE_ARGS_INVALID, status.code); + }); + + sta_network_->getOcsp( + [testOcspType](const SupplicantStatus &status, OcspType ocspType) { + EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code); + EXPECT_EQ(testOcspType, ocspType); + }); +} + +/* + * SetPmkCacheEntry + */ +TEST_F(SupplicantStaNetworkHidlTest, SetPmkCache) { + uint8_t bytes[128] = {0}; + std::vector<uint8_t> serializedEntry(bytes, bytes + sizeof(bytes)); + + sta_network_->setPmkCache( + serializedEntry, [](const SupplicantStatus &status) { + EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code); + }); +} |